cdd93d98f5d65cbba57628806e288f3844a11e88
14 from juju
.errors
import JujuAPIError
16 log
= logging
.getLogger("websocket")
23 # Connect to an arbitrary api server
24 client = await Connection.connect(
25 api_endpoint, model_uuid, username, password, cacert)
27 # Connect using a controller/model name
28 client = await Connection.connect_model('local.local:default')
30 # Connect to the currently active model
31 client = await Connection.connect_current()
34 def __init__(self
, endpoint
, uuid
, username
, password
, cacert
=None):
35 self
.endpoint
= endpoint
37 self
.username
= username
38 self
.password
= password
41 self
.__request
_id
__ = 0
52 def _get_ssl(self
, cert
):
53 return ssl
.create_default_context(
54 purpose
=ssl
.Purpose
.CLIENT_AUTH
, cadata
=cert
)
56 async def open(self
, addr
, cert
=None):
59 kw
['ssl'] = self
._get
_ssl
(cert
)
61 self
.ws
= await websockets
.connect(addr
, **kw
)
64 async def close(self
):
68 result
= await self
.ws
.recv()
69 if result
is not None:
70 result
= json
.loads(result
)
73 async def rpc(self
, msg
, encoder
=None):
74 self
.__request
_id
__ += 1
75 msg
['request-id'] = self
.__request
_id
__
76 if'params' not in msg
:
78 if "version" not in msg
:
79 msg
['version'] = self
.facades
[msg
['type']]
80 outgoing
= json
.dumps(msg
, indent
=2, cls
=encoder
)
81 await self
.ws
.send(outgoing
)
82 result
= await self
.recv()
83 #log.debug("Send: %s", outgoing)
84 #log.debug("Recv: %s", result)
85 if result
and 'error' in result
:
86 raise JujuAPIError(result
)
89 async def clone(self
):
90 """Return a new Connection, connected to the same websocket endpoint
94 return await Connection
.connect(
102 async def controller(self
):
103 """Return a Connection to the controller at self.endpoint
106 return await Connection
.connect(
115 async def connect(cls
, endpoint
, uuid
, username
, password
, cacert
=None):
116 """Connect to the websocket.
118 If uuid is None, the connection will be to the controller. Otherwise it
119 will be to the model.
123 url
= "wss://{}/model/{}/api".format(endpoint
, uuid
)
125 url
= "wss://{}/api".format(endpoint
)
126 client
= cls(endpoint
, uuid
, username
, password
, cacert
)
127 await client
.open(url
, cacert
)
128 server_info
= await client
.login(username
, password
)
129 client
.build_facades(server_info
['facades'])
130 log
.info("Driver connected to juju %s", url
)
135 async def connect_current(cls
):
136 """Connect to the currently active model.
139 jujudata
= JujuData()
140 controller_name
= jujudata
.current_controller()
141 controller
= jujudata
.controllers()[controller_name
]
142 endpoint
= controller
['api-endpoints'][0]
143 cacert
= controller
.get('ca-cert')
144 accounts
= jujudata
.accounts()[controller_name
]
145 username
= accounts
['user']
146 password
= accounts
['password']
147 models
= jujudata
.models()[controller_name
]
148 model_name
= models
['current-model']
149 model_uuid
= models
['models'][model_name
]['uuid']
151 return await cls
.connect(
152 endpoint
, model_uuid
, username
, password
, cacert
)
155 async def connect_model(cls
, model
):
156 """Connect to a model by name.
158 :param str model: <controller>:<model>
161 controller_name
, model_name
= model
.split(':')
163 jujudata
= JujuData()
164 controller
= jujudata
.controllers()[controller_name
]
165 endpoint
= controller
['api-endpoints'][0]
166 cacert
= controller
.get('ca-cert')
167 accounts
= jujudata
.accounts()[controller_name
]
168 username
= accounts
['user']
169 password
= accounts
['password']
170 models
= jujudata
.models()[controller_name
]
171 model_uuid
= models
['models'][model_name
]['uuid']
173 return await cls
.connect(
174 endpoint
, model_uuid
, username
, password
, cacert
)
176 def build_facades(self
, info
):
179 self
.facades
[facade
['name']] = facade
['versions'][-1]
181 async def login(self
, username
, password
):
182 if not username
.startswith('user-'):
183 username
= 'user-{}'.format(username
)
185 result
= await self
.rpc({
190 "auth-tag": username
,
191 "credentials": password
,
192 "nonce": "".join(random
.sample(string
.printable
, 12)),
194 return result
['response']
199 self
.path
= os
.environ
.get('JUJU_DATA') or '~/.local/share/juju'
200 self
.path
= os
.path
.abspath(os
.path
.expanduser(self
.path
))
202 def current_controller(self
):
203 cmd
= shlex
.split('juju show-controller --format yaml')
204 output
= subprocess
.check_output(cmd
)
205 output
= yaml
.safe_load(output
)
206 return list(output
.keys())[0]
208 def controllers(self
):
209 return self
._load
_yaml
('controllers.yaml', 'controllers')
212 return self
._load
_yaml
('models.yaml', 'controllers')
215 return self
._load
_yaml
('accounts.yaml', 'controllers')
217 def _load_yaml(self
, filename
, key
):
218 filepath
= os
.path
.join(self
.path
, filename
)
219 with io
.open(filepath
, 'rt') as f
:
220 return yaml
.safe_load(f
)[key
]