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