diff --git a/unit_tests/utilities/test_zaza_utilities_juju.py b/unit_tests/utilities/test_zaza_utilities_juju.py index d62534a..11bbcbe 100644 --- a/unit_tests/utilities/test_zaza_utilities_juju.py +++ b/unit_tests/utilities/test_zaza_utilities_juju.py @@ -93,7 +93,7 @@ class TestJujuUtils(ut_utils.BaseTestCase): def test_get_full_juju_status(self): self.assertEqual(juju_utils.get_full_juju_status(), self.juju_status) - self.model.get_status.assert_called_once_with() + self.model.get_status.assert_called_once_with(model_name=None) def test_get_machines_for_application(self): self.patch_object(juju_utils, "get_application_status") @@ -106,7 +106,7 @@ class TestJujuUtils(ut_utils.BaseTestCase): self.get_application_status.assert_called_once() # Subordinate application has no units - def _get_application_status(application): + def _get_application_status(application, model_name=None): _apps = { self.application: self.application_data, self.subordinate_application: @@ -155,7 +155,7 @@ class TestJujuUtils(ut_utils.BaseTestCase): self.application)), self.machine_data.get("instance-id")) self.get_machines_for_application.assert_called_once_with( - self.application) + self.application, model_name=None) def test_get_provider_type(self): self.patch_object(juju_utils, "get_cloud_configs") @@ -171,12 +171,17 @@ class TestJujuUtils(ut_utils.BaseTestCase): self.assertEqual(juju_utils.remote_run(self.unit, _cmd), self.run_output["Stdout"]) self.model.run_on_unit.assert_called_once_with( - self.unit, _cmd, timeout=None) + self.unit, _cmd, timeout=None, model_name=None) # Non-fatal failure self.model.run_on_unit.return_value = self.error_run_output - self.assertEqual(juju_utils.remote_run(self.unit, _cmd, fatal=False), - self.error_run_output["Stderr"]) + self.assertEqual( + juju_utils.remote_run( + self.unit, + _cmd, + fatal=False, + model_name=None), + self.error_run_output["Stderr"]) # Fatal failure with self.assertRaises(Exception): @@ -205,7 +210,8 @@ class TestJujuUtils(ut_utils.BaseTestCase): 'arelation') self.model.run_on_unit.assert_called_with( 'aunit/0', - 'relation-get --format=yaml -r "42" - "otherunit/0"') + 'relation-get --format=yaml -r "42" - "otherunit/0"', + model_name=None) self.yaml.safe_load.assert_called_with(str(data)) def test_get_relation_from_unit_fails(self): @@ -220,7 +226,8 @@ class TestJujuUtils(ut_utils.BaseTestCase): 'arelation') self.model.run_on_unit.assert_called_with( 'aunit/0', - 'relation-get --format=yaml -r "42" - "otherunit/0"') + 'relation-get --format=yaml -r "42" - "otherunit/0"', + model_name=None) self.assertFalse(self.yaml.safe_load.called) def test_leader_get(self): @@ -231,7 +238,7 @@ class TestJujuUtils(ut_utils.BaseTestCase): 'Code': 0, 'Stdout': str(data)} juju_utils.leader_get('application') self.model.run_on_leader.assert_called_with( - 'application', 'leader-get --format=yaml ') + 'application', 'leader-get --format=yaml ', model_name=None) self.yaml.safe_load.assert_called_with(str(data)) def test_leader_get_key(self): @@ -242,7 +249,7 @@ class TestJujuUtils(ut_utils.BaseTestCase): 'Code': 0, 'Stdout': data['foo']} juju_utils.leader_get('application', 'foo') self.model.run_on_leader.assert_called_with( - 'application', 'leader-get --format=yaml foo') + 'application', 'leader-get --format=yaml foo', model_name=None) self.yaml.safe_load.assert_called_with(data['foo']) def test_leader_get_fails(self): @@ -253,7 +260,8 @@ class TestJujuUtils(ut_utils.BaseTestCase): with self.assertRaises(Exception): juju_utils.leader_get('application') self.model.run_on_leader.assert_called_with( - 'application', 'leader-get --format=yaml ') + 'application', 'leader-get --format=yaml ', + model_name=None) self.assertFalse(self.yaml.safe_load.called) def test_get_machine_series(self): @@ -267,6 +275,7 @@ class TestJujuUtils(ut_utils.BaseTestCase): actual = juju_utils.get_machine_series('6') self._get_machine_status.assert_called_with( machine='6', - key='series' + key='series', + model_name=None ) self.assertEqual(expected, actual) diff --git a/zaza/openstack/utilities/generic.py b/zaza/openstack/utilities/generic.py index 7c66793..dbeb7d3 100644 --- a/zaza/openstack/utilities/generic.py +++ b/zaza/openstack/utilities/generic.py @@ -85,21 +85,23 @@ def get_unit_hostnames(units): return host_names -def get_pkg_version(application, pkg): +def get_pkg_version(application, pkg, model_name=None): """Return package version. :param application: Application name :type application: string :param pkg: Package name :type pkg: string + :param model_name: Name of model to query. + :type model_name: str :returns: List of package version :rtype: list """ versions = [] - units = model.get_units(application) + units = model.get_units(application, model_name=model_name) for unit in units: cmd = 'dpkg -l | grep {}'.format(pkg) - out = juju_utils.remote_run(unit.entity_id, cmd) + out = juju_utils.remote_run(unit.entity_id, cmd, model_name=model_name) versions.append(out.split('\n')[0].split()[2]) if len(set(versions)) != 1: raise Exception('Unexpected output from pkg version check') diff --git a/zaza/openstack/utilities/juju.py b/zaza/openstack/utilities/juju.py index a5edba2..fd7abc1 100644 --- a/zaza/openstack/utilities/juju.py +++ b/zaza/openstack/utilities/juju.py @@ -25,13 +25,15 @@ from zaza import ( from zaza.openstack.utilities import generic as generic_utils -def get_application_status(application=None, unit=None): +def get_application_status(application=None, unit=None, model_name=None): """Return the juju status for an application. :param application: Application name :type application: string :param unit: Specific unit :type unit: string + :param model_name: Name of model to query. + :type model_name: str :returns: Juju status output for an application :rtype: dict """ @@ -66,95 +68,112 @@ def get_cloud_configs(cloud=None): return generic_utils.get_yaml_config(cloud_config) -def get_full_juju_status(): +def get_full_juju_status(model_name=None): """Return the full juju status output. + :param model_name: Name of model to query. + :type model_name: str :returns: Full juju status output :rtype: dict """ - status = model.get_status() + status = model.get_status(model_name=model_name) return status -def get_machines_for_application(application): +def get_machines_for_application(application, model_name=None): """Return machines for a given application. :param application: Application name :type application: string + :param model_name: Name of model to query. + :type model_name: str :returns: machines for an application :rtype: Iterator[str] """ - status = get_application_status(application) + status = get_application_status(application, model_name=model_name) if not status: raise StopIteration # libjuju juju status no longer has units for subordinate charms # Use the application it is subordinate-to to find machines if status.get("units") is None and status.get("subordinate-to"): - status = get_application_status(status.get("subordinate-to")[0]) + status = get_application_status(status.get("subordinate-to")[0], + model_name=model_name) for unit in status.get("units").keys(): yield status.get("units").get(unit).get("machine") -def get_unit_name_from_host_name(host_name, application): +def get_unit_name_from_host_name(host_name, application, model_name=None): """Return the juju unit name corresponding to a hostname. :param host_name: Host name to map to unit name. :type host_name: string :param application: Application name :type application: string + :param model_name: Name of model to query. + :type model_name: str """ # Assume that a juju managed hostname always ends in the machine number. machine_number = host_name.split('-')[-1] unit_names = [ u.entity_id - for u in model.get_units(application_name=application) + for u in model.get_units(application_name=application, + model_name=model_name) if int(u.data['machine-id']) == int(machine_number)] return unit_names[0] -def get_machine_status(machine, key=None): +def get_machine_status(machine, key=None, model_name=None): """Return the juju status for a machine. :param machine: Machine number :type machine: string :param key: Key option requested :type key: string + :param model_name: Name of model to query. + :type model_name: str :returns: Juju status output for a machine :rtype: dict """ - status = get_full_juju_status() + status = get_full_juju_status(model_name=model_name) status = status.machines.get(machine) if key: status = status.get(key) return status -def get_machine_series(machine): +def get_machine_series(machine, model_name=None): """Return the juju series for a machine. :param machine: Machine number :type machine: string + :param model_name: Name of model to query. + :type model_name: str :returns: Juju series :rtype: string """ return get_machine_status( machine=machine, - key='series' + key='series', + model_name=model_name ) -def get_machine_uuids_for_application(application): +def get_machine_uuids_for_application(application, model_name=None): """Return machine uuids for a given application. :param application: Application name :type application: string + :param model_name: Name of model to query. + :type model_name: str :returns: machine uuuids for an application :rtype: Iterator[str] """ - for machine in get_machines_for_application(application): - yield get_machine_status(machine, key="instance-id") + for machine in get_machines_for_application(application, + model_name=model_name): + yield get_machine_status(machine, key="instance-id", + model_name=model_name) def get_provider_type(): @@ -175,7 +194,7 @@ def get_provider_type(): return "openstack" -def remote_run(unit, remote_cmd, timeout=None, fatal=None): +def remote_run(unit, remote_cmd, timeout=None, fatal=None, model_name=None): """Run command on unit and return the output. NOTE: This function is pre-deprecated. As soon as libjuju unit.run is able @@ -187,13 +206,19 @@ def remote_run(unit, remote_cmd, timeout=None, fatal=None): :type arg: int :param fatal: Command failure condidered fatal or not :type fatal: boolean + :param model_name: Name of model to query. + :type model_name: str :returns: Juju run output :rtype: string :raises: model.CommandRunFailed """ if fatal is None: fatal = True - result = model.run_on_unit(unit, remote_cmd, timeout=timeout) + result = model.run_on_unit( + unit, + remote_cmd, + timeout=timeout, + model_name=model_name) if result: if int(result.get("Code")) == 0: return result.get("Stdout") @@ -203,7 +228,7 @@ def remote_run(unit, remote_cmd, timeout=None, fatal=None): return result.get("Stderr") -def _get_unit_names(names): +def _get_unit_names(names, model_name=None): """Resolve given application names to first unit name of said application. Helper function that resolves application names to first unit name of @@ -211,6 +236,8 @@ def _get_unit_names(names): :param names: List of units/applications to translate :type names: list(str) + :param model_name: Name of model to query. + :type model_name: str :returns: List of units :rtype: list(str) """ @@ -219,11 +246,14 @@ def _get_unit_names(names): if '/' in name: result.append(name) else: - result.append(model.get_first_unit_name(name)) + result.append(model.get_first_unit_name( + name, + model_name=model_name)) return result -def get_relation_from_unit(entity, remote_entity, remote_interface_name): +def get_relation_from_unit(entity, remote_entity, remote_interface_name, + model_name=None): """Get relation data passed between two units. Get relation data for relation with `remote_interface_name` between @@ -240,6 +270,8 @@ def get_relation_from_unit(entity, remote_entity, remote_interface_name): :param remote_interface_name: Name of interface to query on remote end of relation :type remote_interface_name: str + :param model_name: Name of model to query. + :type model_name: str :returns: dict with relation data :rtype: dict :raises: model.CommandRunFailed @@ -247,27 +279,34 @@ def get_relation_from_unit(entity, remote_entity, remote_interface_name): application = entity.split('/')[0] remote_application = remote_entity.split('/')[0] rid = model.get_relation_id(application, remote_application, - remote_interface_name=remote_interface_name) - (unit, remote_unit) = _get_unit_names([entity, remote_entity]) + remote_interface_name=remote_interface_name, + model_name=model_name) + (unit, remote_unit) = _get_unit_names( + [entity, remote_entity], + model_name=model_name) cmd = 'relation-get --format=yaml -r "{}" - "{}"' .format(rid, remote_unit) - result = model.run_on_unit(unit, cmd) + result = model.run_on_unit(unit, cmd, model_name=model_name) if result and int(result.get('Code')) == 0: return yaml.safe_load(result.get('Stdout')) else: raise model.CommandRunFailed(cmd, result) -def leader_get(application, key=''): +def leader_get(application, key='', model_name=None): """Get leader settings from leader unit of named application. :param application: Application to get leader settings from. :type application: str + :param key: Key option requested + :type key: string + :param model_name: Name of model to query. + :type model_name: str :returns: dict with leader settings :rtype: dict :raises: model.CommandRunFailed """ cmd = 'leader-get --format=yaml {}'.format(key) - result = model.run_on_leader(application, cmd) + result = model.run_on_leader(application, cmd, model_name=model_name) if result and int(result.get('Code')) == 0: return yaml.safe_load(result.get('Stdout')) else: diff --git a/zaza/openstack/utilities/openstack.py b/zaza/openstack/utilities/openstack.py index 9a755e8..cb1b6fd 100644 --- a/zaza/openstack/utilities/openstack.py +++ b/zaza/openstack/utilities/openstack.py @@ -321,13 +321,16 @@ def get_aodh_session_client(session): return aodh_client.Client(session=session) -def get_keystone_scope(): +def get_keystone_scope(model_name=None): """Return Keystone scope based on OpenStack release of the overcloud. + :param model_name: Name of model to query. + :type model_name: str :returns: String keystone scope :rtype: string """ - os_version = get_current_os_versions("keystone")["keystone"] + os_version = get_current_os_versions("keystone", + model_name=model_name)["keystone"] # Keystone policy.json shipped the charm with liberty requires a domain # scoped token. Bug #1649106 if os_version == "liberty": @@ -362,17 +365,20 @@ def get_keystone_session(openrc_creds, scope='PROJECT', verify=None): return session.Session(auth=auth, verify=verify) -def get_overcloud_keystone_session(verify=None): +def get_overcloud_keystone_session(verify=None, model_name=None): """Return Over cloud keystone session. :param verify: Control TLS certificate verification behaviour :type verify: any + :param model_name: Name of model to query. + :type model_name: str :returns keystone_session: keystoneauth1.session.Session object :rtype: keystoneauth1.session.Session """ - return get_keystone_session(get_overcloud_auth(), - scope=get_keystone_scope(), - verify=verify) + return get_keystone_session( + get_overcloud_auth(model_name=model_name), + scope=get_keystone_scope(model_name=model_name), + verify=verify) def get_undercloud_keystone_session(verify=None): @@ -572,7 +578,6 @@ def add_interface_to_netplan(server_name, mac_address): unit_name = juju_utils.get_unit_name_from_host_name( server_name, application_name) - run_cmd_nic = "ip -f link -br -o addr|grep {}".format(mac_address) interface = model.run_on_unit(unit_name, run_cmd_nic) interface = interface['Stdout'].split(' ')[0] @@ -1394,11 +1399,13 @@ def get_os_code_info(package, pkg_version): return OPENSTACK_CODENAMES[vers] -def get_current_os_versions(deployed_applications): +def get_current_os_versions(deployed_applications, model_name=None): """Determine OpenStack codename of deployed applications. :param deployed_applications: List of deployed applications :type deployed_applications: list + :param model_name: Name of model to query. + :type model_name: str :returns: List of aplication to codenames dictionaries :rtype: list """ @@ -1408,7 +1415,8 @@ def get_current_os_versions(deployed_applications): continue version = generic_utils.get_pkg_version(application['name'], - application['type']['pkg']) + application['type']['pkg'], + model_name=model_name) versions[application['name']] = ( get_os_code_info(application['type']['pkg'], version)) return versions @@ -1473,17 +1481,21 @@ def get_os_release(release_pair=None): return index -def get_application_config_option(application, option): +def get_application_config_option(application, option, model_name=None): """Return application configuration. :param application: Name of application :type application: string :param option: Specific configuration option :type option: string + :param model_name: Name of model to query. + :type model_name: str :returns: Value of configuration option :rtype: Configuration option value type """ - application_config = model.get_application_config(application) + application_config = model.get_application_config( + application, + model_name=model_name) try: return application_config.get(option).get('value') except AttributeError: @@ -1557,27 +1569,39 @@ def get_undercloud_auth(): # Openstack Client helpers -def get_keystone_ip(): +def get_keystone_ip(model_name=None): """Return the IP address to use when communicating with keystone api. + :param model_name: Name of model to query. + :type model_name: str :returns: IP address :rtype: str """ - if get_application_config_option('keystone', 'vip'): - return get_application_config_option('keystone', 'vip') - unit = model.get_units('keystone')[0] + vip_option = get_application_config_option( + 'keystone', + 'vip', + model_name=model_name) + if vip_option: + return vip_option + unit = model.get_units('keystone', model_name=model_name)[0] return unit.public_address -def get_keystone_api_version(): +def get_keystone_api_version(model_name=None): """Return the keystone api version. + :param model_name: Name of model to query. + :type model_name: str :returns: Keystone's api version :rtype: int """ - os_version = get_current_os_versions('keystone')['keystone'] - api_version = get_application_config_option('keystone', - 'preferred-api-version') + os_version = get_current_os_versions( + 'keystone', + model_name=model_name)['keystone'] + api_version = get_application_config_option( + 'keystone', + 'preferred-api-version', + model_name=model_name) if os_version >= 'queens': api_version = 3 elif api_version is None: @@ -1586,15 +1610,21 @@ def get_keystone_api_version(): return int(api_version) -def get_overcloud_auth(address=None): +def get_overcloud_auth(address=None, model_name=None): """Get overcloud OpenStack authentication from the environment. + :param model_name: Name of model to query. + :type model_name: str :returns: Dictionary of authentication settings :rtype: dict """ tls_rid = model.get_relation_id('keystone', 'vault', + model_name=model_name, remote_interface_name='certificates') - ssl_config = get_application_config_option('keystone', 'ssl_cert') + ssl_config = get_application_config_option( + 'keystone', + 'ssl_cert', + model_name=model_name) if tls_rid or ssl_config: transport = 'https' port = 35357 @@ -1603,11 +1633,14 @@ def get_overcloud_auth(address=None): port = 5000 if not address: - address = get_keystone_ip() + address = get_keystone_ip(model_name=model_name) - password = juju_utils.leader_get('keystone', 'admin_passwd') + password = juju_utils.leader_get( + 'keystone', + 'admin_passwd', + model_name=model_name) - if get_keystone_api_version() == 2: + if get_keystone_api_version(model_name=model_name) == 2: # V2 Explicitly, or None when charm does not possess the config key logging.info('Using keystone API V2 for overcloud auth') auth_settings = { @@ -1633,7 +1666,7 @@ def get_overcloud_auth(address=None): 'API_VERSION': 3, } if tls_rid: - unit = model.get_first_unit_name('keystone') + unit = model.get_first_unit_name('keystone', model_name=model_name) model.scp_from_unit( unit, KEYSTONE_REMOTE_CACERT,