56f9e18fbb1e55c82671c7ac6e1a9f059d6223f7
13 log
= logging
.getLogger("websocket")
20 # Connect to an arbitrary api server
21 client = await Connection.connect(
22 api_endpoint, model_uuid, username, password, cacert)
24 # Connect using a controller/model name
25 client = await Connection.connect_model('local.local:default')
27 # Connect to the currently active model
28 client = await Connection.connect_current()
31 def __init__(self
, endpoint
, uuid
, username
, password
, cacert
=None):
32 self
.endpoint
= endpoint
34 self
.username
= username
35 self
.password
= password
38 self
.__request
_id
__ = 0
43 def _get_ssl(self
, cert
):
44 return ssl
.create_default_context(
45 purpose
=ssl
.Purpose
.CLIENT_AUTH
, cadata
=cert
)
47 async def open(self
, addr
, cert
=None):
50 kw
['ssl'] = self
._get
_ssl
(cert
)
52 self
.ws
= await websockets
.connect(addr
, **kw
)
55 async def close(self
):
59 result
= await self
.ws
.recv()
60 if result
is not None:
61 result
= json
.loads(result
)
64 async def rpc(self
, msg
, encoder
=None):
65 self
.__request
_id
__ += 1
66 msg
['RequestId'] = self
.__request
_id
__
67 if'Params' not in msg
:
69 if "Version" not in msg
:
70 msg
['Version'] = self
.facades
[msg
['Type']]
71 outgoing
= json
.dumps(msg
, indent
=2, cls
=encoder
)
72 await self
.ws
.send(outgoing
)
73 result
= await self
.recv()
74 log
.debug("send %s got %s", msg
, result
)
75 if result
and 'Error' in result
:
76 raise RuntimeError(result
)
79 async def clone(self
):
80 """Return a new Connection, connected to the same websocket endpoint
84 return await Connection
.connect(
93 async def connect(cls
, endpoint
, uuid
, username
, password
, cacert
=None):
94 url
= "wss://{}/model/{}/api".format(endpoint
, uuid
)
95 client
= cls(endpoint
, uuid
, username
, password
, cacert
)
96 await client
.open(url
, cacert
)
97 server_info
= await client
.login(username
, password
)
98 client
.build_facades(server_info
['facades'])
99 log
.info("Driver connected to juju %s", endpoint
)
104 async def connect_current(cls
):
105 """Connect to the currently active model.
108 jujudata
= JujuData()
109 controller_name
= jujudata
.current_controller()
110 controller
= jujudata
.controllers()[controller_name
]
111 endpoint
= controller
['api-endpoints'][0]
112 cacert
= controller
.get('ca-cert')
113 accounts
= jujudata
.accounts()[controller_name
]
114 username
= accounts
['current-account']
115 password
= accounts
['accounts'][username
]['password']
116 models
= jujudata
.models()[controller_name
]['accounts'][username
]
117 model_name
= models
['current-model']
118 model_uuid
= models
['models'][model_name
]['uuid']
120 return await cls
.connect(
121 endpoint
, model_uuid
, username
, password
, cacert
)
124 async def connect_model(cls
, model
):
125 """Connect to a model by name.
127 :param str model: <controller>:<model>
130 controller_name
, model_name
= model
.split(':')
132 jujudata
= JujuData()
133 controller
= jujudata
.controllers()[controller_name
]
134 endpoint
= controller
['api-endpoints'][0]
135 cacert
= controller
.get('ca-cert')
136 accounts
= jujudata
.accounts()[controller_name
]
137 username
= accounts
['current-account']
138 password
= accounts
['accounts'][username
]['password']
139 models
= jujudata
.models()[controller_name
]['accounts'][username
]
140 model_uuid
= models
['models'][model_name
]['uuid']
142 return await cls
.connect(
143 endpoint
, model_uuid
, username
, password
, cacert
)
145 def build_facades(self
, info
):
148 self
.facades
[facade
['Name']] = facade
['Versions'][-1]
150 async def login(self
, username
, password
):
151 if not username
.startswith('user-'):
152 username
= 'user-{}'.format(username
)
154 result
= await self
.rpc({
159 "auth-tag": username
,
160 "credentials": password
,
161 "Nonce": "".join(random
.sample(string
.printable
, 12)),
163 return result
['Response']
168 self
.path
= os
.environ
.get('JUJU_DATA') or '~/.local/share/juju'
169 self
.path
= os
.path
.abspath(os
.path
.expanduser(self
.path
))
171 def current_controller(self
):
173 filepath
= os
.path
.join(self
.path
, 'current-controller')
174 with io
.open(filepath
, 'rt') as f
:
175 return f
.read().strip()
180 def controllers(self
):
181 return self
._load
_yaml
('controllers.yaml', 'controllers')
184 return self
._load
_yaml
('models.yaml', 'controllers')
187 return self
._load
_yaml
('accounts.yaml', 'controllers')
189 def _load_yaml(self
, filename
, key
):
190 filepath
= os
.path
.join(self
.path
, filename
)
191 with io
.open(filepath
, 'rt') as f
:
192 return yaml
.safe_load(f
)[key
]