From 827d81fc12201e525d6d57d2908ee9be71f02649 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Thu, 20 Oct 2022 11:18:04 +0100 Subject: [PATCH] Allow tempest to run in minimal mode (#956) Add test class that runs tempest in miminal mode. This is useful for testing that the OpenStack apis are responding but there are components not configured like an external network. In addtion some missing doc strings were added and support for neutron tempest configuration pre ussuri removed as there is a branch for that. --- zaza/openstack/charm_tests/tempest/tests.py | 17 +++ zaza/openstack/charm_tests/tempest/utils.py | 113 ++++++++++++-------- 2 files changed, 88 insertions(+), 42 deletions(-) diff --git a/zaza/openstack/charm_tests/tempest/tests.py b/zaza/openstack/charm_tests/tempest/tests.py index b7fc859..da8078b 100644 --- a/zaza/openstack/charm_tests/tempest/tests.py +++ b/zaza/openstack/charm_tests/tempest/tests.py @@ -122,6 +122,23 @@ class TempestTestWithKeystoneV3(TempestTestBase): return super().run() +class TempestTestWithKeystoneMinimal(TempestTestBase): + """Tempest test class to validate an OpenStack setup with Keystone V2.""" + + def run(self): + """Run tempest tests as specified in tests/tests.yaml. + + Allow test to run even if some components are missing (like + external network setup). + See TempestTestBase.run() for the available test options. + + :returns: Status of tempest run + :rtype: bool + """ + tempest_utils.render_tempest_config_keystone_v3(minimal=True) + return super().run() + + class TempestTest(TempestTestBase): """Tempest test class. diff --git a/zaza/openstack/charm_tests/tempest/utils.py b/zaza/openstack/charm_tests/tempest/utils.py index fa6c051..7463e11 100644 --- a/zaza/openstack/charm_tests/tempest/utils.py +++ b/zaza/openstack/charm_tests/tempest/utils.py @@ -21,6 +21,8 @@ import shutil import subprocess import urllib.parse +from neutronclient.common import exceptions as neutronexceptions + import zaza.model as model import zaza.utilities.deployment_env as deployment_env import zaza.openstack.utilities.juju as juju_utils @@ -51,13 +53,18 @@ def render_tempest_config_keystone_v2(): _setup_tempest('tempest_v2.j2', 'accounts.j2') -def render_tempest_config_keystone_v3(): +def render_tempest_config_keystone_v3(minimal=False): """Render tempest config for Keystone V3 API. + :param minimal: Run in minimal mode eg ignore missing setup + :type minimal: bool :returns: None :rtype: None """ - _setup_tempest('tempest_v3.j2', 'accounts.j2') + _setup_tempest( + 'tempest_v3.j2', + 'accounts.j2', + minimal=minimal) def get_workspace(): @@ -105,20 +112,22 @@ def _init_workspace(workspace_path): pass -def _setup_tempest(tempest_template, accounts_template): +def _setup_tempest(tempest_template, accounts_template, minimal=False): """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 + :param minimal: Run in minimal mode eg ignore missing setup + :type minimal: bool :returns: None :rtype: None """ workspace_name, workspace_path = get_workspace() destroy_workspace(workspace_name, workspace_path) _init_workspace(workspace_path) - context = _get_tempest_context(workspace_path) + context = _get_tempest_context(workspace_path, missing_fatal=not minimal) _render_tempest_config( os.path.join(workspace_path, 'etc/tempest.conf'), context, @@ -129,9 +138,13 @@ def _setup_tempest(tempest_template, accounts_template): accounts_template) -def _get_tempest_context(workspace_path): +def _get_tempest_context(workspace_path, missing_fatal=True): """Generate the tempest config context. + :param workspace_path: path to workspace directory + :type workspace_path: str + :param missing_fatal: Raise an exception if a resource is missing + :type missing_fatal: bool :returns: Context dictionary :rtype: dict """ @@ -153,8 +166,14 @@ def _get_tempest_context(workspace_path): _add_application_ips(ctxt) for svc_name, ctxt_func in ctxt_funcs.items(): if svc_name in ctxt['enabled_services']: - ctxt_func(ctxt, keystone_session) - _add_environment_var_config(ctxt, ctxt['enabled_services']) + ctxt_func( + ctxt, + keystone_session, + missing_fatal=missing_fatal) + _add_environment_var_config( + ctxt, + ctxt['enabled_services'], + missing_fatal=missing_fatal) _add_auth_config(ctxt) if 'octavia' in ctxt['enabled_services']: _add_octavia_config(ctxt) @@ -194,13 +213,15 @@ def _add_application_ips(ctxt): ctxt['ncc'] = juju_utils.get_application_ip('nova-cloud-controller') -def _add_nova_config(ctxt, keystone_session): +def _add_nova_config(ctxt, keystone_session, missing_fatal=True): """Add nova config to context. :param ctxt: Context dictionary :type ctxt: dict :param keystone_session: keystoneauth1.session.Session object :type: keystoneauth1.session.Session + :param missing_fatal: Raise an exception if a resource is missing + :type missing_fatal: bool :returns: None :rtype: None """ @@ -213,54 +234,52 @@ def _add_nova_config(ctxt, keystone_session): ctxt['flavor_ref_alt'] = flavor.id -def _add_neutron_config(ctxt, keystone_session): +def _add_neutron_config(ctxt, keystone_session, missing_fatal=True): """Add neutron config to context. :param ctxt: Context dictionary :type ctxt: dict :param keystone_session: keystoneauth1.session.Session object :type: keystoneauth1.session.Session + :param missing_fatal: Raise an exception if a resource is missing + :type missing_fatal: bool :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' + try: + 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'] + except neutronexceptions.NotFound: + if missing_fatal: + raise + 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 -def _add_glance_config(ctxt, keystone_session): +def _add_glance_config(ctxt, keystone_session, missing_fatal=True): """Add glance config to context. :param ctxt: Context dictionary :type ctxt: dict :param keystone_session: keystoneauth1.session.Session object :type: keystoneauth1.session.Session + :param missing_fatal: Raise an exception if a resource is missing + :type missing_fatal: bool :returns: None :rtype: None """ @@ -276,13 +295,15 @@ def _add_glance_config(ctxt, keystone_session): ctxt['image_alt_id'] = image_alt[0].id -def _add_cinder_config(ctxt, keystone_session): +def _add_cinder_config(ctxt, keystone_session, missing_fatal=True): """Add cinder config to context. :param ctxt: Context dictionary :type ctxt: dict :param keystone_session: keystoneauth1.session.Session object :type: keystoneauth1.session.Session + :param missing_fatal: Raise an exception if a resource is missing + :type missing_fatal: bool :returns: None :rtype: None """ @@ -297,13 +318,15 @@ def _add_cinder_config(ctxt, keystone_session): break -def _add_keystone_config(ctxt, keystone_session): +def _add_keystone_config(ctxt, keystone_session, missing_fatal=True): """Add keystone config to context. :param ctxt: Context dictionary :type ctxt: dict :param keystone_session: keystoneauth1.session.Session object :type: keystoneauth1.session.Session + :param missing_fatal: Raise an exception if a resource is missing + :type missing_fatal: bool :returns: None :rtype: None """ @@ -313,11 +336,13 @@ def _add_keystone_config(ctxt, keystone_session): ctxt['default_domain_id'] = domain.id -def _add_octavia_config(ctxt): +def _add_octavia_config(ctxt, missing_fatal=True): """Add octavia config to context. :param ctxt: Context dictionary :type ctxt: dict + :param missing_fatal: Raise an exception if a resource is missing + :type missing_fatal: bool :returns: None :rtype: None :raises: subprocess.CalledProcessError @@ -334,11 +359,15 @@ def _add_octavia_config(ctxt): ]) -def _add_environment_var_config(ctxt, services): +def _add_environment_var_config(ctxt, services, missing_fatal=True): """Add environment variable config to context. :param ctxt: Context dictionary :type ctxt: dict + :param services: List of services + :type services: List[str] + :param missing_fatal: Raise an exception if a resource is missing + :type missing_fatal: bool :returns: None :rtype: None """ @@ -353,7 +382,7 @@ def _add_environment_var_config(ctxt, services): else: if var not in IGNORABLE_VARS: missing_vars.append(var) - if missing_vars: + if missing_vars and missing_fatal: raise ValueError( ("Environment variables [{}] must all be set to run this" " test").format(', '.join(missing_vars)))