Better fix for moving stuff into an Executor.
[osm/N2VC.git] / juju / controller.py
1 import asyncio
2 import logging
3
4 from . import tag
5 from . import utils
6 from .client import client
7 from .client import connection
8 from .client import watcher
9 from .model import Model
10
11 log = logging.getLogger(__name__)
12
13
14 class Controller(object):
15 def __init__(self, loop=None):
16 """Instantiate a new Controller.
17
18 One of the connect_* methods will need to be called before this
19 object can be used for anything interesting.
20
21 :param loop: an asyncio event loop
22
23 """
24 self.loop = loop or asyncio.get_event_loop()
25 self.connection = None
26
27 async def connect(
28 self, endpoint, username, password, cacert=None, macaroons=None):
29 """Connect to an arbitrary Juju controller.
30
31 """
32 self.connection = await connection.Connection.connect(
33 endpoint, None, username, password, cacert, macaroons)
34
35 async def connect_current(self):
36 """Connect to the current Juju controller.
37
38 """
39 self.connection = (
40 await connection.Connection.connect_current_controller())
41
42 async def connect_controller(self, controller_name):
43 """Connect to a Juju controller by name.
44
45 """
46 self.connection = (
47 await connection.Connection.connect_controller(controller_name))
48
49 async def disconnect(self):
50 """Shut down the watcher task and close websockets.
51
52 """
53 if self.connection and self.connection.is_open:
54 log.debug('Closing controller connection')
55 await self.connection.close()
56 self.connection = None
57
58 async def add_model(
59 self, model_name, cloud_name=None, credential_name=None,
60 owner=None, config=None, region=None):
61 """Add a model to this controller.
62
63 :param str model_name: Name to give the new model.
64 :param str cloud_name: Name of the cloud in which to create the
65 model, e.g. 'aws'. Defaults to same cloud as controller.
66 :param str credential_name: Name of the credential to use when
67 creating the model. Defaults to current credential. If you
68 pass a credential_name, you must also pass a cloud_name,
69 even if it's the default cloud.
70 :param str owner: Username that will own the model. Defaults to
71 the current user.
72 :param dict config: Model configuration.
73 :param str region: Region in which to create the model.
74
75 """
76 model_facade = client.ModelManagerFacade()
77 model_facade.connect(self.connection)
78
79 owner = owner or self.connection.info['user-info']['identity']
80 cloud_name = cloud_name or await self.get_cloud()
81
82 if credential_name:
83 credential = tag.credential(
84 cloud_name,
85 tag.untag('user-', owner),
86 credential_name
87 )
88 else:
89 credential = None
90
91 log.debug('Creating model %s', model_name)
92
93 model_info = await model_facade.CreateModel(
94 tag.cloud(cloud_name),
95 config,
96 credential,
97 model_name,
98 owner,
99 region,
100 )
101
102 # Add our ssh key to the model, to work around
103 # https://bugs.launchpad.net/juju/+bug/1643076
104 try:
105 ssh_key = await utils.read_ssh_key(loop=self.loop)
106 await utils.execute_process(
107 'juju', 'add-ssh-key', '-m', model_name, ssh_key, log=log)
108 except Exception as e:
109 log.exception(
110 "Could not add ssh key to model. You will not be able "
111 "to ssh into machines in this model. "
112 "Manually running `juju add-ssh-key <key>` in the cli "
113 "may fix this problem.")
114
115 model = Model()
116 await model.connect(
117 self.connection.endpoint,
118 model_info.uuid,
119 self.connection.username,
120 self.connection.password,
121 self.connection.cacert,
122 self.connection.macaroons,
123 )
124
125 return model
126
127 async def destroy_models(self, *uuids):
128 """Destroy one or more models.
129
130 :param str \*uuids: UUIDs of models to destroy
131
132 """
133 model_facade = client.ModelManagerFacade()
134 model_facade.connect(self.connection)
135
136 log.debug(
137 'Destroying model%s %s',
138 '' if len(uuids) == 1 else 's',
139 ', '.join(uuids)
140 )
141
142 await model_facade.DestroyModels([
143 client.Entity(tag.model(uuid))
144 for uuid in uuids
145 ])
146 destroy_model = destroy_models
147
148 def add_user(self, username, display_name=None, acl=None, models=None):
149 """Add a user to this controller.
150
151 :param str username: Username
152 :param str display_name: Display name
153 :param str acl: Access control, e.g. 'read'
154 :param list models: Models to which the user is granted access
155
156 """
157 pass
158
159 def change_user_password(self, username, password):
160 """Change the password for a user in this controller.
161
162 :param str username: Username
163 :param str password: New password
164
165 """
166 pass
167
168 def destroy(self, destroy_all_models=False):
169 """Destroy this controller.
170
171 :param bool destroy_all_models: Destroy all hosted models in the
172 controller.
173
174 """
175 pass
176
177 def disable_user(self, username):
178 """Disable a user.
179
180 :param str username: Username
181
182 """
183 pass
184
185 def enable_user(self):
186 """Re-enable a previously disabled user.
187
188 """
189 pass
190
191 def kill(self):
192 """Forcibly terminate all machines and other associated resources for
193 this controller.
194
195 """
196 pass
197
198 async def get_cloud(self):
199 """
200 Get the name of the cloud that this controller lives on.
201 """
202 cloud_facade = client.CloudFacade()
203 cloud_facade.connect(self.connection)
204
205 result = await cloud_facade.Clouds()
206 cloud = list(result.clouds.keys())[0] # only lives on one cloud
207 return tag.untag('cloud-', cloud)
208
209 def get_models(self, all_=False, username=None):
210 """Return list of available models on this controller.
211
212 :param bool all_: List all models, regardless of user accessibilty
213 (admin use only)
214 :param str username: User for which to list models (admin use only)
215
216 """
217 pass
218
219 def get_payloads(self, *patterns):
220 """Return list of known payloads.
221
222 :param str \*patterns: Patterns to match against
223
224 Each pattern will be checked against the following info in Juju::
225
226 - unit name
227 - machine id
228 - payload type
229 - payload class
230 - payload id
231 - payload tag
232 - payload status
233
234 """
235 pass
236
237 def get_users(self, all_=False):
238 """Return list of users that can connect to this controller.
239
240 :param bool all_: Include disabled users
241
242 """
243 pass
244
245 def login(self):
246 """Log in to this controller.
247
248 """
249 pass
250
251 def logout(self, force=False):
252 """Log out of this controller.
253
254 :param bool force: Don't fail even if user not previously logged in
255 with a password
256
257 """
258 pass
259
260 def get_model(self, name):
261 """Get a model by name.
262
263 :param str name: Model name
264
265 """
266 pass
267
268 def get_user(self, username):
269 """Get a user by name.
270
271 :param str username: Username
272
273 """
274 pass