async def connect_juju(ctrl_name=None, model_name=None, endpoint=None, username=None, password=None, cacert=None): controller = Controller(max_frame_size=MAX_FRAME_SIZE) # noqa if endpoint: await controller.connect(endpoint=endpoint, username=username, password=password, cacert=cacert) else: await controller.connect(ctrl_name) if endpoint: model = Model(max_frame_size=MAX_FRAME_SIZE) await model.connect(uuid=model_name, endpoint=endpoint, username=username, password=password, cacert=cacert) elif model_name: model = await controller.get_model(model_name) else: model = Model(max_frame_size=MAX_FRAME_SIZE) # noqa await model.connect() # HACK low unsettable timeout in the model model.charmstore._cs = CharmStore(timeout=60) return controller, model
async def test_normal_use(self, mock_connect, mock_disconnect): async with Model() as model: self.assertTrue(isinstance(model, Model)) self.assertTrue(mock_connect.called) self.assertTrue(mock_disconnect.called)
async def scaleDownWorkersOnMachineId(controller, machineId): if not ScalerDbConnector.isRemovable(machineId): return ("Cannot remove machine: " + machineId + " because it is marked as non removable") else: defaultModel = await controller.get_model("default") await defaultModel.get_status() model = Model() await model.connect() for units in defaultModel.units.items(): if units[1].machine is not None: if units[1].machine.entity_id == machineId: unitName = units[1].entity_id machineName = units[1].machine.entity_id machineToRemove = units[1].machine await units[1].remove() await machineToRemove.remove() ScalerDbConnector.deleteMachine(machineId) await model.disconnect() await controller.disconnect() return "Removed unit: " + unitName + " and machine: " + machineName await model.disconnect() await controller.disconnect() return "fail"
async def get_model(self, name='default'): """Get a model from the Juju Controller. Note: Model objects returned must call disconnected() before it goes out of scope.""" if not self.authenticated: await self.login() model = Model() uuid = await self.get_model_uuid(name) self.log.debug("JujuApi: Connecting to model {} ({})".format( model, uuid, )) await model.connect( self.endpoint, uuid, self.user, self.secret, None, ) return model
async def test_wait_for_active_status(self): # create a custom apps mock from types import SimpleNamespace apps = {"dummy_app": SimpleNamespace( status="active", units=[SimpleNamespace( name="mockunit/0", workload_status="active", workload_status_message="workload_status_message", machine=None, agent_status="idle", )], )} with patch.object(Model, 'applications', new_callable=PropertyMock) as mock_apps: mock_apps.return_value = apps m = Model() # pass "active" via `status` (str) await m.wait_for_idle(apps=["dummy_app"], status="active") # pass "active" via `wait_for_active` (bool; deprecated) await m.wait_for_idle(apps=["dummy_app"], wait_for_active=True) # use both `status` and `wait_for_active` - `wait_for_active` takes precedence await m.wait_for_idle(apps=["dummy_app"], wait_for_active=True, status="doesn't matter") mock_apps.assert_called_with()
async def model(request, tools): model = Model() await model.connect(tools.connection) if request.config.getoption("--is-upgrade"): upgrade_snap_channel = request.config.getoption( "--upgrade-snap-channel") upgrade_charm_channel = request.config.getoption( "--upgrade-charm-channel") if not upgrade_snap_channel and upgrade_charm_channel: raise Exception( "Must have both snap and charm upgrade " "channels set to perform upgrade prior to validation test.") print("Upgrading charms") await upgrade_charms(model, upgrade_charm_channel, tools) print("Upgrading snaps") await upgrade_snaps(model, upgrade_snap_channel, tools) if request.config.getoption("--snapd-upgrade"): snapd_channel = request.config.getoption("--snapd-channel") await log_snap_versions(model, prefix="Before") for unit in model.units.values(): if unit.dead: continue await unit.run(f"sudo snap refresh core --{snapd_channel}") await unit.run(f"sudo snap refresh snapd --{snapd_channel}") await log_snap_versions(model, prefix="After") yield model await model.disconnect()
async def main(): model = Model() await model.connect() await model.reset(force=True) goal_state = Model.from_yaml('bundle-like-thing') ubuntu_app = await model.deploy( 'ubuntu-0', application_name='ubuntu', series='trusty', channel='stable', ) ubuntu_app.on_unit_added(callback=lambda unit: True) await model.deploy( 'nrpe-11', application_name='nrpe', series='trusty', channel='stable', num_units=0, ) await model.add_relation( 'ubuntu', 'nrpe', ) result, ok = await model.block_until(lambda: model.matches(goal_state), timeout=600)
async def model(request, event_loop, connection_name): event_loop.set_exception_handler(lambda l, _: l.stop()) model = Model(event_loop) await model.connect(connection_name) if request.config.getoption("--is-upgrade"): upgrade_snap_channel = request.config.getoption( "--upgrade-snap-channel") upgrade_charm_channel = request.config.getoption( "--upgrade-charm-channel") if not upgrade_snap_channel and upgrade_charm_channel: raise Exception( "Must have both snap and charm upgrade channels set to perform upgrade prior to validation test." ) print("Upgrading charms") await upgrade_charms(model, upgrade_charm_channel) print("Upgrading snaps") await upgrade_snaps(model, upgrade_snap_channel) if request.config.getoption("--snapd-upgrade"): snapd_channel = request.config.getoption("--snapd-channel") cmd = f"sudo snap refresh core --{snapd_channel}" cloudinit_userdata = {"postruncmd": [cmd]} cloudinit_userdata_str = yaml.dump(cloudinit_userdata) await model.set_config({"cloudinit-userdata": cloudinit_userdata_str}) await model.deploy("cs:~containers/charmed-kubernetes") await log_snap_versions(model, prefix="Before") await asyncify(_juju_wait)() await log_snap_versions(model, prefix="After") yield model await model.disconnect()
async def model(controller): """Return the model for the test.""" model_name = os.getenv("PYTEST_MODEL") if model_name: # Reuse existing model _model = Model() full_name = "{}:{}".format(controller.controller_name, os.getenv("PYTEST_MODEL")) try: await _model.connect(full_name) except JujuConnectionError: # Let's create it since it's missing _model = await controller.add_model( model_name, cloud_name=os.getenv("PYTEST_CLOUD_NAME"), region=os.getenv("PYTEST_CLOUD_REGION"), ) else: # Create a new random model model_name = "functest-{}".format(str(uuid.uuid4())[-12:]) _model = await controller.add_model( model_name, cloud_name=os.getenv("PYTEST_CLOUD_NAME"), region=os.getenv("PYTEST_CLOUD_REGION"), ) # https://github.com/juju/python-libjuju/issues/267 subprocess.check_call(["juju", "models"]) while model_name not in await controller.list_models(): await asyncio.sleep(1) yield _model await _model.disconnect() if not os.getenv("PYTEST_KEEP_MODEL"): await controller.destroy_model(model_name) while model_name in await controller.list_models(): await asyncio.sleep(1)
async def main(): model = Model() print('Connecting to model') # connect to current model with current user, per Juju CLI await model.connect() try: print('Deploying local-charm') base_dir = Path(__file__).absolute().parent.parent charm_path = '{}/tests/integration/oci-image-charm'.format(base_dir) resources = {"oci-image": "ubuntu/latest"} application = await model.deploy( charm_path, resources=resources, ) print('Waiting for active') await model.block_until( lambda: all(unit.workload_status == 'active' for unit in application.units), timeout=120, ) print('Removing Charm') await application.remove() finally: print('Disconnecting from model') await model.disconnect()
async def run(): model = Model() await model.connect_current() await model.reset(force=True) ubuntu_app = await model.deploy( 'mysql', service_name='mysql', series='trusty', channel='stable', config={ 'tuning-level': 'safest', }, constraints={ 'mem': 256 * MB, }, ) # update and check app config await ubuntu_app.set_config({'tuning-level': 'fast'}) config = await ubuntu_app.get_config() assert(config['tuning-level']['value'] == 'fast') # update and check app constraints await ubuntu_app.set_constraints({'mem': 512 * MB}) constraints = await ubuntu_app.get_constraints() assert(constraints['mem'] == 512 * MB) await model.disconnect() model.loop.stop()
async def run_in_model(model_name, f, add_model_arg=False, awaitable=True): """Run the given function in the model matching the model_name :param model_name: Name of model to run function in :type model_name: str :param f: Function to run with given moel in focus :type f: functools.partial :param add_model_arg: Whether to add kwarg pointing at model to the given function before running it :type add_model_arg: boolean :param awaitable: Whether f is awaitable :type awaitable: boolean :returns: Output of f :rtype: Unknown, depends on the passed in function """ model = Model() await model.connect_model(model_name) output = None try: if add_model_arg: f.keywords.update(model=model) if awaitable: output = await f() else: output = f() finally: # Disconnect from the api server and cleanup. await model.disconnect() return output
async def main(): model = Model() print('Connecting to model') # Connect to current model with current user, per Juju CLI await model.connect() try: print('Deploying trusted bundle application ubuntu') applications = await model.deploy( 'cs:~juju-qa/bundle/basic-trusted-1', channel='beta', trust=True, ) print('Waiting for active') await model.block_until( lambda: all(unit.workload_status == 'active' for application in applications for unit in application.units)) print("Successfully deployed!") print('Removing bundle') for application in applications: await application.remove() finally: print('Disconnecting from model') await model.disconnect()
async def test_deploy(): # Get env variables CHARM_NAME = os.environ.get('CHARM_NAME') CHARM_PATH = os.path.join(os.environ.get('CHARM_BUILD_DIR'), CHARM_NAME) model = Model() print('Connecting to model') await model.connect_current() print('Resetting model') await model.reset(force=True) try: print('Deploying {} from {}'.format(CHARM_NAME, CHARM_PATH)) application = await model.deploy(entity_url=CHARM_PATH, application_name=CHARM_NAME) print('Waiting for active') await model.block_until( lambda: all(unit.workload_status == 'blocked' for unit in application.units)) print('Removing {}'.format(CHARM_NAME)) await application.remove() finally: print('Disconnecting from model') await model.disconnect()
async def _manual_scale(self, expected_units): log.info("Scaling '{}' to {} unit(s)...".format( SCALABLE_APP, expected_units)) self._configure({ "scaling_units_min": expected_units, "scaling_units_max": expected_units }) try: m = Model() await m.connect_current() try: for i in amulet.helpers.timeout_gen(300): actual_units = len(m.applications[SCALABLE_APP].units) if actual_units == expected_units: break await asyncio.sleep(5) finally: await m.disconnect() except amulet.helpers.TimeoutError: msg = ("The CharmScaler did not scale the application '{}' to {} " "unit(s) in time.").format(SCALABLE_APP, expected_units) amulet.raise_status(amulet.FAIL, msg=msg) except JujuAPIError as e: msg = ("Juju API error: {}").format(str(e)) amulet.raise_status(amulet.FAIL, msg=msg)
async def remove_ssh_key(usr, pwd, ssh_key, url, port, username): try: controllers = redis.StrictRedis(host=url, port=port, charset="utf-8", decode_responses=True, db=10) users = redis.StrictRedis(host=url, port=port, charset="utf-8", decode_responses=True, db=11) user = json.loads(users.get(username)) if ssh_key in user['ssh-keys']: user['ssh-keys'].remove(ssh_key) users.set(username, json.dumps(user)) for con in user['controllers']: for mod in con['models']: controller = json.loads(controllers.get(con['name'])) for modl in controller['models']: if modl['name'] == mod['name']: model = Model() logger.info( 'Setting up Modelconnection for model: %s', mod['name']) await model.connect(controller['endpoints'][0], modl['uuid'], usr, pwd, controller['ca-cert']) await model.remove_ssh_key(username, ssh_key) await model.disconnect() except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() lines = traceback.format_exception(exc_type, exc_value, exc_traceback) for l in lines: logger.error(l)
async def test_expose_with_exposed_endpoints_as_raw_dict(self, mock_conn): mock_facade_version = mock.MagicMock(return_value=13) mock_facade = mock.MagicMock(name="application_facade") mock_facade().Expose.return_value = asyncio.Future() mock_facade().Expose.return_value.set_result([]) app = Application(entity_id="app-id", model=Model()) app.name = "panther" app._facade = mock_facade app._facade_version = mock_facade_version # Check that if we pass a dict as would be the case when processing an # expose change, it gets correctly converted to ExposedEndpoint values, # validated and converted to a dictionary with the right format before # it gets passed to the facade. await app.expose(exposed_endpoints={ "": { "expose-to-spaces": ["alpha"], "expose-to-cidrs": ["0.0.0.0/0"] } }) mock_facade().Expose.assert_called_once_with( application="panther", exposed_endpoints={ "": { "expose-to-spaces": ["alpha"], "expose-to-cidrs": ["0.0.0.0/0"] } })
async def main(): model = Model() # connect to current model with current user, per Juju CLI await model.connect() ubuntu_app = await model.deploy( 'cs:mysql', application_name='mysql', series='trusty', channel='stable', config={ 'tuning-level': 'safest', }, constraints={ 'mem': 256 * MB, }, ) # update and check app config await ubuntu_app.set_config({'tuning-level': 'fast'}) config = await ubuntu_app.get_config() assert(config['tuning-level']['value'] == 'fast') # update and check app constraints await ubuntu_app.set_constraints({'mem': 512 * MB}) constraints = await ubuntu_app.get_constraints() assert(constraints['mem'] == 512 * MB) await model.disconnect()
def test_apply_delta(self): from juju.model import Model from juju.application import Application model = Model() model._connector = mock.MagicMock() delta = _make_delta('application', 'add', dict(name='foo')) # test add prev, new = model.state.apply_delta(delta) self.assertEqual( len(model.state.state[delta.entity][delta.get_id()]), 1) self.assertIsNone(prev) self.assertIsInstance(new, Application) # test remove delta.type = 'remove' prev, new = model.state.apply_delta(delta) # length of the entity history deque is now 3: # - 1 for the first delta # - 1 for the second delta # - 1 for the None sentinel appended after the 'remove' self.assertEqual( len(model.state.state[delta.entity][delta.get_id()]), 3) self.assertIsInstance(new, Application) # new object is falsy because its data is None self.assertFalse(new) self.assertIsInstance(prev, Application) self.assertTrue(prev)
async def add_unit(c_name, m_name, usr, pwd, url, port, app_name, amount, target): try: controllers = redis.StrictRedis(host=url, port=port, charset="utf-8", decode_responses=True, db=10) controller = json.loads(controllers.get(c_name)) model = Model() logger.info('Setting up Model connection for %s:%s', c_name, m_name) for mod in controller['models']: if mod['name'] == m_name: await model.connect(controller['endpoints'][0], mod['uuid'], usr, pwd, controller['ca-cert']) for app, entity in model.state.applications.items(): if app == app_name: logger.info('Adding units to %s', app_name) if target == 'None': target = None await entity.add_unit(count=int(amount), to=target) logger.info('Units added to %s', app_name) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() lines = traceback.format_exception(exc_type, exc_value, exc_traceback) for l in lines: logger.error(l) finally: if 'model' in locals(): await model.disconnect()
async def main(): model = Model() print('Connecting to model') # connect to current model with current user, per Juju CLI await model.connect() try: print('Deploying ubuntu') application = await model.deploy( 'cs:ubuntu-10', application_name='ubuntu', series='trusty', channel='stable', ) print('Waiting for active') await model.block_until( lambda: all(unit.workload_status == 'active' for unit in application.units)) print('Removing ubuntu') await application.remove() finally: print('Disconnecting from model') await model.disconnect()
async def add_model(self, model_name, cloud_name=None, credential_name=None, owner=None, config=None, region=None): """Add a model to this controller. :param str model_name: Name to give the new model. :param str cloud_name: Name of the cloud in which to create the model, e.g. 'aws'. Defaults to same cloud as controller. :param str credential_name: Name of the credential to use when creating the model. If not given, it will attempt to find a default credential. :param str owner: Username that will own the model. Defaults to the current user. :param dict config: Model configuration. :param str region: Region in which to create the model. :return Model: A connection to the newly created model. """ model_facade = client.ModelManagerFacade.from_connection( self.connection()) owner = owner or self.connection().info['user-info']['identity'] cloud_name = cloud_name or await self.get_cloud() try: # attempt to add/update the credential from local data if available credential_name = await self.add_credential(name=credential_name, cloud=cloud_name, owner=owner) except errors.JujuError: # if it's not available locally, assume it's on the controller pass if credential_name: credential = tag.credential(cloud_name, tag.untag('user-', owner), credential_name) else: credential = None log.debug('Creating model %s', model_name) if not config or 'authorized-keys' not in config: config = config or {} config['authorized-keys'] = await utils.read_ssh_key( loop=self._connector.loop) model_info = await model_facade.CreateModel(tag.cloud(cloud_name), config, credential, model_name, owner, region) from juju.model import Model model = Model(jujudata=self._connector.jujudata) kwargs = self.connection().connect_params() kwargs['uuid'] = model_info.uuid await model._connect_direct(**kwargs) return model
async def __aenter__(self): loop = asyncio.get_event_loop() loop.set_exception_handler(lambda l, _: l.stop()) self._model = Model(loop) model_name = "{}:{}".format(self._controller_name, self._model_name) await self._model.connect(model_name) return self._model
def __init__(self, token, controller, model): con = datastore.get_controller(controller) self.c_endpoint = con['endpoints'][0] self.c_cacert = con['ca-cert'] self.m_name = model self.m_access = datastore.get_model_access(controller, self.m_name, token.username) self.m_uuid = datastore.get_model(controller, self.m_name)['uuid'] self.m_connection = Model()
async def test_with_endpoint_and_uuid_with_bakery(self, mock_connect, _): m = Model() await m.connect(endpoint='0.1.2.3:4566', uuid='some-uuid', bakery_client='bakery') mock_connect.assert_called_once_with(endpoint='0.1.2.3:4566', uuid='some-uuid', bakery_client='bakery')
async def _deploy_in_loop(new_loop, model_name, jujudata): new_model = Model(new_loop, jujudata=jujudata) await new_model.connect(model_name) try: await new_model.deploy('cs:xenial/ubuntu') assert 'ubuntu' in new_model.applications finally: await new_model.disconnect()
async def main(): model = Model() print('Connecting to model') # connect to current model with current user, per Juju CLI await model.connect() try: print('Deploying ubuntu') application = await model.deploy( 'cs:~jameinel/ubuntu-lite-7', application_name='ubuntu', series='trusty', channel='stable', ) print('Waiting for active') await model.block_until(lambda: all(unit.workload_status == 'active' for unit in application.units)) print('Expose all opened port ranges') await application.expose() print( 'Expose all opened port ranges to the CIDRs that correspond to a list of spaces' ) await application.expose( exposed_endpoints={"": ExposedEndpoint(to_spaces=["alpha"])}) print('Expose all opened port ranges to a list of CIDRs') await application.expose( exposed_endpoints={"": ExposedEndpoint(to_cidrs=["10.0.0.0/24"])}) print('Expose all opened port ranges to a list of spaces and CIDRs') await application.expose(exposed_endpoints={ "": ExposedEndpoint(to_spaces=["alpha"], to_cidrs=["10.0.0.0/24"]) }) print( 'Expose individual endpoints to different space/CIDR combinations') await application.expose( exposed_endpoints={ "": ExposedEndpoint(to_spaces=["alpha"], to_cidrs=["10.0.0.0/24"]), "ubuntu": ExposedEndpoint(to_cidrs=["10.42.42.0/24"]) }) print('Unexpose individual endpoints (other endpoints remain exposed)') await application.unexpose(exposed_endpoints=["ubuntu"]) print('Unexpose application') await application.unexpose() print('Removing ubuntu') await application.remove() finally: print('Disconnecting from model') await model.disconnect()
async def get_units(): model = Model() if MODEL is None: await model.connect() else: await model.connect_model(MODEL) units = sorted(model.applications['ubuntu'].units, key=lambda u: u.name) await model.disconnect() return units
async def test_model_connect_with_endpoint_and_uuid( self, mock_after_connect, mock_connect, ): from juju.model import Model m = Model() await m.connect(endpoint='0.1.2.3:4566', uuid='some-uuid') mock_connect.assert_called_once_with(endpoint='0.1.2.3:4566', uuid='some-uuid')
async def watch(): model = Model() await model.connect() allwatcher = client.AllWatcherFacade.from_connection(model.connection()) while True: change = await allwatcher.Next() for delta in change.deltas: print(delta.deltas)