diff --git a/zaza/openstack/charm_tests/ceilometer/setup.py b/zaza/openstack/charm_tests/ceilometer/setup.py index c966077..0ff1fb5 100644 --- a/zaza/openstack/charm_tests/ceilometer/setup.py +++ b/zaza/openstack/charm_tests/ceilometer/setup.py @@ -28,11 +28,11 @@ def basic_setup(): tests. """ current_release = openstack_utils.get_os_release() - xenial_pike = openstack_utils.get_os_release('xenial_pike') + xenial_ocata = openstack_utils.get_os_release('xenial_ocata') - if current_release < xenial_pike: + if current_release < xenial_ocata: logging.info( - 'Skipping ceilometer-upgrade as it is not supported before Pike') + 'Skipping ceilometer-upgrade as it is not supported before ocata') return logging.debug('Checking ceilometer-upgrade') diff --git a/zaza/openstack/charm_tests/glance/setup.py b/zaza/openstack/charm_tests/glance/setup.py index c992917..8c7bd3e 100644 --- a/zaza/openstack/charm_tests/glance/setup.py +++ b/zaza/openstack/charm_tests/glance/setup.py @@ -18,6 +18,7 @@ import logging import zaza.openstack.utilities.openstack as openstack_utils CIRROS_IMAGE_NAME = "cirros" +CIRROS_ALT_IMAGE_NAME = "cirros_alt" LTS_RELEASE = "bionic" LTS_IMAGE_NAME = "bionic" @@ -77,6 +78,18 @@ def add_cirros_image(glance_client=None, image_name=None): image_name=image_name) +def add_cirros_alt_image(glance_client=None, image_name=None): + """Add alt cirros image to the current deployment. + + :param glance: Authenticated glanceclient + :type glance: glanceclient.Client + :param image_name: Label for the image in glance + :type image_name: str + """ + image_name = image_name or CIRROS_ALT_IMAGE_NAME + add_cirros_image(glance_client, image_name) + + def add_lts_image(glance_client=None, image_name=None, release=None): """Add an Ubuntu LTS image to the current deployment. diff --git a/zaza/openstack/charm_tests/keystone/__init__.py b/zaza/openstack/charm_tests/keystone/__init__.py index 6c4cca6..4d2e829 100644 --- a/zaza/openstack/charm_tests/keystone/__init__.py +++ b/zaza/openstack/charm_tests/keystone/__init__.py @@ -26,6 +26,8 @@ DEMO_ADMIN_USER_PASSWORD = 'password' DEMO_USER = 'demo' DEMO_PASSWORD = 'password' +TEMPEST_ROLES = ['member', 'ResellerAdmin'] + class BaseKeystoneTest(test_utils.OpenStackBaseTest): """Base for Keystone charm tests.""" diff --git a/zaza/openstack/charm_tests/keystone/setup.py b/zaza/openstack/charm_tests/keystone/setup.py index 688e3cd..6dbb7c1 100644 --- a/zaza/openstack/charm_tests/keystone/setup.py +++ b/zaza/openstack/charm_tests/keystone/setup.py @@ -14,6 +14,8 @@ """Code for setting up keystone.""" +import keystoneauth1 + import zaza.openstack.utilities.openstack as openstack_utils from zaza.openstack.charm_tests.keystone import ( BaseKeystoneTest, @@ -24,6 +26,7 @@ from zaza.openstack.charm_tests.keystone import ( DEMO_ADMIN_USER_PASSWORD, DEMO_USER, DEMO_PASSWORD, + TEMPEST_ROLES, ) @@ -115,3 +118,30 @@ def add_demo_user(): else: # create only V3 user _v3() + + +def _add_additional_roles(roles): + """Add additional roles to this deployment. + + :param ctxt: roles + :type ctxt: list + :returns: None + :rtype: None + """ + keystone_session = openstack_utils.get_overcloud_keystone_session() + keystone_client = openstack_utils.get_keystone_session_client( + keystone_session) + for role_name in roles: + try: + keystone_client.roles.create(role_name) + except keystoneauth1.exceptions.http.Conflict: + pass + + +def add_tempest_roles(): + """Add tempest roles to this deployment. + + :returns: None + :rtype: None + """ + _add_additional_roles(TEMPEST_ROLES) diff --git a/zaza/openstack/charm_tests/nova/utils.py b/zaza/openstack/charm_tests/nova/utils.py index 974edd7..b16eef4 100644 --- a/zaza/openstack/charm_tests/nova/utils.py +++ b/zaza/openstack/charm_tests/nova/utils.py @@ -35,5 +35,15 @@ FLAVORS = { 'ram': 8192, 'disk': 40, 'vcpus': 4}, + 'm1.tempest': { + 'flavorid': 6, + 'ram': 256, + 'disk': 1, + 'vcpus': 1}, + 'm2.tempest': { + 'flavorid': 7, + 'ram': 512, + 'disk': 1, + 'vcpus': 1}, } KEYPAIR_NAME = 'zaza' diff --git a/zaza/openstack/charm_tests/tempest/__init__.py b/zaza/openstack/charm_tests/tempest/__init__.py new file mode 100644 index 0000000..cf4c994 --- /dev/null +++ b/zaza/openstack/charm_tests/tempest/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2020 Canonical Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Collection of code for setting up and running tempest.""" diff --git a/zaza/openstack/charm_tests/tempest/setup.py b/zaza/openstack/charm_tests/tempest/setup.py new file mode 100644 index 0000000..4266ddc --- /dev/null +++ b/zaza/openstack/charm_tests/tempest/setup.py @@ -0,0 +1,291 @@ +# Copyright 2020 Canonical Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Code for configuring and initializing tempest.""" + +import urllib.parse +import os +import subprocess + +import zaza.utilities.deployment_env as deployment_env +import zaza.openstack.utilities.juju as juju_utils +import zaza.openstack.utilities.openstack as openstack_utils +import zaza.openstack.charm_tests.glance.setup as glance_setup +import zaza.openstack.charm_tests.tempest.templates.tempest_v2 as tempest_v2 +import zaza.openstack.charm_tests.tempest.templates.tempest_v3 as tempest_v3 +import zaza.openstack.charm_tests.tempest.templates.accounts as accounts + +SETUP_ENV_VARS = [ + 'TEST_GATEWAY', + 'TEST_CIDR_EXT', + 'TEST_FIP_RANGE', + 'TEST_NAMESERVER', + 'TEST_CIDR_PRIV', + 'TEST_SWIFT_IP', +] +TEMPEST_FLAVOR_NAME = 'm1.tempest' +TEMPEST_ALT_FLAVOR_NAME = 'm2.tempest' + + +def add_application_ips(ctxt): + """Add application access IPs to context. + + :param ctxt: Context dictionary + :type ctxt: dict + :returns: None + :rtype: None + """ + ctxt['keystone'] = juju_utils.get_application_ip('keystone') + ctxt['dashboard'] = juju_utils.get_application_ip('openstack-dashboard') + ctxt['ncc'] = juju_utils.get_application_ip('nova-cloud-controller') + + +def add_nova_config(ctxt, keystone_session): + """Add nova config to context. + + :param ctxt: Context dictionary + :type ctxt: dict + :param keystone_session: keystoneauth1.session.Session object + :type: keystoneauth1.session.Session + :returns: None + :rtype: None + """ + nova_client = openstack_utils.get_nova_session_client( + keystone_session) + for flavor in nova_client.flavors.list(): + if flavor.name == TEMPEST_FLAVOR_NAME: + ctxt['flavor_ref'] = flavor.id + if flavor.name == TEMPEST_ALT_FLAVOR_NAME: + ctxt['flavor_ref_alt'] = flavor.id + + +def add_neutron_config(ctxt, keystone_session): + """Add neutron config to context. + + :param ctxt: Context dictionary + :type ctxt: dict + :param keystone_session: keystoneauth1.session.Session object + :type: keystoneauth1.session.Session + :returns: None + :rtype: None + """ + current_release = openstack_utils.get_os_release() + focal_ussuri = openstack_utils.get_os_release('focal_ussuri') + neutron_client = openstack_utils.get_neutron_session_client( + keystone_session) + net = neutron_client.find_resource("network", "ext_net") + ctxt['ext_net'] = net['id'] + router = neutron_client.find_resource("router", "provider-router") + ctxt['provider_router_id'] = router['id'] + # For focal+ with OVN, we use the same settings as upstream gate. + # This is because the l3_agent_scheduler extension is only + # applicable for OVN when conventional layer-3 agent enabled: + # https://docs.openstack.org/networking-ovn/2.0.1/features.html + # This enables test_list_show_extensions to run successfully. + if current_release >= focal_ussuri: + extensions = ('address-scope,agent,allowed-address-pairs,' + 'auto-allocated-topology,availability_zone,' + 'binding,default-subnetpools,external-net,' + 'extra_dhcp_opt,multi-provider,net-mtu,' + 'network_availability_zone,network-ip-availability,' + 'port-security,provider,quotas,rbac-address-scope,' + 'rbac-policies,standard-attr-revisions,security-group,' + 'standard-attr-description,subnet_allocation,' + 'standard-attr-tag,standard-attr-timestamp,trunk,' + 'quota_details,router,extraroute,ext-gw-mode,' + 'fip-port-details,pagination,sorting,project-id,' + 'dns-integration,qos') + ctxt['neutron_api_extensions'] = extensions + else: + ctxt['neutron_api_extensions'] = 'all' + + +def add_glance_config(ctxt, keystone_session): + """Add glance config to context. + + :param ctxt: Context dictionary + :type ctxt: dict + :param keystone_session: keystoneauth1.session.Session object + :type: keystoneauth1.session.Session + :returns: None + :rtype: None + """ + glance_client = openstack_utils.get_glance_session_client( + keystone_session) + image = openstack_utils.get_images_by_name( + glance_client, glance_setup.CIRROS_IMAGE_NAME) + image_alt = openstack_utils.get_images_by_name( + glance_client, glance_setup.CIRROS_ALT_IMAGE_NAME) + if image: + ctxt['image_id'] = image[0].id + if image_alt: + ctxt['image_alt_id'] = image_alt[0].id + + +def add_cinder_config(ctxt, keystone_session): + """Add cinder config to context. + + :param ctxt: Context dictionary + :type ctxt: dict + :param keystone_session: keystoneauth1.session.Session object + :type: keystoneauth1.session.Session + :returns: None + :rtype: None + """ + volume_types = ['volumev2', 'volumev3'] + keystone_client = openstack_utils.get_keystone_session_client( + keystone_session) + for volume_type in volume_types: + service = keystone_client.services.list(type=volume_type) + if service: + ctxt['catalog_type'] = volume_type + break + + +def add_keystone_config(ctxt, keystone_session): + """Add keystone config to context. + + :param ctxt: Context dictionary + :type ctxt: dict + :param keystone_session: keystoneauth1.session.Session object + :type: keystoneauth1.session.Session + :returns: None + :rtype: None + """ + keystone_client = openstack_utils.get_keystone_session_client( + keystone_session) + domain = keystone_client.domains.find(name="admin_domain") + ctxt['default_domain_id'] = domain.id + + +def add_environment_var_config(ctxt): + """Add environment variable config to context. + + :param ctxt: Context dictionary + :type ctxt: dict + :returns: None + :rtype: None + """ + deploy_env = deployment_env.get_deployment_context() + for var in SETUP_ENV_VARS: + value = deploy_env.get(var) + if value: + ctxt[var.lower()] = value + else: + raise ValueError( + ("Environment variables {} must all be set to run this" + " test").format(', '.join(SETUP_ENV_VARS))) + + +def add_auth_config(ctxt): + """Add authorization config to context. + + :param ctxt: Context dictionary + :type ctxt: dict + :returns: None + :rtype: None + """ + overcloud_auth = openstack_utils.get_overcloud_auth() + ctxt['proto'] = urllib.parse.urlparse(overcloud_auth['OS_AUTH_URL']).scheme + ctxt['admin_username'] = overcloud_auth['OS_USERNAME'] + ctxt['admin_password'] = overcloud_auth['OS_PASSWORD'] + if overcloud_auth['API_VERSION'] == 3: + ctxt['admin_project_name'] = overcloud_auth['OS_PROJECT_NAME'] + ctxt['admin_domain_name'] = overcloud_auth['OS_DOMAIN_NAME'] + ctxt['default_credentials_domain_name'] = ( + overcloud_auth['OS_PROJECT_DOMAIN_NAME']) + + +def get_tempest_context(): + """Generate the tempest config context. + + :returns: Context dictionary + :rtype: dict + """ + keystone_session = openstack_utils.get_overcloud_keystone_session() + ctxt = {} + add_application_ips(ctxt) + add_nova_config(ctxt, keystone_session) + add_neutron_config(ctxt, keystone_session) + add_glance_config(ctxt, keystone_session) + add_cinder_config(ctxt, keystone_session) + add_keystone_config(ctxt, keystone_session) + add_environment_var_config(ctxt) + add_auth_config(ctxt) + return ctxt + + +def render_tempest_config(target_file, ctxt, template): + """Render tempest config for specified config file and template. + + :param target_file: Name of file to render config to + :type target_file: str + :param ctxt: Context dictionary + :type ctxt: dict + :param template: Template module + :type template: module + :returns: None + :rtype: None + """ + # TODO: switch to jinja2 and generate config based on available services + with open(target_file, 'w') as f: + f.write(template.file_contents.format(**ctxt)) + + +def setup_tempest(tempest_template, accounts_template): + """Initialize tempest and render tempest config. + + :param tempest_template: tempest.conf template + :type tempest_template: module + :param accounts_template: accounts.yaml template + :type accounts_template: module + :returns: None + :rtype: None + """ + if os.path.isdir('tempest-workspace'): + try: + subprocess.check_call(['tempest', 'workspace', 'remove', '--rmdir', + '--name', 'tempest-workspace']) + except subprocess.CalledProcessError: + pass + try: + subprocess.check_call(['tempest', 'init', 'tempest-workspace']) + except subprocess.CalledProcessError: + pass + render_tempest_config( + 'tempest-workspace/etc/tempest.conf', + get_tempest_context(), + tempest_template) + render_tempest_config( + 'tempest-workspace/etc/accounts.yaml', + get_tempest_context(), + accounts_template) + + +def render_tempest_config_keystone_v2(): + """Render tempest config for Keystone V2 API. + + :returns: None + :rtype: None + """ + setup_tempest(tempest_v2, accounts) + + +def render_tempest_config_keystone_v3(): + """Render tempest config for Keystone V3 API. + + :returns: None + :rtype: None + """ + setup_tempest(tempest_v3, accounts) diff --git a/zaza/openstack/charm_tests/tempest/templates/__init__.py b/zaza/openstack/charm_tests/tempest/templates/__init__.py new file mode 100644 index 0000000..9269e37 --- /dev/null +++ b/zaza/openstack/charm_tests/tempest/templates/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2020 Canonical Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Collection of templates for tempest.""" diff --git a/zaza/openstack/charm_tests/tempest/templates/accounts.py b/zaza/openstack/charm_tests/tempest/templates/accounts.py new file mode 100644 index 0000000..0c5cf5a --- /dev/null +++ b/zaza/openstack/charm_tests/tempest/templates/accounts.py @@ -0,0 +1,9 @@ +# flake8: noqa +file_contents = """ +- username: 'demo' + tenant_name: 'demo' + password: 'pass' +- username: 'alt_demo' + tenant_name: 'alt_demo' + password: 'secret' +""" diff --git a/zaza/openstack/charm_tests/tempest/templates/tempest_v2.py b/zaza/openstack/charm_tests/tempest/templates/tempest_v2.py new file mode 100644 index 0000000..8b0f939 --- /dev/null +++ b/zaza/openstack/charm_tests/tempest/templates/tempest_v2.py @@ -0,0 +1,96 @@ +# flake8: noqa +file_contents = """ +[DEFAULT] +debug = false +use_stderr = false +log_file = tempest.log + +[auth] +test_accounts_file = accounts.yaml +default_credentials_domain_name = Default +admin_username = {admin_username} +admin_project_name = admin +admin_password = {admin_password} +admin_domain_name = Default + +[compute] +image_ref = {image_id} +image_ref_alt = {image_alt_id} +flavor_ref = {flavor_ref} +flavor_ref_alt = {flavor_ref_alt} +region = RegionOne +min_compute_nodes = 3 + +# TODO: review this as its release specific +# min_microversion = 2.2 +# max_microversion = latest + +[compute-feature-enabled] +console_output = true +resize = true +live_migration = true +block_migration_for_live_migration = true +attach_encrypted_volume = false + +[identity] +uri = {proto}://{keystone}:5000/v2.0 +auth_version = v2 +admin_role = Admin +region = RegionOne +disable_ssl_certificate_validation = true + +[identity-feature-enabled] +api_v2 = true +api_v3 = false + +[image] +http_image = http://{test_swift_ip}:80/swift/v1/images/cirros-0.3.4-x86_64-uec.tar.gz + +[network] +project_network_cidr = {test_cidr_priv} +public_network_id = {ext_net} +dns_servers = {test_nameserver} +project_networks_reachable = false + +[network-feature-enabled] +ipv6 = false + +[orchestration] +stack_owner_role = Admin +instance_type = m1.small +keypair_name = testkey + +[oslo_concurrency] +lock_path = /tmp + +[scenario] +img_dir = /home/ubuntu/images +img_file = cirros-0.3.4-x86_64-disk.img +img_container_format = bare +img_disk_format = qcow2 + +[validation] +run_validation = true +image_ssh_user = cirros + +[service_available] +ceilometer = true +cinder = true +glance = true +heat = true +horizon = true +ironic = false +neutron = true +nova = true +sahara = false +swift = true +trove = false +zaqar = false + +[volume] +backend_names = cinder-ceph +storage_protocol = ceph +catalog_type = {catalog_type} + +[volume-feature-enabled] +backup = false""" diff --git a/zaza/openstack/charm_tests/tempest/templates/tempest_v3.py b/zaza/openstack/charm_tests/tempest/templates/tempest_v3.py new file mode 100644 index 0000000..5140deb --- /dev/null +++ b/zaza/openstack/charm_tests/tempest/templates/tempest_v3.py @@ -0,0 +1,100 @@ +# flake8: noqa +file_contents = """ +[DEFAULT] +debug = false +use_stderr = false +log_file = tempest.log + +[auth] +test_accounts_file = accounts.yaml +default_credentials_domain_name = {default_credentials_domain_name} +admin_username = {admin_username} +admin_project_name = {admin_project_name} +admin_password = {admin_password} +admin_domain_name = {admin_domain_name} + +[compute] +image_ref = {image_id} +image_ref_alt = {image_alt_id} +flavor_ref = {flavor_ref} +flavor_ref_alt = {flavor_ref_alt} +min_compute_nodes = 3 + +# TODO: review this as its release specific +# min_microversion = 2.2 +# max_microversion = latest + +[compute-feature-enabled] +console_output = true +resize = true +live_migration = true +block_migration_for_live_migration = true +attach_encrypted_volume = false + +[identity] +uri = {proto}://{keystone}:5000/v2.0 +uri_v3 = {proto}://{keystone}:5000/v3 +auth_version = v3 +admin_role = Admin +region = RegionOne +default_domain_id = {default_domain_id} +admin_domain_scope = true +disable_ssl_certificate_validation = true + +[identity-feature-enabled] +api_v2 = false +api_v3 = true + +[image] +http_image = http://{test_swift_ip}:80/swift/v1/images/cirros-0.3.4-x86_64-uec.tar.gz + +[network] +project_network_cidr = {test_cidr_priv} +public_network_id = {ext_net} +dns_servers = {test_nameserver} +project_networks_reachable = false +floating_network_name = {ext_net} + +[network-feature-enabled] +ipv6 = false +api_extensions = {neutron_api_extensions} + +[orchestration] +stack_owner_role = Admin +instance_type = m1.small +keypair_name = testkey + +[oslo_concurrency] +lock_path = /tmp + +[scenario] +img_dir = /home/ubuntu/images +img_file = cirros-0.3.4-x86_64-disk.img +img_container_format = bare +img_disk_format = qcow2 + +[validation] +run_validation = true +image_ssh_user = cirros + +[service_available] +ceilometer = true +cinder = true +glance = true +heat = true +horizon = true +ironic = false +neutron = true +nova = true +sahara = false +swift = true +trove = false +zaqar = false + +[volume] +backend_names = cinder-ceph +storage_protocol = ceph +catalog_type = {catalog_type} + +[volume-feature-enabled] +backup = false""" diff --git a/zaza/openstack/charm_tests/tempest/tests.py b/zaza/openstack/charm_tests/tempest/tests.py new file mode 100644 index 0000000..d726930 --- /dev/null +++ b/zaza/openstack/charm_tests/tempest/tests.py @@ -0,0 +1,78 @@ +# Copyright 2020 Canonical Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Code for running tempest tests.""" + +import os +import subprocess + +import zaza +import zaza.charm_lifecycle.utils +import zaza.charm_lifecycle.test +import tempfile + + +class TempestTest(): + """Tempest test class.""" + + test_runner = zaza.charm_lifecycle.test.DIRECT + + def run(self): + """Run tempest tests as specified in tests/tests.yaml. + + Test keys are parsed from ['tests_options']['tempest']['model'], where + valid test keys are: smoke (bool), whitelist (list of tests), blacklist + (list of tests), and regex (list of regex's). + + :returns: Status of tempest run + :rtype: bool + """ + charm_config = zaza.charm_lifecycle.utils.get_charm_config() + tempest_options = ['tempest', 'run', '--workspace', + 'tempest-workspace', '--config', + 'tempest-workspace/etc/tempest.conf'] + for model_alias in zaza.model.get_juju_model_aliases().keys(): + tempest_test_key = model_alias + if model_alias == zaza.charm_lifecycle.utils.DEFAULT_MODEL_ALIAS: + tempest_test_key = 'default' + config = charm_config['tests_options']['tempest'][tempest_test_key] + if config.get('smoke'): + tempest_options.extend(['--smoke']) + if config.get('regex'): + tempest_options.extend( + ['--regex', + ' '.join([reg for reg in config.get('regex')])]) + if config.get('black-regex'): + tempest_options.extend( + ['--black-regex', + ' '.join([reg for reg in config.get('black-regex')])]) + with tempfile.TemporaryDirectory() as tmpdirname: + if config.get('whitelist'): + white_file = os.path.join(tmpdirname, 'white.cfg') + with open(white_file, 'w') as f: + f.write('\n'.join(config.get('whitelist'))) + f.write('\n') + tempest_options.extend(['--whitelist-file', white_file]) + if config.get('blacklist'): + black_file = os.path.join(tmpdirname, 'black.cfg') + with open(black_file, 'w') as f: + f.write('\n'.join(config.get('blacklist'))) + f.write('\n') + tempest_options.extend(['--blacklist-file', black_file]) + print(tempest_options) + try: + subprocess.check_call(tempest_options) + except subprocess.CalledProcessError: + return False + return True diff --git a/zaza/openstack/utilities/juju.py b/zaza/openstack/utilities/juju.py index b3e54bc..3bdf4b8 100644 --- a/zaza/openstack/utilities/juju.py +++ b/zaza/openstack/utilities/juju.py @@ -49,6 +49,27 @@ def get_application_status(application=None, unit=None, model_name=None): return status +def get_application_ip(application): + """Get the application's IP address. + + :param application: Application name + :type application: str + :returns: Application's IP address + :rtype: str + """ + try: + app_config = model.get_application_config(application) + except KeyError: + return '' + vip = app_config.get("vip").get("value") + if vip: + ip = vip + else: + unit = model.get_units(application)[0] + ip = unit.public_address + return ip + + def get_cloud_configs(cloud=None): """Get cloud configuration from local clouds.yaml.