244
248
self.config = config
245
249
self.juju_home = juju_home
246
250
if self.config is not None:
247
self.local = bool(self.config.get('type') == 'local')
252
provider = self.provider
255
self.local = bool(provider == 'local')
249
257
self.local and bool(self.config.get('container') == 'kvm'))
250
self.maas = bool(self.config.get('type') == 'maas')
251
self.joyent = bool(self.config.get('type') == 'joyent')
258
self.maas = bool(provider == 'maas')
259
self.joyent = bool(provider == 'joyent')
253
261
self.local = False
255
263
self.maas = False
256
264
self.joyent = False
268
"""Return the provider type for this environment.
270
See get_cloud to determine the specific cloud.
273
return self.config['type']
275
raise NoProvider('No provider specified.')
277
def get_region(self):
278
provider = self.provider
279
if provider == 'azure':
280
if 'tenant-id' not in self.config:
281
return self.config['location'].replace(' ', '').lower()
282
return self.config['location']
283
elif provider == 'joyent':
284
matcher = re.compile('https://(.*).api.joyentcloud.com')
285
return matcher.match(self.config['sdc-url']).group(1)
286
elif provider == 'lxd':
288
elif provider == 'manual':
289
return self.config['bootstrap-host']
290
elif provider in ('maas', 'manual'):
293
return self.config['region']
295
def set_region(self, region):
297
provider = self.provider
300
if provider == 'azure':
301
self.config['location'] = region
302
elif provider == 'joyent':
303
self.config['sdc-url'] = (
304
'https://{}.api.joyentcloud.com'.format(region))
305
elif provider == 'lxd':
306
if region != 'localhost':
307
raise ValueError('Only "localhost" allowed for lxd.')
308
elif provider == 'manual':
309
self.config['bootstrap-host'] = region
310
elif provider == 'maas':
311
if region is not None:
312
raise ValueError('Only None allowed for maas.')
314
self.config['region'] = region
258
316
def clone(self, model_name=None):
259
317
config = deepcopy(self.config)
260
318
if model_name is None:
1283
1302
def bootstrap(self, upload_tools=False, bootstrap_series=None,
1284
1303
credential=None, auto_upgrade=False, metadata_source=None,
1285
to=None, agent_version=None):
1304
to=None, no_gui=False, agent_version=None):
1286
1305
"""Bootstrap a controller."""
1287
1306
self._check_bootstrap()
1288
1307
with self._bootstrap_config() as config_filename:
1289
1308
args = self.get_bootstrap_args(
1290
1309
upload_tools, config_filename, bootstrap_series, credential,
1291
auto_upgrade, metadata_source, to, agent_version)
1310
auto_upgrade, metadata_source, to, no_gui, agent_version)
1292
1311
self.update_user_name()
1293
1312
self.juju('bootstrap', args, include_e=False)
1295
1314
@contextmanager
1296
1315
def bootstrap_async(self, upload_tools=False, bootstrap_series=None,
1297
auto_upgrade=False, metadata_source=None, to=None):
1316
auto_upgrade=False, metadata_source=None, to=None,
1298
1318
self._check_bootstrap()
1299
1319
with self._bootstrap_config() as config_filename:
1300
1320
args = self.get_bootstrap_args(
1301
1321
upload_tools, config_filename, bootstrap_series, None,
1302
auto_upgrade, metadata_source, to)
1322
auto_upgrade, metadata_source, to, no_gui)
1303
1323
self.update_user_name()
1304
1324
with self.juju_async('bootstrap', args, include_e=False):
2255
2283
args.append('--auto-upgrade')
2256
2284
if to is not None:
2257
2285
args.extend(['--to', to])
2287
args.append('--no-gui')
2258
2288
return tuple(args)
2261
class EnvJujuClient2B9(EnvJujuClientRC):
2263
def update_user_name(self):
2266
def create_cloned_environment(
2267
self, cloned_juju_home, controller_name, user_name=None):
2268
"""Create a cloned environment.
2270
`user_name` is unused in this version of beta.
2272
user_client = self.clone(env=self.env.clone())
2273
user_client.env.juju_home = cloned_juju_home
2274
# New user names the controller.
2275
user_client.env.controller = Controller(controller_name)
2278
def get_model_uuid(self):
2279
name = self.env.environment
2280
output_yaml = self.get_juju_output('show-model', '--format', 'yaml')
2281
output = yaml.safe_load(output_yaml)
2282
return output[name]['model-uuid']
2284
def add_user_perms(self, username, models=None, permissions='read'):
2285
"""Adds provided user and return register command arguments.
2287
:return: Registration token provided by the add-user command.
2291
models = self.env.environment
2293
args = (username, '--models', models, '--acl', permissions,
2294
'-c', self.env.controller.name)
2296
output = self.get_juju_output('add-user', *args, include_e=False)
2297
return self._get_register_command(output)
2299
def grant(self, user_name, permission, model=None):
2300
"""Grant the user with a model."""
2302
model = self.model_name
2303
self.juju('grant', (user_name, model, '--acl', permission),
2306
def revoke(self, username, models=None, permissions='read'):
2308
models = self.env.environment
2310
args = (username, models, '--acl', permissions)
2312
self.controller_juju('revoke', args)
2314
def set_config(self, service, options):
2315
option_strings = self._dict_as_option_strings(options)
2316
self.juju('set-config', (service,) + option_strings)
2318
def get_config(self, service):
2319
return yaml.safe_load(self.get_juju_output('get-config', service))
2321
def get_model_config(self):
2322
"""Return the value of the environment's configured option."""
2323
return yaml.safe_load(
2324
self.get_juju_output('get-model-config', '--format', 'yaml'))
2326
def get_env_option(self, option):
2327
"""Return the value of the environment's configured option."""
2328
return self.get_juju_output('get-model-config', option)
2330
def set_env_option(self, option, value):
2331
"""Set the value of the option in the environment."""
2332
option_value = "%s=%s" % (option, value)
2333
return self.juju('set-model-config', (option_value,))
2335
def unset_env_option(self, option):
2336
"""Unset the value of the option in the environment."""
2337
return self.juju('unset-model-config', (option,))
2339
def list_disabled_commands(self):
2340
"""List all the commands disabled on the model."""
2341
raw = self.get_juju_output('block list', '--format', 'yaml')
2342
return yaml.safe_load(raw)
2344
def disable_command(self, args):
2345
"""Disable a command set."""
2346
return self.juju('block', args)
2348
def enable_command(self, args):
2349
"""Enable a command set."""
2350
return self.juju('unblock', args)
2352
def enable_ha(self):
2353
self.juju('enable-ha', ('-n', '3'))
2356
class EnvJujuClient2B8(EnvJujuClient2B9):
2291
class EnvJujuClient1X(EnvJujuClientRC):
2292
"""Base for all 1.x client drivers."""
2294
default_backend = Juju1XBackend
2296
config_class = SimpleEnvironment
2358
2298
status_class = ServiceStatus
2360
def remove_service(self, service):
2361
self.juju('remove-service', (service,))
2363
def run(self, commands, applications):
2364
responses = self.get_juju_output(
2365
'run', '--format', 'json', '--service', ','.join(applications),
2367
return json.loads(responses)
2369
def deployer(self, bundle_template, name=None, deploy_delay=10,
2371
"""Deploy a bundle using deployer."""
2372
bundle = self.format_bundle(bundle_template)
2375
'--deploy-delay', str(deploy_delay),
2376
'--timeout', str(timeout),
2381
e_arg = ('-e', 'local.{}:{}'.format(
2382
self.env.controller.name, self.env.environment))
2384
self.juju('deployer', args, self.env.needs_sudo(), include_e=False)
2387
class EnvJujuClient2B7(EnvJujuClient2B8):
2389
def get_controller_model_name(self):
2390
"""Return the name of the 'controller' model.
2392
Return the name of the environment when an 'controller' model does
2398
class EnvJujuClient2B3(EnvJujuClient2B7):
2400
def _add_model(self, model_name, config_file):
2401
self.controller_juju('create-model', (
2402
model_name, '--config', config_file))
2405
class EnvJujuClient2B2(EnvJujuClient2B3):
2407
def get_bootstrap_args(
2408
self, upload_tools, config_filename, bootstrap_series=None,
2409
credential=None, auto_upgrade=False, metadata_source=None,
2410
to=None, agent_version=None):
2411
"""Return the bootstrap arguments for the substrate."""
2412
err_fmt = 'EnvJujuClient2B2 does not support bootstrap argument {}'
2414
raise ValueError(err_fmt.format('auto_upgrade'))
2415
if metadata_source is not None:
2416
raise ValueError(err_fmt.format('metadata_source'))
2418
raise ValueError(err_fmt.format('to'))
2419
if agent_version is not None:
2420
raise ValueError(err_fmt.format('agent_version'))
2422
# Only accept kvm packages by requiring >1 cpu core, see lp:1446264
2423
constraints = 'mem=2G cpu-cores=1'
2425
constraints = 'mem=2G'
2426
cloud_region = self.get_cloud_region(self.env.get_cloud(),
2427
self.env.get_region())
2428
args = ['--constraints', constraints, self.env.environment,
2429
cloud_region, '--config', config_filename]
2431
args.insert(0, '--upload-tools')
2433
args.extend(['--agent-version', self.get_matching_agent_version()])
2435
if bootstrap_series is not None:
2436
args.extend(['--bootstrap-series', bootstrap_series])
2438
if credential is not None:
2439
args.extend(['--credential', credential])
2443
def get_controller_client(self):
2444
"""Return a client for the controller model. May return self."""
2447
def get_controller_model_name(self):
2448
"""Return the name of the 'controller' model.
2450
Return the name of the environment when an 'controller' model does
2453
models = self.get_models()
2454
# The dict can be empty because 1.x does not support the models.
2455
# This is an ambiguous case for the jes feature flag which supports
2456
# multiple models, but none is named 'admin' by default. Since the
2457
# jes case also uses '-e' for models, the env is the controller model.
2458
for model in models.get('models', []):
2459
if 'admin' in model['name']:
2461
return self.env.environment
2464
class EnvJujuClient2A2(EnvJujuClient2B2):
2465
"""Drives Juju 2.0-alpha2 clients."""
2467
default_backend = Juju2A2Backend
2469
config_class = SimpleEnvironment
2300
# The environments.yaml options that are replaced by bootstrap options.
2301
# For Juju 1.x, no bootstrap options are used.
2302
bootstrap_replaces = frozenset()
2304
destroy_model_command = 'destroy-environment'
2306
supported_container_types = frozenset([KVM_MACHINE, LXC_MACHINE])
2308
agent_metadata_url = 'tools-metadata-url'
2310
_show_status = 'status'
2472
2313
def _get_env(cls, env):
2475
2316
'JujuData cannot be used with {}'.format(cls.__name__))
2478
def bootstrap(self, upload_tools=False, bootstrap_series=None):
2479
"""Bootstrap a controller."""
2480
self._check_bootstrap()
2481
args = self.get_bootstrap_args(upload_tools, bootstrap_series)
2482
self.juju('bootstrap', args, self.env.needs_sudo())
2485
def bootstrap_async(self, upload_tools=False):
2486
self._check_bootstrap()
2487
args = self.get_bootstrap_args(upload_tools)
2488
with self.juju_async('bootstrap', args):
2490
log.info('Waiting for bootstrap of {}.'.format(
2491
self.env.environment))
2493
def get_bootstrap_args(self, upload_tools, bootstrap_series=None,
2495
"""Return the bootstrap arguments for the substrate."""
2496
if credential is not None:
2498
'--credential is not supported by this juju version.')
2499
constraints = self._get_substrate_constraints()
2500
args = ('--constraints', constraints,
2501
'--agent-version', self.get_matching_agent_version())
2503
args = ('--upload-tools',) + args
2504
if bootstrap_series is not None:
2505
args = args + ('--bootstrap-series', bootstrap_series)
2508
def deploy(self, charm, repository=None, to=None, series=None,
2509
service=None, force=False, storage=None, constraints=None):
2511
if repository is not None:
2512
args.extend(['--repository', repository])
2514
args.extend(['--to', to])
2515
if service is not None:
2516
args.extend([service])
2517
if storage is not None:
2518
args.extend(['--storage', storage])
2519
if constraints is not None:
2520
args.extend(['--constraints', constraints])
2521
return self.juju('deploy', tuple(args))
2524
class EnvJujuClient2A1(EnvJujuClient2A2):
2525
"""Drives Juju 2.0-alpha1 clients."""
2527
_show_status = 'status'
2529
default_backend = Juju1XBackend
2531
2319
def get_cache_path(self):
2532
2320
return get_cache_path(self.env.juju_home, models=False)