From 8e55f588c8b4efec3889cbf6a3a6bf590ca98ef5 Mon Sep 17 00:00:00 2001 From: Felipe Reyes Date: Sat, 14 Mar 2020 22:04:48 -0300 Subject: [PATCH 01/64] mysql: add check for seeded file in pxc Verify the seeded file is present with the expected content since it will be used by *-relation-changed hook to determine if it can process requests of related units. Related-Bug: #1868326 --- zaza/openstack/charm_tests/mysql/tests.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/zaza/openstack/charm_tests/mysql/tests.py b/zaza/openstack/charm_tests/mysql/tests.py index a800712..f674e3f 100644 --- a/zaza/openstack/charm_tests/mysql/tests.py +++ b/zaza/openstack/charm_tests/mysql/tests.py @@ -29,6 +29,9 @@ import zaza.openstack.utilities.openstack as openstack_utils import zaza.openstack.utilities.generic as generic_utils +PXC_SEEDED_FILE = "/var/lib/percona-xtradb-cluster/seeded" + + class MySQLBaseTest(test_utils.OpenStackBaseTest): """Base for mysql charm tests.""" @@ -202,6 +205,10 @@ class PerconaClusterCharmTests(MySQLCommonTests, PerconaClusterBaseTest): " (wanted=%s, cluster_size=%s)" % (self.units, cluster_size)) assert cluster_size >= self.units, msg + logging.info("Ensuring PXC seeded file is present") + zaza.model.block_until_file_has_contents(self.application, + PXC_SEEDED_FILE, "done") + def test_130_change_root_password(self): """Change root password. From 135e2dd8060bcda5cb1a2e44082192ff7aa50db0 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Mon, 4 May 2020 16:42:43 +0200 Subject: [PATCH 02/64] Re-enable neutron/test_800_ovs_bridges_are_managed_by_us --- zaza/openstack/charm_tests/neutron/tests.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/zaza/openstack/charm_tests/neutron/tests.py b/zaza/openstack/charm_tests/neutron/tests.py index d94b47f..cb90b74 100644 --- a/zaza/openstack/charm_tests/neutron/tests.py +++ b/zaza/openstack/charm_tests/neutron/tests.py @@ -144,16 +144,12 @@ class NeutronGatewayTest(NeutronPluginApiSharedTests): self.assertIn('qos', ovs_agent['configurations']['extensions']) - @unittest.expectedFailure def test_800_ovs_bridges_are_managed_by_us(self): """Checking OVS bridges' external-id. OVS bridges created by us should be marked as managed by us in their external-id. See http://docs.openvswitch.org/en/latest/topics/integration/ - - NOTE(lourot): this test is expected to fail as long as this feature - hasn't landed yet: https://review.opendev.org/717074 """ for unit in zaza.model.get_units(self._APP_NAME, model_name=self.model_name): From 06ed9eecf02aaeeb4166d3b7b042e030beaa2f0b Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Tue, 16 Jun 2020 17:01:05 -0500 Subject: [PATCH 03/64] Add gnocchi s3 test --- zaza/openstack/charm_tests/gnocchi/tests.py | 42 ++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index f789cff..269851f 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -16,11 +16,14 @@ """Encapsulate Gnocchi testing.""" +import boto3 import logging - from gnocchiclient.v1 import client as gnocchi_client + +import zaza.model as model import zaza.openstack.charm_tests.test_utils as test_utils import zaza.openstack.utilities.openstack as openstack_utils +from zaza.openstack.charm_tests.swift.tests import S3APITest class GnocchiTest(test_utils.OpenStackBaseTest): @@ -58,3 +61,40 @@ class GnocchiTest(test_utils.OpenStackBaseTest): """ with self.pause_resume(self.services): logging.info("Testing pause and resume") + + +class GnocchiS3Test(test_utils.OpenStackBaseTest): + """Tests for S3 storage backend""" + + swift = S3APITest + kwargs = { + 'region_name': swift.s3_region, + 'aws_access_key_id': swift.ec2_creds.access, + 'aws_secret_access_key': swift.ec2_creds.secret, + 'endpoint_url': swift.s3_endpoint, + 'verify': swift.cacert, + } + + def update_gnocchi_config_for_s3(self): + """Update Gnocchi with the correct values for the S3 backend""" + logging.debug('Changing charm setting to connect to S3') + model.set_application_config( + 'gnocchi', + {'s3-endpoint-url': self.swift.s3_endpoint, + 's3-region-name': self.swift.s3_region, + 's3-access-key-id': self.swift.ec2_creds.access, + 's3-secret-access-key': self.swift.ec2_creds.secret}, + model_name=self.model_name + ) + logging.debug( + 'Waiting for units to execute config-changed hook') + model.wait_for_agent_status(model_name=self.model_name) + logging.debug( + 'Waiting for units to reach target states') + model.wait_for_application_states( + model_name=self.model_name, + states={'gnocchi': { + 'workload-status-': 'active', + 'workload-status-message': 'Unit is ready'}} + ) + model.block_until_all_units_idle() \ No newline at end of file From e66ef5ac6c039d2687326d669726c001766da65b Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Tue, 16 Jun 2020 17:38:59 -0500 Subject: [PATCH 04/64] Get S3 info --- zaza/openstack/charm_tests/gnocchi/tests.py | 50 +++++++++++++++------ 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index 269851f..6148675 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -66,24 +66,48 @@ class GnocchiTest(test_utils.OpenStackBaseTest): class GnocchiS3Test(test_utils.OpenStackBaseTest): """Tests for S3 storage backend""" - swift = S3APITest - kwargs = { - 'region_name': swift.s3_region, - 'aws_access_key_id': swift.ec2_creds.access, - 'aws_secret_access_key': swift.ec2_creds.secret, - 'endpoint_url': swift.s3_endpoint, - 'verify': swift.cacert, - } + @classmethod + def setUpClass(cls): + """Run class setup for running tests.""" + super(GnocchiS3Test, cls).setUpClass() + + session = openstack_utils.get_overcloud_keystone_session() + ks_client = openstack_utils.get_keystone_session_client(session) + + # Get token data so we can clean our user_id and project_id + token_data = ks_client.tokens.get_token_data(session.get_token()) + project_id = token_data['token']['project']['id'] + user_id = token_data['token']['user']['id'] + + # Store URL to service providing S3 compatible API + for entry in token_data['token']['catalog']: + if entry['type'] == 's3': + for endpoint in entry['endpoints']: + if endpoint['interface'] == 'public': + cls.s3_region = endpoint['region'] + cls.s3_endpoint = endpoint['url'] + + # Create AWS compatible application credentials in Keystone + cls.ec2_creds = ks_client.ec2.create(user_id, project_id) + + # kwargs = { + # 'region_name': swift.s3_region, + # 'aws_access_key_id': swift.ec2_creds.access, + # 'aws_secret_access_key': swift.ec2_creds.secret, + # 'endpoint_url': swift.s3_endpoint, + # 'verify': swift.cacert, + # } def update_gnocchi_config_for_s3(self): """Update Gnocchi with the correct values for the S3 backend""" + logging.debug('Changing charm setting to connect to S3') model.set_application_config( 'gnocchi', - {'s3-endpoint-url': self.swift.s3_endpoint, - 's3-region-name': self.swift.s3_region, - 's3-access-key-id': self.swift.ec2_creds.access, - 's3-secret-access-key': self.swift.ec2_creds.secret}, + {'s3-endpoint-url': self.s3_endpoint, + 's3-region-name': self.s3_region, + 's3-access-key-id': self.ec2_creds.access, + 's3-secret-access-key': self.ec2_creds.secret}, model_name=self.model_name ) logging.debug( @@ -97,4 +121,4 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): 'workload-status-': 'active', 'workload-status-message': 'Unit is ready'}} ) - model.block_until_all_units_idle() \ No newline at end of file + model.block_until_all_units_idle() From 0c33bcc4a1705cbf36a9f3d369749c38f1436ba0 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Wed, 17 Jun 2020 15:17:02 -0500 Subject: [PATCH 05/64] Gnocchi S3 Tests --- zaza/openstack/charm_tests/gnocchi/tests.py | 58 +++++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index 6148675..2f1cb83 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -64,7 +64,7 @@ class GnocchiTest(test_utils.OpenStackBaseTest): class GnocchiS3Test(test_utils.OpenStackBaseTest): - """Tests for S3 storage backend""" + """Test object storage S3 API.""" @classmethod def setUpClass(cls): @@ -74,7 +74,7 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): session = openstack_utils.get_overcloud_keystone_session() ks_client = openstack_utils.get_keystone_session_client(session) - # Get token data so we can clean our user_id and project_id + # Get token data so we can glean our user_id and project_id token_data = ks_client.tokens.get_token_data(session.get_token()) project_id = token_data['token']['project']['id'] user_id = token_data['token']['user']['id'] @@ -90,18 +90,10 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): # Create AWS compatible application credentials in Keystone cls.ec2_creds = ks_client.ec2.create(user_id, project_id) - # kwargs = { - # 'region_name': swift.s3_region, - # 'aws_access_key_id': swift.ec2_creds.access, - # 'aws_secret_access_key': swift.ec2_creds.secret, - # 'endpoint_url': swift.s3_endpoint, - # 'verify': swift.cacert, - # } - - def update_gnocchi_config_for_s3(self): - """Update Gnocchi with the correct values for the S3 backend""" + def test_s3_connection_for_gnocchi(self): + """Use S3 API to list buckets.""" - logging.debug('Changing charm setting to connect to S3') + logging.info('Changing charm config to connect to swift S3 backend') model.set_application_config( 'gnocchi', {'s3-endpoint-url': self.s3_endpoint, @@ -110,15 +102,47 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): 's3-secret-access-key': self.ec2_creds.secret}, model_name=self.model_name ) - logging.debug( + logging.info( 'Waiting for units to execute config-changed hook') model.wait_for_agent_status(model_name=self.model_name) - logging.debug( + logging.info( 'Waiting for units to reach target states') model.wait_for_application_states( model_name=self.model_name, states={'gnocchi': { - 'workload-status-': 'active', - 'workload-status-message': 'Unit is ready'}} + 'workload-status-': 'active', + 'workload-status-message': 'Unit is ready' + }, + 'ceilometer': { + 'workload-status' : 'blocked', + 'workload-status-message': 'Run the ceilometer-upgrade action on the leader to initialize ceilometer and gnocchi' + } + } ) model.block_until_all_units_idle() + + def test_s3_list_gnocchi_buckets(self): + """Verify that the gnocchi buckets were created in the S3 backend """ + + kwargs = { + 'region_name': self.s3_region, + 'aws_access_key_id': self.ec2_creds.access, + 'aws_secret_access_key': self.ec2_creds.secret, + 'endpoint_url': self.s3_endpoint, + 'verify': self.cacert, + } + s3_client = boto3.client('s3', **kwargs) + s3 = boto3.resource('s3', **kwargs) + + bucket_names = ['gnocchi-measure', 'gnocchi-aggregates'] + # Validate their presence + bucket_list = s3_client.list_buckets() + logging.info(pprint.pformat(bucket_list)) + for bkt in bucket_list['Buckets']: + for gnocchi_bkt in bucket_names: + print(gnocchi_bkt) + if bkt['Name'] == gnocchi_bkt: + print('break out of 1st loop') + break + else: + AssertionError('Bucket "{}" not found'.format(gnocchi_bkt)) From a1d4a3c4ae17d7929352406c002beccd5f1753bb Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Wed, 17 Jun 2020 15:30:04 -0500 Subject: [PATCH 06/64] pep8 --- zaza/openstack/charm_tests/gnocchi/tests.py | 40 ++++++++++----------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index 2f1cb83..6f7b986 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -18,12 +18,12 @@ import boto3 import logging +import pprint from gnocchiclient.v1 import client as gnocchi_client import zaza.model as model import zaza.openstack.charm_tests.test_utils as test_utils import zaza.openstack.utilities.openstack as openstack_utils -from zaza.openstack.charm_tests.swift.tests import S3APITest class GnocchiTest(test_utils.OpenStackBaseTest): @@ -92,38 +92,39 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): def test_s3_connection_for_gnocchi(self): """Use S3 API to list buckets.""" - logging.info('Changing charm config to connect to swift S3 backend') model.set_application_config( 'gnocchi', {'s3-endpoint-url': self.s3_endpoint, - 's3-region-name': self.s3_region, - 's3-access-key-id': self.ec2_creds.access, - 's3-secret-access-key': self.ec2_creds.secret}, + 's3-region-name': self.s3_region, + 's3-access-key-id': self.ec2_creds.access, + 's3-secret-access-key': self.ec2_creds.secret}, model_name=self.model_name ) logging.info( - 'Waiting for units to execute config-changed hook') + 'Waiting for units to execute config-changed hook') model.wait_for_agent_status(model_name=self.model_name) logging.info( - 'Waiting for units to reach target states') + 'Waiting for units to reach target states') model.wait_for_application_states( model_name=self.model_name, - states={'gnocchi': { - 'workload-status-': 'active', - 'workload-status-message': 'Unit is ready' - }, - 'ceilometer': { - 'workload-status' : 'blocked', - 'workload-status-message': 'Run the ceilometer-upgrade action on the leader to initialize ceilometer and gnocchi' - } - } + states={ + 'gnocchi': { + 'workload-status-': 'active', + 'workload-status-message': 'Unit is ready' + }, + 'ceilometer': { + 'workload-status': 'blocked', + 'workload-status-message': 'Run the ' + + 'ceilometer-upgrade action on the leader ' + + 'to initialize ceilometer and gnocchi' + } + } ) model.block_until_all_units_idle() def test_s3_list_gnocchi_buckets(self): - """Verify that the gnocchi buckets were created in the S3 backend """ - + """Verify that the gnocchi buckets were created in the S3 backend.""" kwargs = { 'region_name': self.s3_region, 'aws_access_key_id': self.ec2_creds.access, @@ -132,7 +133,6 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): 'verify': self.cacert, } s3_client = boto3.client('s3', **kwargs) - s3 = boto3.resource('s3', **kwargs) bucket_names = ['gnocchi-measure', 'gnocchi-aggregates'] # Validate their presence @@ -140,9 +140,7 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): logging.info(pprint.pformat(bucket_list)) for bkt in bucket_list['Buckets']: for gnocchi_bkt in bucket_names: - print(gnocchi_bkt) if bkt['Name'] == gnocchi_bkt: - print('break out of 1st loop') break else: AssertionError('Bucket "{}" not found'.format(gnocchi_bkt)) From a3836e06e2f25cacd10fb2b1f788e3f48b06e635 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Thu, 18 Jun 2020 16:13:27 -0500 Subject: [PATCH 07/64] pep8 --- zaza/openstack/charm_tests/gnocchi/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index 6f7b986..0a0a0cc 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -64,7 +64,7 @@ class GnocchiTest(test_utils.OpenStackBaseTest): class GnocchiS3Test(test_utils.OpenStackBaseTest): - """Test object storage S3 API.""" + """Test Gnocchi with S3 storage backend.""" @classmethod def setUpClass(cls): @@ -91,7 +91,7 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): cls.ec2_creds = ks_client.ec2.create(user_id, project_id) def test_s3_connection_for_gnocchi(self): - """Use S3 API to list buckets.""" + """Set S3 config for gnocchi-upgrade.""" logging.info('Changing charm config to connect to swift S3 backend') model.set_application_config( 'gnocchi', From a3da91e643df477cffb17156486bfa6e0fa881e8 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Thu, 18 Jun 2020 16:32:57 -0500 Subject: [PATCH 08/64] Move configure step to setup.py --- zaza/openstack/charm_tests/gnocchi/setup.py | 69 +++++++++++++++++++++ zaza/openstack/charm_tests/gnocchi/tests.py | 34 +--------- 2 files changed, 70 insertions(+), 33 deletions(-) create mode 100644 zaza/openstack/charm_tests/gnocchi/setup.py diff --git a/zaza/openstack/charm_tests/gnocchi/setup.py b/zaza/openstack/charm_tests/gnocchi/setup.py new file mode 100644 index 0000000..086c263 --- /dev/null +++ b/zaza/openstack/charm_tests/gnocchi/setup.py @@ -0,0 +1,69 @@ +# 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. + +"""Setup for Gnocchi tests.""" + +import logging + +import zaza.model as model +import zaza.openstack.utilities.openstack as openstack_utils + + +def configure_s3_backend(): + """Inject S3 parameters from Swift for Gnocchi config.""" + session = openstack_utils.get_overcloud_keystone_session() + ks_client = openstack_utils.get_keystone_session_client(session) + + logging.info('Retrieving S3 connection data from Swift') + token_data = ks_client.tokens.get_token_data(session.get_token()) + project_id = token_data['token']['project']['id'] + user_id = token_data['token']['user']['id'] + + # Store URL to service providing S3 compatible API + for entry in token_data['token']['catalog']: + if entry['type'] == 's3': + for endpoint in entry['endpoints']: + if endpoint['interface'] == 'public': + s3_region = endpoint['region'] + s3_endpoint = endpoint['url'] + + # Create AWS compatible application credentials in Keystone + ec2_creds = ks_client.ec2.create(user_id, project_id) + + logging.info('Changing Gnocchi charm config to connect to S3') + model.set_application_config( + 'gnocchi', + {'s3-endpoint-url': s3_endpoint, + 's3-region-name': s3_region, + 's3-access-key-id': ec2_creds.access, + 's3-secret-access-key': ec2_creds.secret} + ) + logging.info('Waiting for units to execute config-changed hook') + model.wait_for_agent_status() + logging.info('Waiting for units to reach target states') + model.wait_for_application_states( + states={ + 'gnocchi': { + 'workload-status-': 'active', + 'workload-status-message': 'Unit is ready' + }, + 'ceilometer': { + 'workload-status': 'blocked', + 'workload-status-message': 'Run the ' + + 'ceilometer-upgrade action on the leader ' + + 'to initialize ceilometer and gnocchi' + } + } + ) + model.block_until_all_units_idle() \ No newline at end of file diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index 0a0a0cc..d7d5736 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -64,7 +64,7 @@ class GnocchiTest(test_utils.OpenStackBaseTest): class GnocchiS3Test(test_utils.OpenStackBaseTest): - """Test Gnocchi with S3 storage backend.""" + """Test Gnocchi for S3 storage backend.""" @classmethod def setUpClass(cls): @@ -90,38 +90,6 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): # Create AWS compatible application credentials in Keystone cls.ec2_creds = ks_client.ec2.create(user_id, project_id) - def test_s3_connection_for_gnocchi(self): - """Set S3 config for gnocchi-upgrade.""" - logging.info('Changing charm config to connect to swift S3 backend') - model.set_application_config( - 'gnocchi', - {'s3-endpoint-url': self.s3_endpoint, - 's3-region-name': self.s3_region, - 's3-access-key-id': self.ec2_creds.access, - 's3-secret-access-key': self.ec2_creds.secret}, - model_name=self.model_name - ) - logging.info( - 'Waiting for units to execute config-changed hook') - model.wait_for_agent_status(model_name=self.model_name) - logging.info( - 'Waiting for units to reach target states') - model.wait_for_application_states( - model_name=self.model_name, - states={ - 'gnocchi': { - 'workload-status-': 'active', - 'workload-status-message': 'Unit is ready' - }, - 'ceilometer': { - 'workload-status': 'blocked', - 'workload-status-message': 'Run the ' + - 'ceilometer-upgrade action on the leader ' + - 'to initialize ceilometer and gnocchi' - } - } - ) - model.block_until_all_units_idle() def test_s3_list_gnocchi_buckets(self): """Verify that the gnocchi buckets were created in the S3 backend.""" From 692853e242bb66ba426032ecec162a840112335a Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Fri, 19 Jun 2020 14:19:25 -0500 Subject: [PATCH 09/64] pep8 --- zaza/openstack/charm_tests/gnocchi/setup.py | 2 +- zaza/openstack/charm_tests/gnocchi/tests.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/zaza/openstack/charm_tests/gnocchi/setup.py b/zaza/openstack/charm_tests/gnocchi/setup.py index 086c263..4995f22 100644 --- a/zaza/openstack/charm_tests/gnocchi/setup.py +++ b/zaza/openstack/charm_tests/gnocchi/setup.py @@ -66,4 +66,4 @@ def configure_s3_backend(): } } ) - model.block_until_all_units_idle() \ No newline at end of file + model.block_until_all_units_idle() diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index d7d5736..680bf3c 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -21,7 +21,6 @@ import logging import pprint from gnocchiclient.v1 import client as gnocchi_client -import zaza.model as model import zaza.openstack.charm_tests.test_utils as test_utils import zaza.openstack.utilities.openstack as openstack_utils @@ -90,7 +89,6 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): # Create AWS compatible application credentials in Keystone cls.ec2_creds = ks_client.ec2.create(user_id, project_id) - def test_s3_list_gnocchi_buckets(self): """Verify that the gnocchi buckets were created in the S3 backend.""" kwargs = { From 7fc0aa8bd96eb062ec49bb8909ddd2fa928e1a95 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Fri, 3 Jul 2020 15:26:52 +0200 Subject: [PATCH 10/64] Remove some deprecation warnings --- unit_tests/utilities/test_zaza_utilities_openstack.py | 4 ++-- zaza/openstack/utilities/generic.py | 2 +- zaza/openstack/utilities/openstack.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/unit_tests/utilities/test_zaza_utilities_openstack.py b/unit_tests/utilities/test_zaza_utilities_openstack.py index a6aa190..7271708 100644 --- a/unit_tests/utilities/test_zaza_utilities_openstack.py +++ b/unit_tests/utilities/test_zaza_utilities_openstack.py @@ -809,12 +809,12 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): name='_get_os_version' ) self.patch( - 'zaza.openstack.utilities.juju.get_machines_for_application', + 'zaza.utilities.juju.get_machines_for_application', new_callable=mock.MagicMock(), name='_get_machines' ) self.patch( - 'zaza.openstack.utilities.juju.get_machine_series', + 'zaza.utilities.juju.get_machine_series', new_callable=mock.MagicMock(), name='_get_machine_series' ) diff --git a/zaza/openstack/utilities/generic.py b/zaza/openstack/utilities/generic.py index 0874cc6..dd8b1e0 100644 --- a/zaza/openstack/utilities/generic.py +++ b/zaza/openstack/utilities/generic.py @@ -23,9 +23,9 @@ import telnetlib import yaml from zaza import model -from zaza.openstack.utilities import juju as juju_utils from zaza.openstack.utilities import exceptions as zaza_exceptions from zaza.openstack.utilities.os_versions import UBUNTU_OPENSTACK_RELEASE +from zaza.utilities import juju as juju_utils def assertActionRanOK(action): diff --git a/zaza/openstack/utilities/openstack.py b/zaza/openstack/utilities/openstack.py index 28ba29a..227d041 100644 --- a/zaza/openstack/utilities/openstack.py +++ b/zaza/openstack/utilities/openstack.py @@ -40,6 +40,7 @@ from keystoneauth1.identity import ( ) import zaza.openstack.utilities.cert as cert import zaza.utilities.deployment_env as deployment_env +import zaza.utilities.juju as juju_utils from novaclient import client as novaclient_client from neutronclient.v2_0 import client as neutronclient from neutronclient.common import exceptions as neutronexceptions @@ -66,7 +67,6 @@ from zaza import model from zaza.openstack.utilities import ( exceptions, generic as generic_utils, - juju as juju_utils, ) CIRROS_RELEASE_URL = 'http://download.cirros-cloud.net/version/released' From 16cee0193fa2dff1834bf8838f34a63add5fddca Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Fri, 3 Jul 2020 15:43:00 +0200 Subject: [PATCH 11/64] Make linter happy --- zaza/openstack/charm_tests/neutron/tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/zaza/openstack/charm_tests/neutron/tests.py b/zaza/openstack/charm_tests/neutron/tests.py index 4b1ff1b..c8c302a 100644 --- a/zaza/openstack/charm_tests/neutron/tests.py +++ b/zaza/openstack/charm_tests/neutron/tests.py @@ -23,7 +23,6 @@ import copy import logging import tenacity -import unittest import zaza import zaza.openstack.charm_tests.nova.utils as nova_utils From 158e8ce37f9d9240a396136e91c0387d840a2891 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Wed, 22 Jul 2020 10:40:29 +0200 Subject: [PATCH 12/64] octavia/policyd: Change target for policyd test List available provider drivers as policyd test, this should be more light weight than creating and retrieving a load-balancer. Fixes #349 --- zaza/openstack/charm_tests/policyd/tests.py | 87 +-------------------- 1 file changed, 3 insertions(+), 84 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 67b9909..e69aaff 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -659,7 +659,7 @@ class HeatTests(BasePolicydSpecialization): class OctaviaTests(BasePolicydSpecialization): """Test the policyd override using the octavia client.""" - _rule = {'rule.yaml': "{'os_load-balancer_api:loadbalancer:get_one': '!'}"} + _rule = {'rule.yaml': "{'os_load-balancer_api:provider:get_all': '!'}"} @classmethod def setUpClass(cls, application_name=None): @@ -667,89 +667,8 @@ class OctaviaTests(BasePolicydSpecialization): super(OctaviaTests, cls).setUpClass(application_name="octavia") cls.application_name = "octavia" - def setup_for_attempt_operation(self, ip): - """Create a loadbalancer. - - This is necessary so that the attempt is to show the load-balancer and - this is an operator that the policy can stop. Unfortunately, octavia, - whilst it has a policy for just listing load-balancers, unfortunately, - it doesn't work; whereas showing the load-balancer can be stopped. - - NB this only works if the setup phase of the octavia tests have been - completed. - - :param ip: the ip of for keystone. - :type ip: str - """ - logging.info("Setting up loadbalancer.") - auth = openstack_utils.get_overcloud_auth(address=ip) - sess = openstack_utils.get_keystone_session(auth) - - octavia_client = openstack_utils.get_octavia_session_client(sess) - neutron_client = openstack_utils.get_neutron_session_client(sess) - - if openstack_utils.dvr_enabled(): - network_name = 'private_lb_fip_network' - else: - network_name = 'private' - resp = neutron_client.list_networks(name=network_name) - - vip_subnet_id = resp['networks'][0]['subnets'][0] - - res = octavia_client.load_balancer_create( - json={ - 'loadbalancer': { - 'description': 'Created by Zaza', - 'admin_state_up': True, - 'vip_subnet_id': vip_subnet_id, - 'name': 'zaza-lb-0', - }}) - self.lb_id = res['loadbalancer']['id'] - # now wait for it to get to the active state - - @tenacity.retry(wait=tenacity.wait_fixed(1), - reraise=True, stop=tenacity.stop_after_delay(900)) - def wait_for_lb_resource(client, resource_id): - resp = client.load_balancer_show(resource_id) - logging.info(resp['provisioning_status']) - assert resp['provisioning_status'] == 'ACTIVE', ( - 'load balancer resource has not reached ' - 'expected provisioning status: {}' - .format(resp)) - return resp - - logging.info('Awaiting loadbalancer to reach provisioning_status ' - '"ACTIVE"') - resp = wait_for_lb_resource(octavia_client, self.lb_id) - logging.info(resp) - logging.info("Setup loadbalancer complete.") - - def cleanup_for_attempt_operation(self, ip): - """Remove the loadbalancer. - - :param ip: the ip of for keystone. - :type ip: str - """ - logging.info("Deleting loadbalancer {}.".format(self.lb_id)) - auth = openstack_utils.get_overcloud_auth(address=ip) - sess = openstack_utils.get_keystone_session(auth) - - octavia_client = openstack_utils.get_octavia_session_client(sess) - octavia_client.load_balancer_delete(self.lb_id) - logging.info("Deleting loadbalancer in progress ...") - - @tenacity.retry(wait=tenacity.wait_fixed(1), - reraise=True, stop=tenacity.stop_after_delay(900)) - def wait_til_deleted(client, lb_id): - lb_list = client.load_balancer_list() - ids = [lb['id'] for lb in lb_list['loadbalancers']] - assert lb_id not in ids, 'load balancer still deleting' - - wait_til_deleted(octavia_client, self.lb_id) - logging.info("Deleted loadbalancer.") - def get_client_and_attempt_operation(self, ip): - """Attempt to show the loadbalancer as a policyd override. + """Attempt to list available provider drivers. This operation should pass normally, and fail when the rule has been overriden (see the `rule` class variable. @@ -761,6 +680,6 @@ class OctaviaTests(BasePolicydSpecialization): octavia_client = openstack_utils.get_octavia_session_client( self.get_keystone_session_admin_user(ip)) try: - octavia_client.load_balancer_show(self.lb_id) + octavia_client.provider_list() except octaviaclient.OctaviaClientException: raise PolicydOperationFailedException() From a1eb37c36562766ca69ba6d320e563bc09cac35b Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Thu, 23 Jul 2020 09:49:48 +0200 Subject: [PATCH 13/64] Make neutron/arista tests more robust When creating an Arista-backed Neutron network, the API call from Neutron to Arista sometimes spuriously fails. This is not fatal and Neutron simply retries a few seconds later. The tests now account for that. --- .../charm_tests/neutron_arista/tests.py | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/zaza/openstack/charm_tests/neutron_arista/tests.py b/zaza/openstack/charm_tests/neutron_arista/tests.py index 3f78bf7..ba29bd6 100644 --- a/zaza/openstack/charm_tests/neutron_arista/tests.py +++ b/zaza/openstack/charm_tests/neutron_arista/tests.py @@ -39,15 +39,36 @@ class NeutronCreateAristaNetworkTest(neutron_tests.NeutronCreateNetworkTest): cls.neutron_client.list_networks() def _assert_test_network_exists_and_return_id(self): - actual_network_names = arista_utils.query_fixture_networks( - arista_utils.fixture_ip_addr()) - self.assertEqual(actual_network_names, [self._TEST_NET_NAME]) + logging.info('Checking that the test network exists on the Arista ' + 'test fixture...') + + # Sometimes the API call from Neutron to Arista fails and Neutron + # retries a couple of seconds later, which is why the newly created + # test network may not be immediately visible on Arista's API. + for attempt in tenacity.Retrying( + wait=tenacity.wait_fixed(10), # seconds + stop=tenacity.stop_after_attempt(3), + reraise=True): + with attempt: + actual_network_names = arista_utils.query_fixture_networks( + arista_utils.fixture_ip_addr()) + self.assertEqual(actual_network_names, [self._TEST_NET_NAME]) + return super(NeutronCreateAristaNetworkTest, self)._assert_test_network_exists_and_return_id() def _assert_test_network_doesnt_exist(self): - actual_network_names = arista_utils.query_fixture_networks( - arista_utils.fixture_ip_addr()) - self.assertEqual(actual_network_names, []) + logging.info("Checking that the test network doesn't exist on the " + "Arista test fixture...") + + for attempt in tenacity.Retrying( + wait=tenacity.wait_fixed(10), # seconds + stop=tenacity.stop_after_attempt(3), + reraise=True): + with attempt: + actual_network_names = arista_utils.query_fixture_networks( + arista_utils.fixture_ip_addr()) + self.assertEqual(actual_network_names, []) + super(NeutronCreateAristaNetworkTest, self)._assert_test_network_doesnt_exist() From 59d68e49a5830741a602aee0a6e7fa73d8eda776 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh <567675+ajkavanagh@users.noreply.github.com> Date: Thu, 23 Jul 2020 14:07:21 +0100 Subject: [PATCH 14/64] Ensure that the tests directory exists for the local cacert (#373) A recent change to allow multiple zazas to run at the same time fixed the cacert file into a local 'tests/' directory. Unfortunately, that doesn't exist for every environment where zaza runs (e.g. mojo) and so this patch ensures that the path exists prior to trying to download into it. --- zaza/openstack/utilities/openstack.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/zaza/openstack/utilities/openstack.py b/zaza/openstack/utilities/openstack.py index 4424cea..14c708c 100644 --- a/zaza/openstack/utilities/openstack.py +++ b/zaza/openstack/utilities/openstack.py @@ -1737,6 +1737,15 @@ def get_overcloud_auth(address=None, model_name=None): } if tls_rid: unit = model.get_first_unit_name('keystone', model_name=model_name) + + # ensure that the path to put the local cacert in actually exists. The + # assumption that 'tests/' exists for, say, mojo is false. + # Needed due to: + # commit: 537473ad3addeaa3d1e4e2d0fd556aeaa4018eb2 + _dir = os.path.dirname(KEYSTONE_LOCAL_CACERT) + if not os.path.exists(_dir): + os.makedirs(_dir) + model.scp_from_unit( unit, KEYSTONE_REMOTE_CACERT, From 6bbb4fd4e34102b0bd924b00461abfd0abaa745f Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Fri, 24 Jul 2020 10:13:50 -0500 Subject: [PATCH 15/64] add iscsi-connector files and setup --- .../charm_tests/iscsi-connector/__init__.py | 15 +++++ .../charm_tests/iscsi-connector/setup.py | 56 +++++++++++++++++++ .../charm_tests/iscsi-connector/tests.py | 15 +++++ 3 files changed, 86 insertions(+) create mode 100644 zaza/openstack/charm_tests/iscsi-connector/__init__.py create mode 100644 zaza/openstack/charm_tests/iscsi-connector/setup.py create mode 100644 zaza/openstack/charm_tests/iscsi-connector/tests.py diff --git a/zaza/openstack/charm_tests/iscsi-connector/__init__.py b/zaza/openstack/charm_tests/iscsi-connector/__init__.py new file mode 100644 index 0000000..c0cf179 --- /dev/null +++ b/zaza/openstack/charm_tests/iscsi-connector/__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 testing iscsi-connector charm.""" \ No newline at end of file diff --git a/zaza/openstack/charm_tests/iscsi-connector/setup.py b/zaza/openstack/charm_tests/iscsi-connector/setup.py new file mode 100644 index 0000000..f18a4ad --- /dev/null +++ b/zaza/openstack/charm_tests/iscsi-connector/setup.py @@ -0,0 +1,56 @@ +# 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 setting up iscsi-connector tests.""" + +# """ +# steps for testing without password: +# - deploy ubuntu for storage +# - deploy ubuntu for initiator +# configure that unit: +# - install tgt -y +# - configure iscsi target +# - need to know the initiator ip +# - restart tgt +# - configure ubuntu initiator with init dict, target, port +# """ + +import zaza.model +import zaza.openstack.utilities.openstack as openstack_utils + + +def basic_target_setup(): + """Run basic setup for iscsi guest.""" + unit = zaza.model.get_units('ubuntu-target')[0] + setup_cmds = [ + "apt install --yes tgt", + "systemctl start tgt",] + for cmd in setup_cmds: + zaza.model.run_on_unit( + unit.entity_id, + cmd) + +def configure_iscsi_target(): + lun = 'iqn.2020-07.canonical.com:lun1' + backing_store = 'dev/vdb' + initiator_address = zaza.model.get_app_ips('ubuntu')[0] + write_file = ( + """echo -e '\n\tbacking-store {}\n\tinitiator-address {}\n' | """ + """sudo tee /etc/tgt/conf.d/iscsi.conf""".format(lun, backing_store,initiator_address) + ) + zaza.model.run_on_unit('ubuntu-target/0', write_file) + # Restart tgt to load new config + restart_tgt = "systemctl restart tgt" + zaza.model.run_on_unit('ubuntu-target/0', restart_tgt) + diff --git a/zaza/openstack/charm_tests/iscsi-connector/tests.py b/zaza/openstack/charm_tests/iscsi-connector/tests.py new file mode 100644 index 0000000..c30f38c --- /dev/null +++ b/zaza/openstack/charm_tests/iscsi-connector/tests.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. + +"""Encapsulate iscsi-connector testing.""" \ No newline at end of file From fb8de5cc81221ecc2e09a628540616da627c65ce Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Fri, 24 Jul 2020 11:09:22 -0500 Subject: [PATCH 16/64] add open-port cmd for iscsi target --- zaza/openstack/charm_tests/iscsi-connector/setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/iscsi-connector/setup.py b/zaza/openstack/charm_tests/iscsi-connector/setup.py index f18a4ad..f453ed4 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/setup.py +++ b/zaza/openstack/charm_tests/iscsi-connector/setup.py @@ -35,7 +35,8 @@ def basic_target_setup(): unit = zaza.model.get_units('ubuntu-target')[0] setup_cmds = [ "apt install --yes tgt", - "systemctl start tgt",] + "systemctl start tgt", + "open-port 3260"] for cmd in setup_cmds: zaza.model.run_on_unit( unit.entity_id, From 277272547f06b04d06ea94149f79d0b47f44a3ed Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Fri, 24 Jul 2020 14:14:19 -0500 Subject: [PATCH 17/64] add tests iscsi-connector --- .../charm_tests/iscsi-connector/setup.py | 4 +++ .../charm_tests/iscsi-connector/tests.py | 32 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/iscsi-connector/setup.py b/zaza/openstack/charm_tests/iscsi-connector/setup.py index f453ed4..d016a36 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/setup.py +++ b/zaza/openstack/charm_tests/iscsi-connector/setup.py @@ -26,12 +26,15 @@ # - configure ubuntu initiator with init dict, target, port # """ +import logging + import zaza.model import zaza.openstack.utilities.openstack as openstack_utils def basic_target_setup(): """Run basic setup for iscsi guest.""" + logging.info('Installing package tgt on ubuntu-target') unit = zaza.model.get_units('ubuntu-target')[0] setup_cmds = [ "apt install --yes tgt", @@ -50,6 +53,7 @@ def configure_iscsi_target(): """echo -e '\n\tbacking-store {}\n\tinitiator-address {}\n' | """ """sudo tee /etc/tgt/conf.d/iscsi.conf""".format(lun, backing_store,initiator_address) ) + logging.info('Writing target iscsi.conf') zaza.model.run_on_unit('ubuntu-target/0', write_file) # Restart tgt to load new config restart_tgt = "systemctl restart tgt" diff --git a/zaza/openstack/charm_tests/iscsi-connector/tests.py b/zaza/openstack/charm_tests/iscsi-connector/tests.py index c30f38c..5c2babf 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/tests.py +++ b/zaza/openstack/charm_tests/iscsi-connector/tests.py @@ -12,4 +12,34 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Encapsulate iscsi-connector testing.""" \ No newline at end of file +"""Encapsulate iscsi-connector testing.""" + +import logging +import tempfile + +import zaza.model +import zaza.openstack.charm_tests.test_utils as test_utils +import zaza.openstack.utilities.generic as generic_utils + + +class ISCSIConnectorTest(test_utils.BaseCharmTest): + """Class for iscsi-connector tests.""" + + IQN = 'iqn.2020-07.canonical.com:lun1' + + def configure_iscsi_connector(self): + unit_fqdn = self.get_unit_full_hostname('ubuntu/0') + target_ip = zaza.model.get_app_ips('ubuntu-target')[0] + conf = { + 'initiator-dictionary': '{"{unit_fqdn}":"{IQN}"}', + 'target': target_ip, + 'port': '3260', + } + zaza.model.set_application_config('iscsi-connector', conf) + + def get_unit_full_hostname(self, unit_name): + """Retrieve the full hostname of a unit.""" + for unit in zaza.model.get_units(unit_name): + result = zaza.model.run_on_unit(unit.entity_id, 'hostname -f') + hostname = result['Stdout'].rstrip() + return hostname From bad9b4c768d9669f6c14bda6a3fc6bb48992de0d Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Fri, 24 Jul 2020 14:14:19 -0500 Subject: [PATCH 18/64] add tests iscsi-connector --- .../charm_tests/iscsi-connector/setup.py | 4 +++ .../charm_tests/iscsi-connector/tests.py | 35 ++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/iscsi-connector/setup.py b/zaza/openstack/charm_tests/iscsi-connector/setup.py index f453ed4..d016a36 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/setup.py +++ b/zaza/openstack/charm_tests/iscsi-connector/setup.py @@ -26,12 +26,15 @@ # - configure ubuntu initiator with init dict, target, port # """ +import logging + import zaza.model import zaza.openstack.utilities.openstack as openstack_utils def basic_target_setup(): """Run basic setup for iscsi guest.""" + logging.info('Installing package tgt on ubuntu-target') unit = zaza.model.get_units('ubuntu-target')[0] setup_cmds = [ "apt install --yes tgt", @@ -50,6 +53,7 @@ def configure_iscsi_target(): """echo -e '\n\tbacking-store {}\n\tinitiator-address {}\n' | """ """sudo tee /etc/tgt/conf.d/iscsi.conf""".format(lun, backing_store,initiator_address) ) + logging.info('Writing target iscsi.conf') zaza.model.run_on_unit('ubuntu-target/0', write_file) # Restart tgt to load new config restart_tgt = "systemctl restart tgt" diff --git a/zaza/openstack/charm_tests/iscsi-connector/tests.py b/zaza/openstack/charm_tests/iscsi-connector/tests.py index c30f38c..d7b67fc 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/tests.py +++ b/zaza/openstack/charm_tests/iscsi-connector/tests.py @@ -12,4 +12,37 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Encapsulate iscsi-connector testing.""" \ No newline at end of file +"""Encapsulate iscsi-connector testing.""" + +import logging +import tempfile + +import zaza.model +import zaza.openstack.charm_tests.test_utils as test_utils +import zaza.openstack.utilities.generic as generic_utils + + +class ISCSIConnectorTest(test_utils.BaseCharmTest): + """Class for iscsi-connector tests.""" + + IQN = 'iqn.2020-07.canonical.com:lun1' + + def configure_iscsi_connector(self): + unit_fqdn = self.get_unit_full_hostname('ubuntu/0') + target_ip = zaza.model.get_app_ips('ubuntu-target')[0] + conf = { + 'initiator-dictionary': '{"{unit_fqdn}":"{IQN}"}', + 'target': target_ip, + 'port': '3260', + } + zaza.model.set_application_config('iscsi-connector', conf) + + def get_unit_full_hostname(self, unit_name): + """Retrieve the full hostname of a unit.""" + for unit in zaza.model.get_units(unit_name): + result = zaza.model.run_on_unit(unit.entity_id, 'hostname -f') + hostname = result['Stdout'].rstrip() + return hostname + + def test_iscsi_connector(self): + self.configure_iscsi_connector() \ No newline at end of file From 5664251152e06597e78f20cd926fc0c830fd24db Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Fri, 24 Jul 2020 14:25:27 -0500 Subject: [PATCH 19/64] tests.py --- zaza/openstack/charm_tests/iscsi-connector/tests.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zaza/openstack/charm_tests/iscsi-connector/tests.py b/zaza/openstack/charm_tests/iscsi-connector/tests.py index f7b67ea..8f707eb 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/tests.py +++ b/zaza/openstack/charm_tests/iscsi-connector/tests.py @@ -25,6 +25,11 @@ import zaza.openstack.utilities.generic as generic_utils class ISCSIConnectorTest(test_utils.BaseCharmTest): """Class for iscsi-connector tests.""" + @classmethod + def setUpClass(cls): + """Run class setup for running glance tests.""" + super(ISCSIConnectorTest, cls).setUpClass() + IQN = 'iqn.2020-07.canonical.com:lun1' def configure_iscsi_connector(self): From 4c57e73f73ab18d9d9fe85dd1bdf798a6b805ee8 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Fri, 24 Jul 2020 14:25:27 -0500 Subject: [PATCH 20/64] tests.py --- zaza/openstack/charm_tests/iscsi-connector/tests.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zaza/openstack/charm_tests/iscsi-connector/tests.py b/zaza/openstack/charm_tests/iscsi-connector/tests.py index f7b67ea..8f707eb 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/tests.py +++ b/zaza/openstack/charm_tests/iscsi-connector/tests.py @@ -25,6 +25,11 @@ import zaza.openstack.utilities.generic as generic_utils class ISCSIConnectorTest(test_utils.BaseCharmTest): """Class for iscsi-connector tests.""" + @classmethod + def setUpClass(cls): + """Run class setup for running glance tests.""" + super(ISCSIConnectorTest, cls).setUpClass() + IQN = 'iqn.2020-07.canonical.com:lun1' def configure_iscsi_connector(self): From 411d9aeb50b2165feac7a43494521ed854ec3549 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Fri, 24 Jul 2020 15:14:12 -0500 Subject: [PATCH 21/64] fix hostname --- zaza/openstack/charm_tests/iscsi-connector/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/iscsi-connector/tests.py b/zaza/openstack/charm_tests/iscsi-connector/tests.py index 8f707eb..be5cb24 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/tests.py +++ b/zaza/openstack/charm_tests/iscsi-connector/tests.py @@ -33,7 +33,7 @@ class ISCSIConnectorTest(test_utils.BaseCharmTest): IQN = 'iqn.2020-07.canonical.com:lun1' def configure_iscsi_connector(self): - unit_fqdn = self.get_unit_full_hostname('ubuntu/0') + unit_fqdn = self.get_unit_full_hostname('ubuntu') target_ip = zaza.model.get_app_ips('ubuntu-target')[0] conf = { 'initiator-dictionary': '{"{unit_fqdn}":"{IQN}"}', From 738664e8bab6cbacb51deedfbeb1bfbe7ffea429 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Fri, 24 Jul 2020 15:53:00 -0500 Subject: [PATCH 22/64] assert iscsi session --- .../openstack/charm_tests/iscsi-connector/tests.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/zaza/openstack/charm_tests/iscsi-connector/tests.py b/zaza/openstack/charm_tests/iscsi-connector/tests.py index be5cb24..33a85dd 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/tests.py +++ b/zaza/openstack/charm_tests/iscsi-connector/tests.py @@ -14,6 +14,7 @@ """Encapsulate iscsi-connector testing.""" +import json import logging import tempfile @@ -29,14 +30,14 @@ class ISCSIConnectorTest(test_utils.BaseCharmTest): def setUpClass(cls): """Run class setup for running glance tests.""" super(ISCSIConnectorTest, cls).setUpClass() - - IQN = 'iqn.2020-07.canonical.com:lun1' def configure_iscsi_connector(self): + iqn = 'iqn.2020-07.canonical.com:lun1' unit_fqdn = self.get_unit_full_hostname('ubuntu') target_ip = zaza.model.get_app_ips('ubuntu-target')[0] + initiator_dictionary = json.dumps({unit_fqdn:iqn}) conf = { - 'initiator-dictionary': '{"{unit_fqdn}":"{IQN}"}', + 'initiator-dictionary': initiator_dictionary, 'target': target_ip, 'port': '3260', } @@ -51,3 +52,10 @@ class ISCSIConnectorTest(test_utils.BaseCharmTest): def test_iscsi_connector(self): self.configure_iscsi_connector() + logging.info('Wait for idle/ready status...') + zaza_model.wait_for_application_states() + + def test_validate_iscsi_session(self): + unit = zaza.model.get_units('ubuntu')[0] + run = zaza.model.run_on_unit(unit.entity_id, 'iscsiadm -m session') + assert run['Stdout'] != "iscsiadm: No active sessions." From ab91b94d779a260cf572e34114ea64448fc3cd65 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Mon, 27 Jul 2020 10:25:21 -0500 Subject: [PATCH 23/64] add logs to iscsi test --- zaza/openstack/charm_tests/iscsi-connector/tests.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zaza/openstack/charm_tests/iscsi-connector/tests.py b/zaza/openstack/charm_tests/iscsi-connector/tests.py index 33a85dd..bbd4e87 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/tests.py +++ b/zaza/openstack/charm_tests/iscsi-connector/tests.py @@ -53,9 +53,11 @@ class ISCSIConnectorTest(test_utils.BaseCharmTest): def test_iscsi_connector(self): self.configure_iscsi_connector() logging.info('Wait for idle/ready status...') - zaza_model.wait_for_application_states() + zaza.model.wait_for_application_states() def test_validate_iscsi_session(self): unit = zaza.model.get_units('ubuntu')[0] + logging.info('Checking if iscsi session is active.') run = zaza.model.run_on_unit(unit.entity_id, 'iscsiadm -m session') - assert run['Stdout'] != "iscsiadm: No active sessions." + logging.info('iscsiadm -m session: Stdout: {}, Stderr: {}, Code: {}'.format(run['Stdout'], run['Stderr'], run['Code'])) + assert run['Code'] == '0' From e997647870e6f194eeaf9a29da05af862de48934 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Mon, 27 Jul 2020 15:47:21 -0500 Subject: [PATCH 24/64] Add CHAP auth to tests --- zaza/openstack/charm_tests/iscsi-connector/setup.py | 9 +++++++-- zaza/openstack/charm_tests/iscsi-connector/tests.py | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/zaza/openstack/charm_tests/iscsi-connector/setup.py b/zaza/openstack/charm_tests/iscsi-connector/setup.py index d016a36..dbfc6f0 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/setup.py +++ b/zaza/openstack/charm_tests/iscsi-connector/setup.py @@ -49,9 +49,14 @@ def configure_iscsi_target(): lun = 'iqn.2020-07.canonical.com:lun1' backing_store = 'dev/vdb' initiator_address = zaza.model.get_app_ips('ubuntu')[0] + username = 'iscsi-user' + password = 'password123' + username_in = 'iscsi-target' + password_in = 'secretpass' write_file = ( - """echo -e '\n\tbacking-store {}\n\tinitiator-address {}\n' | """ - """sudo tee /etc/tgt/conf.d/iscsi.conf""".format(lun, backing_store,initiator_address) + """echo -e '\n\tbacking-store {}\n\tinitiator-address {}\n\tincominguser {} {}\n\t""" + """outgoinguser {} {}' | sudo tee /etc/tgt/conf.d/iscsi.conf""".format(lun, backing_store, + initiator_address, username, password, username_in, password_in) ) logging.info('Writing target iscsi.conf') zaza.model.run_on_unit('ubuntu-target/0', write_file) diff --git a/zaza/openstack/charm_tests/iscsi-connector/tests.py b/zaza/openstack/charm_tests/iscsi-connector/tests.py index bbd4e87..0507b81 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/tests.py +++ b/zaza/openstack/charm_tests/iscsi-connector/tests.py @@ -40,6 +40,11 @@ class ISCSIConnectorTest(test_utils.BaseCharmTest): 'initiator-dictionary': initiator_dictionary, 'target': target_ip, 'port': '3260', + 'iscsi-node-session-auth-authmethod': 'CHAP', + 'iscsi-node-session-auth-username': 'iscsi-user', + 'iscsi-node-session-auth-password': 'password123', + 'iscsi-node-session-auth-username_in': 'iscsi-target', + 'iscsi-node-session-auth-password_in': 'secretpass', } zaza.model.set_application_config('iscsi-connector', conf) From 127ac7c87c9f3592292dddd88db4dd2e1d2c93bf Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Mon, 27 Jul 2020 15:59:06 -0500 Subject: [PATCH 25/64] fix typo --- zaza/openstack/charm_tests/iscsi-connector/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zaza/openstack/charm_tests/iscsi-connector/tests.py b/zaza/openstack/charm_tests/iscsi-connector/tests.py index 0507b81..89c1223 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/tests.py +++ b/zaza/openstack/charm_tests/iscsi-connector/tests.py @@ -43,8 +43,8 @@ class ISCSIConnectorTest(test_utils.BaseCharmTest): 'iscsi-node-session-auth-authmethod': 'CHAP', 'iscsi-node-session-auth-username': 'iscsi-user', 'iscsi-node-session-auth-password': 'password123', - 'iscsi-node-session-auth-username_in': 'iscsi-target', - 'iscsi-node-session-auth-password_in': 'secretpass', + 'iscsi-node-session-auth-username-in': 'iscsi-target', + 'iscsi-node-session-auth-password-in': 'secretpass', } zaza.model.set_application_config('iscsi-connector', conf) From 7c9e2edbd1ea37b5b0f96d1e292327a1a9e35175 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Mon, 27 Jul 2020 16:23:36 -0500 Subject: [PATCH 26/64] add missing newline --- zaza/openstack/charm_tests/iscsi-connector/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/iscsi-connector/setup.py b/zaza/openstack/charm_tests/iscsi-connector/setup.py index dbfc6f0..ff58c4f 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/setup.py +++ b/zaza/openstack/charm_tests/iscsi-connector/setup.py @@ -55,7 +55,7 @@ def configure_iscsi_target(): password_in = 'secretpass' write_file = ( """echo -e '\n\tbacking-store {}\n\tinitiator-address {}\n\tincominguser {} {}\n\t""" - """outgoinguser {} {}' | sudo tee /etc/tgt/conf.d/iscsi.conf""".format(lun, backing_store, + """outgoinguser {} {}\n' | sudo tee /etc/tgt/conf.d/iscsi.conf""".format(lun, backing_store, initiator_address, username, password, username_in, password_in) ) logging.info('Writing target iscsi.conf') From e5305333454d4a47cd6aa2f900231474fc858ff7 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Thu, 30 Jul 2020 10:16:19 +0200 Subject: [PATCH 27/64] Make NeutronCreateNetworkTest more robust --- zaza/openstack/charm_tests/neutron/tests.py | 16 ++++++++++++++-- .../charm_tests/neutron_arista/tests.py | 9 +-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/zaza/openstack/charm_tests/neutron/tests.py b/zaza/openstack/charm_tests/neutron/tests.py index ec6998d..311e763 100644 --- a/zaza/openstack/charm_tests/neutron/tests.py +++ b/zaza/openstack/charm_tests/neutron/tests.py @@ -295,19 +295,31 @@ class NeutronCreateNetworkTest(test_utils.OpenStackBaseTest): def test_400_create_network(self): """Create a network, verify that it exists, and then delete it.""" + self._wait_for_neutron_ready() self._assert_test_network_doesnt_exist() self._create_test_network() net_id = self._assert_test_network_exists_and_return_id() self._delete_test_network(net_id) self._assert_test_network_doesnt_exist() + @classmethod + def _wait_for_neutron_ready(cls): + logging.info('Waiting for Neutron to become ready...') + zaza.model.wait_for_application_states() + for attempt in tenacity.Retrying( + wait=tenacity.wait_fixed(5), # seconds + stop=tenacity.stop_after_attempt(12), + reraise=True): + with attempt: + cls.neutron_client.list_networks() + def _create_test_network(self): - logging.debug('Creating neutron network...') + logging.info('Creating neutron network...') network = {'name': self._TEST_NET_NAME} self.neutron_client.create_network({'network': network}) def _delete_test_network(self, net_id): - logging.debug('Deleting neutron network...') + logging.info('Deleting neutron network...') self.neutron_client.delete_network(net_id) def _assert_test_network_exists_and_return_id(self): diff --git a/zaza/openstack/charm_tests/neutron_arista/tests.py b/zaza/openstack/charm_tests/neutron_arista/tests.py index ba29bd6..354dfd1 100644 --- a/zaza/openstack/charm_tests/neutron_arista/tests.py +++ b/zaza/openstack/charm_tests/neutron_arista/tests.py @@ -29,14 +29,7 @@ class NeutronCreateAristaNetworkTest(neutron_tests.NeutronCreateNetworkTest): def setUpClass(cls): """Run class setup for running Neutron Arista tests.""" super(NeutronCreateAristaNetworkTest, cls).setUpClass() - - logging.info('Waiting for Neutron to become ready...') - for attempt in tenacity.Retrying( - wait=tenacity.wait_fixed(5), # seconds - stop=tenacity.stop_after_attempt(12), - reraise=True): - with attempt: - cls.neutron_client.list_networks() + cls._wait_for_neutron_ready() def _assert_test_network_exists_and_return_id(self): logging.info('Checking that the test network exists on the Arista ' From a005cf32263b3133ea72427ead080b0eda342698 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Tue, 4 Aug 2020 09:12:42 -0500 Subject: [PATCH 28/64] pep8 --- .../charm_tests/iscsi-connector/__init__.py | 2 +- .../charm_tests/iscsi-connector/setup.py | 28 ++++++++----------- .../charm_tests/iscsi-connector/tests.py | 14 ++++++---- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/zaza/openstack/charm_tests/iscsi-connector/__init__.py b/zaza/openstack/charm_tests/iscsi-connector/__init__.py index c0cf179..659b2b0 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/__init__.py +++ b/zaza/openstack/charm_tests/iscsi-connector/__init__.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Collection of code for setting up and testing iscsi-connector charm.""" \ No newline at end of file +"""Collection of code for setting up and testing iscsi-connector charm.""" diff --git a/zaza/openstack/charm_tests/iscsi-connector/setup.py b/zaza/openstack/charm_tests/iscsi-connector/setup.py index ff58c4f..b7a3f2d 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/setup.py +++ b/zaza/openstack/charm_tests/iscsi-connector/setup.py @@ -14,22 +14,9 @@ """Code for setting up iscsi-connector tests.""" -# """ -# steps for testing without password: -# - deploy ubuntu for storage -# - deploy ubuntu for initiator -# configure that unit: -# - install tgt -y -# - configure iscsi target -# - need to know the initiator ip -# - restart tgt -# - configure ubuntu initiator with init dict, target, port -# """ - import logging import zaza.model -import zaza.openstack.utilities.openstack as openstack_utils def basic_target_setup(): @@ -45,7 +32,9 @@ def basic_target_setup(): unit.entity_id, cmd) + def configure_iscsi_target(): + """Configure the iscsi target.""" lun = 'iqn.2020-07.canonical.com:lun1' backing_store = 'dev/vdb' initiator_address = zaza.model.get_app_ips('ubuntu')[0] @@ -54,13 +43,18 @@ def configure_iscsi_target(): username_in = 'iscsi-target' password_in = 'secretpass' write_file = ( - """echo -e '\n\tbacking-store {}\n\tinitiator-address {}\n\tincominguser {} {}\n\t""" - """outgoinguser {} {}\n' | sudo tee /etc/tgt/conf.d/iscsi.conf""".format(lun, backing_store, - initiator_address, username, password, username_in, password_in) + """echo -e '\n\tbacking-store {}\n\tinitiator-address """ + """{}\n\tincominguser {} {}\n\toutgoinguser {} {}\n' """ + """ | sudo tee /etc/tgt/conf.d/iscsi.conf""".format(lun, + backing_store, + initiator_address, + username, + password, + username_in, + password_in) ) logging.info('Writing target iscsi.conf') zaza.model.run_on_unit('ubuntu-target/0', write_file) # Restart tgt to load new config restart_tgt = "systemctl restart tgt" zaza.model.run_on_unit('ubuntu-target/0', restart_tgt) - diff --git a/zaza/openstack/charm_tests/iscsi-connector/tests.py b/zaza/openstack/charm_tests/iscsi-connector/tests.py index 89c1223..a703a88 100644 --- a/zaza/openstack/charm_tests/iscsi-connector/tests.py +++ b/zaza/openstack/charm_tests/iscsi-connector/tests.py @@ -16,11 +16,9 @@ import json import logging -import tempfile import zaza.model import zaza.openstack.charm_tests.test_utils as test_utils -import zaza.openstack.utilities.generic as generic_utils class ISCSIConnectorTest(test_utils.BaseCharmTest): @@ -32,10 +30,11 @@ class ISCSIConnectorTest(test_utils.BaseCharmTest): super(ISCSIConnectorTest, cls).setUpClass() def configure_iscsi_connector(self): + """Configure iscsi connector.""" iqn = 'iqn.2020-07.canonical.com:lun1' unit_fqdn = self.get_unit_full_hostname('ubuntu') target_ip = zaza.model.get_app_ips('ubuntu-target')[0] - initiator_dictionary = json.dumps({unit_fqdn:iqn}) + initiator_dictionary = json.dumps({unit_fqdn: iqn}) conf = { 'initiator-dictionary': initiator_dictionary, 'target': target_ip, @@ -56,13 +55,18 @@ class ISCSIConnectorTest(test_utils.BaseCharmTest): return hostname def test_iscsi_connector(self): + """Test iscsi configuration and wait for idle status.""" self.configure_iscsi_connector() logging.info('Wait for idle/ready status...') zaza.model.wait_for_application_states() - + def test_validate_iscsi_session(self): + """Validate that the iscsi session is active.""" unit = zaza.model.get_units('ubuntu')[0] logging.info('Checking if iscsi session is active.') run = zaza.model.run_on_unit(unit.entity_id, 'iscsiadm -m session') - logging.info('iscsiadm -m session: Stdout: {}, Stderr: {}, Code: {}'.format(run['Stdout'], run['Stderr'], run['Code'])) + logging.info("""iscsiadm -m session: Stdout: {}, Stderr: {}, """ + """Code: {}""".format(run['Stdout'], + run['Stderr'], + run['Code'])) assert run['Code'] == '0' From 8a47b981b9651706f1211912a81fb70379249869 Mon Sep 17 00:00:00 2001 From: David Ames Date: Thu, 6 Aug 2020 23:06:01 +0000 Subject: [PATCH 29/64] Tell horizon to use the "defaul" region SAML Mellon tests have been broken on Focal. The handling of the region selection behaves differently from bionic. Or it may be that bionic accidentally was working by seeing a string as int 0. Horizon has a "default" region which returns the keystone URL. --- zaza/openstack/charm_tests/saml_mellon/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/saml_mellon/tests.py b/zaza/openstack/charm_tests/saml_mellon/tests.py index 28ceb4e..f0f5d18 100644 --- a/zaza/openstack/charm_tests/saml_mellon/tests.py +++ b/zaza/openstack/charm_tests/saml_mellon/tests.py @@ -93,7 +93,7 @@ class CharmKeystoneSAMLMellonTest(BaseKeystoneTest): proto = "http" url = "{}://{}/horizon/auth/login/".format(proto, horizon_ip) - region = "{}://{}:5000/v3".format(proto, keystone_ip) + region = "default" horizon_expect = ('') From c498b358527a7bdacbfeeb5494af57407c74a402 Mon Sep 17 00:00:00 2001 From: David Ames Date: Thu, 6 Aug 2020 23:16:11 +0000 Subject: [PATCH 30/64] Lint fix --- zaza/openstack/charm_tests/saml_mellon/tests.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/zaza/openstack/charm_tests/saml_mellon/tests.py b/zaza/openstack/charm_tests/saml_mellon/tests.py index f0f5d18..7bb0abc 100644 --- a/zaza/openstack/charm_tests/saml_mellon/tests.py +++ b/zaza/openstack/charm_tests/saml_mellon/tests.py @@ -72,12 +72,6 @@ class CharmKeystoneSAMLMellonTest(BaseKeystoneTest): def test_saml_mellon_redirects(self): """Validate the horizon -> keystone -> IDP redirects.""" - if self.vip: - keystone_ip = self.vip - else: - unit = zaza.model.get_units(self.application_name)[0] - keystone_ip = unit.public_address - horizon = "openstack-dashboard" horizon_vip = (zaza.model.get_application_config(horizon) .get("vip").get("value")) From 7d4d40700aecbb5ed5aaa6f69f766eb0a00e7558 Mon Sep 17 00:00:00 2001 From: David Ames Date: Fri, 7 Aug 2020 01:12:37 +0000 Subject: [PATCH 31/64] Region is different for before and after Focal For the Horizon region setting use the Keystone URL prior to Focal and "default" thereafter. --- zaza/openstack/charm_tests/saml_mellon/tests.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/saml_mellon/tests.py b/zaza/openstack/charm_tests/saml_mellon/tests.py index 7bb0abc..9b5b211 100644 --- a/zaza/openstack/charm_tests/saml_mellon/tests.py +++ b/zaza/openstack/charm_tests/saml_mellon/tests.py @@ -21,6 +21,7 @@ import requests import zaza.model from zaza.openstack.charm_tests.keystone import BaseKeystoneTest import zaza.charm_lifecycle.utils as lifecycle_utils +import zaza.openstack.utilities.openstack as openstack_utils class FailedToReachIDP(Exception): @@ -42,6 +43,8 @@ class CharmKeystoneSAMLMellonTest(BaseKeystoneTest): cls.test_config = lifecycle_utils.get_charm_config() cls.application_name = cls.test_config['charm_name'] cls.action = "get-sp-metadata" + cls.current_release = openstack_utils.get_os_release() + cls.FOCAL_USSURI = openstack_utils.get_os_release("focal_ussuri") def test_run_get_sp_metadata_action(self): """Validate the get-sp-metadata action.""" @@ -72,6 +75,12 @@ class CharmKeystoneSAMLMellonTest(BaseKeystoneTest): def test_saml_mellon_redirects(self): """Validate the horizon -> keystone -> IDP redirects.""" + if self.vip: + keystone_ip = self.vip + else: + unit = zaza.model.get_units(self.application_name)[0] + keystone_ip = unit.public_address + horizon = "openstack-dashboard" horizon_vip = (zaza.model.get_application_config(horizon) .get("vip").get("value")) @@ -86,8 +95,13 @@ class CharmKeystoneSAMLMellonTest(BaseKeystoneTest): else: proto = "http" + # Use Keystone URL for < Focal + if self.current_release < self.FOCAL_USSURI: + region = "{}://{}:5000/v3".format(proto, keystone_ip) + else: + region = "default" + url = "{}://{}/horizon/auth/login/".format(proto, horizon_ip) - region = "default" horizon_expect = ('') From 80995ccf2341329b67507eba5063e4d3bb56a898 Mon Sep 17 00:00:00 2001 From: Chris MacNaughton Date: Mon, 10 Aug 2020 09:09:02 +0200 Subject: [PATCH 32/64] Vault tests should leave Vault unsealed When cleaning up after a Vault test case, Vault should be left in the same state we found it, unsealed. Closes-Bug: #379 --- zaza/openstack/charm_tests/vault/tests.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zaza/openstack/charm_tests/vault/tests.py b/zaza/openstack/charm_tests/vault/tests.py index 5c93046..40227fd 100644 --- a/zaza/openstack/charm_tests/vault/tests.py +++ b/zaza/openstack/charm_tests/vault/tests.py @@ -51,6 +51,10 @@ class BaseVaultTest(test_utils.OpenStackBaseTest): vault_utils.auth_all(cls.clients, cls.vault_creds['root_token']) vault_utils.ensure_secret_backend(cls.clients[0]) + def tearDown(self): + """Tun test cleanup for Vault tests.""" + vault_utils.unseal_all(self.clients, self.vault_creds['keys'][0]) + @contextlib.contextmanager def pause_resume(self, services, pgrep_full=False): """Override pause_resume for Vault behavior.""" From a3433a1276a6ca013a4c8ca7fb8a3e31eecb8ee9 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Mon, 10 Aug 2020 12:32:49 +0000 Subject: [PATCH 33/64] Add Ceph test CheckPoolTypes Add a ceph test to check that the type of pools requested by clients matches the pools that were created. --- zaza/openstack/charm_tests/ceph/tests.py | 44 ++++++++++++++++++++++++ zaza/openstack/utilities/ceph.py | 38 ++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/zaza/openstack/charm_tests/ceph/tests.py b/zaza/openstack/charm_tests/ceph/tests.py index 1200934..d20486f 100644 --- a/zaza/openstack/charm_tests/ceph/tests.py +++ b/zaza/openstack/charm_tests/ceph/tests.py @@ -794,6 +794,50 @@ class CephPrometheusTest(unittest.TestCase): '3', _get_mon_count_from_prometheus(unit.public_address)) +class CephPoolConfig(Exception): + """Custom Exception for bad Ceph pool config.""" + + pass + + +class CheckPoolTypes(unittest.TestCase): + """Test the ceph pools created for clients are of the expected type.""" + + def test_check_pool_types(self): + """Check type of pools created for clients.""" + app_pools = [ + ('glance', 'glance'), + ('nova-compute', 'nova'), + ('cinder-ceph', 'cinder-ceph')] + runtime_pool_details = zaza_ceph.get_ceph_pool_details() + for app, pool_name in app_pools: + juju_pool_config = zaza_model.get_application_config(app).get( + 'pool-type') + if juju_pool_config: + expected_pool_type = juju_pool_config['value'] + else: + # If the pool-type option is absent assume the default of + # replicated. + expected_pool_type = zaza_ceph.REPLICATED_POOL_TYPE + for pool_config in runtime_pool_details: + if pool_config['pool_name'] == pool_name: + logging.info('Checking {} is {}'.format( + pool_name, + expected_pool_type)) + expected_pool_code = -1 + if expected_pool_type == zaza_ceph.REPLICATED_POOL_TYPE: + expected_pool_code = zaza_ceph.REPLICATED_POOL_CODE + elif expected_pool_type == zaza_ceph.ERASURE_POOL_TYPE: + expected_pool_code = zaza_ceph.ERASURE_POOL_CODE + self.assertEqual( + pool_config['type'], + expected_pool_code) + break + else: + raise CephPoolConfig( + "Failed to find config for {}".format(pool_name)) + + # NOTE: We might query before prometheus has fetch data @tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, min=5, max=10), diff --git a/zaza/openstack/utilities/ceph.py b/zaza/openstack/utilities/ceph.py index 61d7d66..893ea5e 100644 --- a/zaza/openstack/utilities/ceph.py +++ b/zaza/openstack/utilities/ceph.py @@ -5,6 +5,11 @@ import logging import zaza.openstack.utilities.openstack as openstack_utils import zaza.model as zaza_model +REPLICATED_POOL_TYPE = 'replicated' +ERASURE_POOL_TYPE = 'erasure-coded' +REPLICATED_POOL_CODE = 1 +ERASURE_POOL_CODE = 3 + def get_expected_pools(radosgw=False): """Get expected ceph pools. @@ -97,6 +102,39 @@ def get_ceph_pools(unit_name, model_name=None): return pools +def get_ceph_pool_details(query_leader=True, unit_name=None, model_name=None): + """Get ceph pool details. + + Return a list of ceph pools details dicts. + + :param query_leader: Whether to query the leader for pool details. + :type query_leader: bool + :param unit_name: Name of unit to get the pools on if query_leader is False + :type unit_name: string + :param model_name: Name of model to operate in + :type model_name: str + :returns: Dict of ceph pools + :rtype: List[Dict,] + :raise: zaza_model.CommandRunFailed + """ + cmd = 'sudo ceph osd pool ls detail -f json' + if query_leader and unit_name: + raise ValueError("Cannot set query_leader and unit_name") + if query_leader: + result = zaza_model.run_on_leader( + 'ceph-mon', + cmd, + model_name=model_name) + else: + result = zaza_model.run_on_unit( + unit_name, + cmd, + model_name=model_name) + if int(result.get('Code')) != 0: + raise zaza_model.CommandRunFailed(cmd, result) + return json.loads(result.get('Stdout')) + + def get_ceph_df(unit_name, model_name=None): """Return dict of ceph df json output, including ceph pool state. From 3343582ddd80dc71c5f25d3bf140dc3b06957943 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Mon, 17 Aug 2020 17:32:22 -0500 Subject: [PATCH 34/64] add test for external cert config option for gnocchi --- zaza/openstack/charm_tests/gnocchi/tests.py | 38 ++++++++++ .../charm_tests/iscsi-connector/__init__.py | 15 ---- .../charm_tests/iscsi-connector/setup.py | 60 ---------------- .../charm_tests/iscsi-connector/tests.py | 72 ------------------- 4 files changed, 38 insertions(+), 147 deletions(-) delete mode 100644 zaza/openstack/charm_tests/iscsi-connector/__init__.py delete mode 100644 zaza/openstack/charm_tests/iscsi-connector/setup.py delete mode 100644 zaza/openstack/charm_tests/iscsi-connector/tests.py diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index 680bf3c..93051d5 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -16,12 +16,15 @@ """Encapsulate Gnocchi testing.""" +import base64 import boto3 import logging import pprint from gnocchiclient.v1 import client as gnocchi_client +import zaza.model as model import zaza.openstack.charm_tests.test_utils as test_utils +import zaza.openstack.utilities as utilities import zaza.openstack.utilities.openstack as openstack_utils @@ -89,6 +92,41 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): # Create AWS compatible application credentials in Keystone cls.ec2_creds = ks_client.ec2.create(user_id, project_id) + def test_upload_external_cert(self): + """Verify that the external CA is uploaded correctly.""" + logging.info('Changing value for trusted-external-ca-cert.') + ca_cert_option = 'trusted-external-ca-cert' + ppk, cert = utilities.cert.generate_cert('gnocchi_test.ci.local') + b64_cert = base64.b64encode(cert).decode() + config = { + ca_cert_option: b64_cert, + } + model.set_application_config( + 'gnocchi', + config + ) + model.block_until_all_units_idle() + + def test_validate_cert_location(self): + """Validate that the cert is correctly uploaded to the unit.""" + cert_location = '/usr/local/share/ca-certificates' + cert_name = 'gnocchi-external.crt' + cmd = 'ls ' + cert_location + '/' + cert_name + logging.info("Validating that the file {} is created in \ + {}".format(cert_name, cert_location)) + result = model.run_on_unit('gnocchi/0', cmd) + self.assertEqual(result['Code'], '0') + + def test_validate_update_ca_certificates(self): + """Validate that /usr/sbin/update-ca-certificates ran successfully.""" + linked_cert_location = '/etc/ssl/certs' + cert_name = 'gnocchi-external.pem' + cmd = 'ls ' + linked_cert_location + '/' + cert_name + logging.info("Validating that the link {} is created in \ + {}".format(cert_name, linked_cert_location)) + result = model.run_on_unit('gnocchi/0', cmd) + self.assertEqual(result['Code'], '0') + def test_s3_list_gnocchi_buckets(self): """Verify that the gnocchi buckets were created in the S3 backend.""" kwargs = { diff --git a/zaza/openstack/charm_tests/iscsi-connector/__init__.py b/zaza/openstack/charm_tests/iscsi-connector/__init__.py deleted file mode 100644 index 659b2b0..0000000 --- a/zaza/openstack/charm_tests/iscsi-connector/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# 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 testing iscsi-connector charm.""" diff --git a/zaza/openstack/charm_tests/iscsi-connector/setup.py b/zaza/openstack/charm_tests/iscsi-connector/setup.py deleted file mode 100644 index b7a3f2d..0000000 --- a/zaza/openstack/charm_tests/iscsi-connector/setup.py +++ /dev/null @@ -1,60 +0,0 @@ -# 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 setting up iscsi-connector tests.""" - -import logging - -import zaza.model - - -def basic_target_setup(): - """Run basic setup for iscsi guest.""" - logging.info('Installing package tgt on ubuntu-target') - unit = zaza.model.get_units('ubuntu-target')[0] - setup_cmds = [ - "apt install --yes tgt", - "systemctl start tgt", - "open-port 3260"] - for cmd in setup_cmds: - zaza.model.run_on_unit( - unit.entity_id, - cmd) - - -def configure_iscsi_target(): - """Configure the iscsi target.""" - lun = 'iqn.2020-07.canonical.com:lun1' - backing_store = 'dev/vdb' - initiator_address = zaza.model.get_app_ips('ubuntu')[0] - username = 'iscsi-user' - password = 'password123' - username_in = 'iscsi-target' - password_in = 'secretpass' - write_file = ( - """echo -e '\n\tbacking-store {}\n\tinitiator-address """ - """{}\n\tincominguser {} {}\n\toutgoinguser {} {}\n' """ - """ | sudo tee /etc/tgt/conf.d/iscsi.conf""".format(lun, - backing_store, - initiator_address, - username, - password, - username_in, - password_in) - ) - logging.info('Writing target iscsi.conf') - zaza.model.run_on_unit('ubuntu-target/0', write_file) - # Restart tgt to load new config - restart_tgt = "systemctl restart tgt" - zaza.model.run_on_unit('ubuntu-target/0', restart_tgt) diff --git a/zaza/openstack/charm_tests/iscsi-connector/tests.py b/zaza/openstack/charm_tests/iscsi-connector/tests.py deleted file mode 100644 index a703a88..0000000 --- a/zaza/openstack/charm_tests/iscsi-connector/tests.py +++ /dev/null @@ -1,72 +0,0 @@ -# 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. - -"""Encapsulate iscsi-connector testing.""" - -import json -import logging - -import zaza.model -import zaza.openstack.charm_tests.test_utils as test_utils - - -class ISCSIConnectorTest(test_utils.BaseCharmTest): - """Class for iscsi-connector tests.""" - - @classmethod - def setUpClass(cls): - """Run class setup for running glance tests.""" - super(ISCSIConnectorTest, cls).setUpClass() - - def configure_iscsi_connector(self): - """Configure iscsi connector.""" - iqn = 'iqn.2020-07.canonical.com:lun1' - unit_fqdn = self.get_unit_full_hostname('ubuntu') - target_ip = zaza.model.get_app_ips('ubuntu-target')[0] - initiator_dictionary = json.dumps({unit_fqdn: iqn}) - conf = { - 'initiator-dictionary': initiator_dictionary, - 'target': target_ip, - 'port': '3260', - 'iscsi-node-session-auth-authmethod': 'CHAP', - 'iscsi-node-session-auth-username': 'iscsi-user', - 'iscsi-node-session-auth-password': 'password123', - 'iscsi-node-session-auth-username-in': 'iscsi-target', - 'iscsi-node-session-auth-password-in': 'secretpass', - } - zaza.model.set_application_config('iscsi-connector', conf) - - def get_unit_full_hostname(self, unit_name): - """Retrieve the full hostname of a unit.""" - for unit in zaza.model.get_units(unit_name): - result = zaza.model.run_on_unit(unit.entity_id, 'hostname -f') - hostname = result['Stdout'].rstrip() - return hostname - - def test_iscsi_connector(self): - """Test iscsi configuration and wait for idle status.""" - self.configure_iscsi_connector() - logging.info('Wait for idle/ready status...') - zaza.model.wait_for_application_states() - - def test_validate_iscsi_session(self): - """Validate that the iscsi session is active.""" - unit = zaza.model.get_units('ubuntu')[0] - logging.info('Checking if iscsi session is active.') - run = zaza.model.run_on_unit(unit.entity_id, 'iscsiadm -m session') - logging.info("""iscsiadm -m session: Stdout: {}, Stderr: {}, """ - """Code: {}""".format(run['Stdout'], - run['Stderr'], - run['Code'])) - assert run['Code'] == '0' From a67b4906eef7e199b2a0949f6723a6d64097f670 Mon Sep 17 00:00:00 2001 From: coreycb Date: Tue, 18 Aug 2020 09:54:09 -0400 Subject: [PATCH 35/64] Use juju model as workspace and store in home (#383) * Use juju model as workspace and store in home The current juju model will now be used as the tempest workspace name. Additionally, all workspaces will be stored in ~/.tempest/. This patch also introduces a new option 'keep-workspace' that can be specified along with other tempest options to keep the workspace after tempest test execution. It defaults to False. Also minor adjustment to smoke option to test boolean value. --- zaza/openstack/charm_tests/tempest/setup.py | 19 +++--- zaza/openstack/charm_tests/tempest/tests.py | 20 ++++-- zaza/openstack/charm_tests/tempest/utils.py | 67 +++++++++++++++++++++ 3 files changed, 88 insertions(+), 18 deletions(-) create mode 100644 zaza/openstack/charm_tests/tempest/utils.py diff --git a/zaza/openstack/charm_tests/tempest/setup.py b/zaza/openstack/charm_tests/tempest/setup.py index af1ee4c..7ce4478 100644 --- a/zaza/openstack/charm_tests/tempest/setup.py +++ b/zaza/openstack/charm_tests/tempest/setup.py @@ -16,11 +16,12 @@ import jinja2 import urllib.parse -import subprocess +import os 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.tempest.utils as tempest_utils import zaza.openstack.charm_tests.glance.setup as glance_setup SETUP_ENV_VARS = { @@ -279,21 +280,15 @@ def setup_tempest(tempest_template, accounts_template): :returns: None :rtype: None """ - 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 + workspace_name, workspace_path = tempest_utils.get_workspace() + tempest_utils.destroy_workspace(workspace_name, workspace_path) + tempest_utils.init_workspace(workspace_path) render_tempest_config( - 'tempest-workspace/etc/tempest.conf', + os.path.join(workspace_path, 'etc/tempest.conf'), get_tempest_context(), tempest_template) render_tempest_config( - 'tempest-workspace/etc/accounts.yaml', + os.path.join(workspace_path, 'etc/accounts.yaml'), get_tempest_context(), accounts_template) diff --git a/zaza/openstack/charm_tests/tempest/tests.py b/zaza/openstack/charm_tests/tempest/tests.py index d726930..979caff 100644 --- a/zaza/openstack/charm_tests/tempest/tests.py +++ b/zaza/openstack/charm_tests/tempest/tests.py @@ -20,6 +20,7 @@ import subprocess import zaza import zaza.charm_lifecycle.utils import zaza.charm_lifecycle.test +import zaza.openstack.charm_tests.tempest.utils as tempest_utils import tempfile @@ -33,21 +34,24 @@ class TempestTest(): 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). + (list of tests), regex (list of regex's), and keep-workspace (bool). :returns: Status of tempest run :rtype: bool """ + result = True charm_config = zaza.charm_lifecycle.utils.get_charm_config() + workspace_name, workspace_path = tempest_utils.get_workspace() tempest_options = ['tempest', 'run', '--workspace', - 'tempest-workspace', '--config', - 'tempest-workspace/etc/tempest.conf'] + workspace_name, '--config', + os.path.join(workspace_path, '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'): + smoke = config.get('smoke') + if smoke and smoke is True: tempest_options.extend(['--smoke']) if config.get('regex'): tempest_options.extend( @@ -74,5 +78,9 @@ class TempestTest(): try: subprocess.check_call(tempest_options) except subprocess.CalledProcessError: - return False - return True + result = False + break + keep_workspace = config.get('keep-workspace') + if not keep_workspace or keep_workspace is not True: + tempest_utils.destroy_workspace(workspace_name, workspace_path) + return result diff --git a/zaza/openstack/charm_tests/tempest/utils.py b/zaza/openstack/charm_tests/tempest/utils.py new file mode 100644 index 0000000..8f3aea7 --- /dev/null +++ b/zaza/openstack/charm_tests/tempest/utils.py @@ -0,0 +1,67 @@ +# 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. + +"""Utility code for working with tempest workspaces.""" + +import os +from pathlib import Path +import shutil +import subprocess + +import zaza.model as model + + +def get_workspace(): + """Get tempest workspace name and path. + + :returns: A tuple containing tempest workspace name and workspace path + :rtype: Tuple[str, str] + """ + home = str(Path.home()) + workspace_name = model.get_juju_model() + workspace_path = os.path.join(home, '.tempest', workspace_name) + return (workspace_name, workspace_path) + + +def destroy_workspace(workspace_name, workspace_path): + """Delete tempest workspace. + + :param workspace_name: name of workspace + :type workspace_name: str + :param workspace_path: directory path where workspace is stored + :type workspace_path: str + :returns: None + :rtype: None + """ + try: + subprocess.check_call(['tempest', 'workspace', 'remove', '--rmdir', + '--name', workspace_name]) + except subprocess.CalledProcessError: + pass + if os.path.isdir(workspace_path): + shutil.rmtree(workspace_path) + + +def init_workspace(workspace_path): + """Initialize tempest workspace. + + :param workspace_path: directory path where workspace is stored + :type workspace_path: str + :returns: None + :rtype: None + """ + try: + subprocess.check_call(['tempest', 'init', workspace_path]) + except subprocess.CalledProcessError: + pass From 8a79a93e8305c31926d317f2aaea362811695a50 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Wed, 19 Aug 2020 08:59:04 -0500 Subject: [PATCH 36/64] combine 3 functions into one --- zaza/openstack/charm_tests/gnocchi/tests.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index 93051d5..3487996 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -107,8 +107,7 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): ) model.block_until_all_units_idle() - def test_validate_cert_location(self): - """Validate that the cert is correctly uploaded to the unit.""" + logging.info('Validate that the cert is correctly uploaded to the unit') cert_location = '/usr/local/share/ca-certificates' cert_name = 'gnocchi-external.crt' cmd = 'ls ' + cert_location + '/' + cert_name @@ -117,13 +116,12 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): result = model.run_on_unit('gnocchi/0', cmd) self.assertEqual(result['Code'], '0') - def test_validate_update_ca_certificates(self): - """Validate that /usr/sbin/update-ca-certificates ran successfully.""" + logging.info('Validate that /usr/sbin/update-ca-certificates ran successfully') linked_cert_location = '/etc/ssl/certs' - cert_name = 'gnocchi-external.pem' - cmd = 'ls ' + linked_cert_location + '/' + cert_name + linked_cert_name = 'gnocchi-external.pem' + cmd = 'ls ' + linked_cert_location + '/' + linked_cert_name logging.info("Validating that the link {} is created in \ - {}".format(cert_name, linked_cert_location)) + {}".format(linked_cert_name, linked_cert_location)) result = model.run_on_unit('gnocchi/0', cmd) self.assertEqual(result['Code'], '0') From cd548b01cfd470983f1a6f49748be3297c7e9b7d Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Wed, 19 Aug 2020 09:32:09 -0500 Subject: [PATCH 37/64] pep8, duplicate logs --- zaza/openstack/charm_tests/gnocchi/tests.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index 3487996..e3b9ae1 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -107,7 +107,6 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): ) model.block_until_all_units_idle() - logging.info('Validate that the cert is correctly uploaded to the unit') cert_location = '/usr/local/share/ca-certificates' cert_name = 'gnocchi-external.crt' cmd = 'ls ' + cert_location + '/' + cert_name @@ -116,7 +115,6 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): result = model.run_on_unit('gnocchi/0', cmd) self.assertEqual(result['Code'], '0') - logging.info('Validate that /usr/sbin/update-ca-certificates ran successfully') linked_cert_location = '/etc/ssl/certs' linked_cert_name = 'gnocchi-external.pem' cmd = 'ls ' + linked_cert_location + '/' + linked_cert_name From c5c964506e18c92cca8bea5b47e9981ae8d17e67 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Wed, 19 Aug 2020 16:43:04 +0200 Subject: [PATCH 38/64] Increase neutron-arista timeout --- zaza/openstack/charm_tests/neutron_arista/tests.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/zaza/openstack/charm_tests/neutron_arista/tests.py b/zaza/openstack/charm_tests/neutron_arista/tests.py index 354dfd1..f9eb2fb 100644 --- a/zaza/openstack/charm_tests/neutron_arista/tests.py +++ b/zaza/openstack/charm_tests/neutron_arista/tests.py @@ -38,9 +38,10 @@ class NeutronCreateAristaNetworkTest(neutron_tests.NeutronCreateNetworkTest): # Sometimes the API call from Neutron to Arista fails and Neutron # retries a couple of seconds later, which is why the newly created # test network may not be immediately visible on Arista's API. + # NOTE(lourot): I experienced a run where it took 53 seconds. for attempt in tenacity.Retrying( wait=tenacity.wait_fixed(10), # seconds - stop=tenacity.stop_after_attempt(3), + stop=tenacity.stop_after_attempt(12), reraise=True): with attempt: actual_network_names = arista_utils.query_fixture_networks( @@ -56,7 +57,7 @@ class NeutronCreateAristaNetworkTest(neutron_tests.NeutronCreateNetworkTest): for attempt in tenacity.Retrying( wait=tenacity.wait_fixed(10), # seconds - stop=tenacity.stop_after_attempt(3), + stop=tenacity.stop_after_attempt(12), reraise=True): with attempt: actual_network_names = arista_utils.query_fixture_networks( From 5ad5f85f99958bfbefacc184a90d10b0d4f19a5c Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Wed, 19 Aug 2020 20:47:00 +0100 Subject: [PATCH 39/64] Fix the charm-upgrade tests as used by CoT The biggest change is that the upgrade_groups() is a list of tuples ... [()] ... rathern than a collections.OrderedDict(). This tends to make more sense as they are processed in order and don't actually need to be indexed. Also excludes easyrsa (as it's not an upgrade target) and adds a "Database Services" which upgrades mysql or percona FIRST before moving on to rabbitmq and other stateful services. This is because some charms really need to talk to mysql if one of the other stateful services does a relation changed hook. This makes it more likely that the system will ugprade correctly. --- .../test_zaza_utilities_upgrade_utils.py | 15 +++-- .../charm_tests/charm_upgrade/tests.py | 7 ++- .../series_upgrade/parallel_tests.py | 7 ++- .../charm_tests/series_upgrade/tests.py | 23 ++------ zaza/openstack/utilities/upgrade_utils.py | 57 +++++++++++++------ 5 files changed, 64 insertions(+), 45 deletions(-) diff --git a/unit_tests/utilities/test_zaza_utilities_upgrade_utils.py b/unit_tests/utilities/test_zaza_utilities_upgrade_utils.py index 814772e..e038daf 100644 --- a/unit_tests/utilities/test_zaza_utilities_upgrade_utils.py +++ b/unit_tests/utilities/test_zaza_utilities_upgrade_utils.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import collections import copy import mock import pprint @@ -89,12 +88,14 @@ class TestUpgradeUtils(ut_utils.BaseTestCase): expected) def test_get_upgrade_groups(self): - expected = collections.OrderedDict([ + # expected = collections.OrderedDict([ + expected = [ + ('Database Services', []), ('Stateful Services', []), ('Core Identity', []), ('Control Plane', ['cinder']), ('Data Plane', ['nova-compute']), - ('sweep_up', [])]) + ('sweep_up', [])] actual = openstack_upgrade.get_upgrade_groups() pprint.pprint(expected) pprint.pprint(actual) @@ -103,12 +104,14 @@ class TestUpgradeUtils(ut_utils.BaseTestCase): expected) def test_get_series_upgrade_groups(self): - expected = collections.OrderedDict([ - ('Stateful Services', ['mydb']), + # expected = collections.OrderedDict([ + expected = [ + ('Database Services', ['mydb']), + ('Stateful Services', []), ('Core Identity', []), ('Control Plane', ['cinder']), ('Data Plane', ['nova-compute']), - ('sweep_up', ['ntp'])]) + ('sweep_up', ['ntp'])] actual = openstack_upgrade.get_series_upgrade_groups() pprint.pprint(expected) pprint.pprint(actual) diff --git a/zaza/openstack/charm_tests/charm_upgrade/tests.py b/zaza/openstack/charm_tests/charm_upgrade/tests.py index 0ffd30b..f5f301a 100644 --- a/zaza/openstack/charm_tests/charm_upgrade/tests.py +++ b/zaza/openstack/charm_tests/charm_upgrade/tests.py @@ -53,8 +53,11 @@ class FullCloudCharmUpgradeTest(unittest.TestCase): """Run charm upgrade.""" self.lts.test_launch_small_instance() applications = zaza.model.get_status().applications - groups = upgrade_utils.get_charm_upgrade_groups() - for group_name, group in groups.items(): + groups = upgrade_utils.get_charm_upgrade_groups( + extra_filters=[upgrade_utils._filter_etcd, + upgrade_utils._filter_easyrsa, + upgrade_utils._filter_memcached]) + for group_name, group in groups: logging.info("About to upgrade {} ({})".format(group_name, group)) for application, app_details in applications.items(): if application not in group: diff --git a/zaza/openstack/charm_tests/series_upgrade/parallel_tests.py b/zaza/openstack/charm_tests/series_upgrade/parallel_tests.py index 6408cf0..a49eae6 100644 --- a/zaza/openstack/charm_tests/series_upgrade/parallel_tests.py +++ b/zaza/openstack/charm_tests/series_upgrade/parallel_tests.py @@ -73,11 +73,14 @@ class ParallelSeriesUpgradeTest(unittest.TestCase): workaround_script = None files = [] applications = model.get_status().applications - for group_name, apps in upgrade_groups.items(): + for group_name, apps in upgrade_groups: logging.info("About to upgrade {} from {} to {}".format( group_name, from_series, to_series)) upgrade_functions = [] - if group_name in ["Stateful Services", "Data Plane", "sweep_up"]: + if group_name in ["Database Services", + "Stateful Services", + "Data Plane", + "sweep_up"]: logging.info("Going to upgrade {} unit by unit".format(apps)) upgrade_function = \ parallel_series_upgrade.serial_series_upgrade diff --git a/zaza/openstack/charm_tests/series_upgrade/tests.py b/zaza/openstack/charm_tests/series_upgrade/tests.py index 9e72338..4b06dbb 100644 --- a/zaza/openstack/charm_tests/series_upgrade/tests.py +++ b/zaza/openstack/charm_tests/series_upgrade/tests.py @@ -30,22 +30,6 @@ from zaza.openstack.utilities import ( from zaza.openstack.charm_tests.nova.tests import LTSGuestCreateTest -def _filter_easyrsa(app, app_config, model_name=None): - charm_name = upgrade_utils.extract_charm_name_from_url(app_config['charm']) - if "easyrsa" in charm_name: - logging.warn("Skipping series upgrade of easyrsa Bug #1850121") - return True - return False - - -def _filter_etcd(app, app_config, model_name=None): - charm_name = upgrade_utils.extract_charm_name_from_url(app_config['charm']) - if "etcd" in charm_name: - logging.warn("Skipping series upgrade of easyrsa Bug #1850124") - return True - return False - - class SeriesUpgradeTest(unittest.TestCase): """Class to encapsulate Series Upgrade Tests.""" @@ -75,7 +59,7 @@ class SeriesUpgradeTest(unittest.TestCase): continue if "etcd" in app_details["charm"]: logging.warn( - "Skipping series upgrade of easyrsa Bug #1850124") + "Skipping series upgrade of etcd Bug #1850124") continue charm_name = upgrade_utils.extract_charm_name_from_url( app_details['charm']) @@ -208,10 +192,11 @@ class ParallelSeriesUpgradeTest(unittest.TestCase): # Set Feature Flag os.environ["JUJU_DEV_FEATURE_FLAGS"] = "upgrade-series" upgrade_groups = upgrade_utils.get_series_upgrade_groups( - extra_filters=[_filter_etcd, _filter_easyrsa]) + extra_filters=[upgrade_utils._filter_etcd, + upgrade_utils._filter_easyrsa]) applications = model.get_status().applications completed_machines = [] - for group_name, group in upgrade_groups.items(): + for group_name, group in upgrade_groups: logging.warn("About to upgrade {} ({})".format(group_name, group)) upgrade_group = [] for application, app_details in applications.items(): diff --git a/zaza/openstack/utilities/upgrade_utils.py b/zaza/openstack/utilities/upgrade_utils.py index b134a69..995e0bb 100644 --- a/zaza/openstack/utilities/upgrade_utils.py +++ b/zaza/openstack/utilities/upgrade_utils.py @@ -13,15 +13,17 @@ # limitations under the License. """Collection of functions to support upgrade testing.""" -import re + +import itertools import logging -import collections +import re + import zaza.model -SERVICE_GROUPS = collections.OrderedDict([ - ('Stateful Services', ['percona-cluster', 'rabbitmq-server', 'ceph-mon', - 'mysql-innodb-cluster']), +SERVICE_GROUPS = ( + ('Database Services', ['percona-cluster', 'mysql-innodb-cluster']), + ('Stateful Services', ['rabbitmq-server', 'ceph-mon']), ('Core Identity', ['keystone']), ('Control Plane', [ 'aodh', 'barbican', 'ceilometer', 'ceph-fs', @@ -31,8 +33,7 @@ SERVICE_GROUPS = collections.OrderedDict([ 'nova-cloud-controller', 'openstack-dashboard']), ('Data Plane', [ 'nova-compute', 'ceph-osd', - 'swift-proxy', 'swift-storage']) -]) + 'swift-proxy', 'swift-storage'])) UPGRADE_EXCLUDE_LIST = ['rabbitmq-server', 'percona-cluster'] @@ -106,6 +107,30 @@ def _apply_extra_filters(filters, extra_filters): return filters +def _filter_easyrsa(app, app_config, model_name=None): + charm_name = extract_charm_name_from_url(app_config['charm']) + if "easyrsa" in charm_name: + logging.warn("Skipping upgrade of easyrsa Bug #1850121") + return True + return False + + +def _filter_etcd(app, app_config, model_name=None): + charm_name = extract_charm_name_from_url(app_config['charm']) + if "etcd" in charm_name: + logging.warn("Skipping upgrade of easyrsa Bug #1850124") + return True + return False + + +def _filter_memcached(app, app_config, model_name=None): + charm_name = extract_charm_name_from_url(app_config['charm']) + if "memcached" in charm_name: + logging.warn("Skipping upgrade of memcached charm") + return True + return False + + def get_upgrade_groups(model_name=None, extra_filters=None): """Place apps in the model into their upgrade groups. @@ -170,21 +195,21 @@ def get_charm_upgrade_groups(model_name=None, extra_filters=None): def _build_service_groups(applications): - groups = collections.OrderedDict() - for phase_name, charms in SERVICE_GROUPS.items(): + groups = [] + for phase_name, charms in SERVICE_GROUPS: group = [] for app, app_config in applications.items(): charm_name = extract_charm_name_from_url(app_config['charm']) if charm_name in charms: group.append(app) - groups[phase_name] = group + groups.append((phase_name, group)) - sweep_up = [] - for app in applications: - if not (app in [a for group in groups.values() for a in group]): - sweep_up.append(app) - groups['sweep_up'] = sweep_up - for name, group in groups.items(): + # collect all the values into a list, and then a lookup hash + values = list(itertools.chain(*(ls for _, ls in groups))) + vhash = {v: 1 for v in values} + sweep_up = [app for app in applications if app not in vhash] + groups.append(('sweep_up', sweep_up)) + for name, group in groups: group.sort() return groups From 5455bee2e968124cce5ab726d32fa491011fb28f Mon Sep 17 00:00:00 2001 From: Chris MacNaughton Date: Thu, 20 Aug 2020 08:51:57 +0200 Subject: [PATCH 40/64] Fix protocol rendering for keystone URL Closes #387 --- zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 b/zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 index f5505ad..83a05ca 100644 --- a/zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 +++ b/zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 @@ -34,7 +34,7 @@ attach_encrypted_volume = false {% if 'keystone' in enabled_services %} [identity] -uri = {proto}://{{ keystone }}:5000/v2.0 +uri = {{ proto }}://{{ keystone }}:5000/v2.0 auth_version = v2 admin_role = Admin region = RegionOne From 056e29cbdc71850ba8a50341c146279cf9fd5997 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 20 Aug 2020 11:15:16 +0100 Subject: [PATCH 41/64] Remove commented out code sections These were left over from the refactor. --- unit_tests/utilities/test_zaza_utilities_upgrade_utils.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/unit_tests/utilities/test_zaza_utilities_upgrade_utils.py b/unit_tests/utilities/test_zaza_utilities_upgrade_utils.py index e038daf..f42ba5a 100644 --- a/unit_tests/utilities/test_zaza_utilities_upgrade_utils.py +++ b/unit_tests/utilities/test_zaza_utilities_upgrade_utils.py @@ -88,7 +88,6 @@ class TestUpgradeUtils(ut_utils.BaseTestCase): expected) def test_get_upgrade_groups(self): - # expected = collections.OrderedDict([ expected = [ ('Database Services', []), ('Stateful Services', []), @@ -104,7 +103,6 @@ class TestUpgradeUtils(ut_utils.BaseTestCase): expected) def test_get_series_upgrade_groups(self): - # expected = collections.OrderedDict([ expected = [ ('Database Services', ['mydb']), ('Stateful Services', []), From 6a18a37c13bec181375ffa541d1a396652f9431b Mon Sep 17 00:00:00 2001 From: Chris MacNaughton Date: Thu, 20 Aug 2020 17:08:33 +0200 Subject: [PATCH 42/64] Ensure that the workspace deletion doesn't fail with file not found. --- zaza/openstack/charm_tests/tempest/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/tempest/utils.py b/zaza/openstack/charm_tests/tempest/utils.py index 8f3aea7..52ae126 100644 --- a/zaza/openstack/charm_tests/tempest/utils.py +++ b/zaza/openstack/charm_tests/tempest/utils.py @@ -47,7 +47,7 @@ def destroy_workspace(workspace_name, workspace_path): try: subprocess.check_call(['tempest', 'workspace', 'remove', '--rmdir', '--name', workspace_name]) - except subprocess.CalledProcessError: + except (subprocess.CalledProcessError, FileNotFoundError): pass if os.path.isdir(workspace_path): shutil.rmtree(workspace_path) From d7bc2d32986ebe1718f7e2e3f4d8981a9c71c80c Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Thu, 20 Aug 2020 17:22:31 +0200 Subject: [PATCH 43/64] Revert "Merge pull request #384 from camille-rodriguez/master" This reverts commit 73bab8395bb793578e3992cf4d2a31e89d642791, reversing changes made to c165c42c0030269ea62feda7a6471985e0eb172c. This change introduced a new test under an existing test class, which is a breaking change and prevents us to keep testing older releases. This new test will be soon re-introduced under a new test class. --- zaza/openstack/charm_tests/gnocchi/tests.py | 34 --------------------- 1 file changed, 34 deletions(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index e3b9ae1..680bf3c 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -16,15 +16,12 @@ """Encapsulate Gnocchi testing.""" -import base64 import boto3 import logging import pprint from gnocchiclient.v1 import client as gnocchi_client -import zaza.model as model import zaza.openstack.charm_tests.test_utils as test_utils -import zaza.openstack.utilities as utilities import zaza.openstack.utilities.openstack as openstack_utils @@ -92,37 +89,6 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): # Create AWS compatible application credentials in Keystone cls.ec2_creds = ks_client.ec2.create(user_id, project_id) - def test_upload_external_cert(self): - """Verify that the external CA is uploaded correctly.""" - logging.info('Changing value for trusted-external-ca-cert.') - ca_cert_option = 'trusted-external-ca-cert' - ppk, cert = utilities.cert.generate_cert('gnocchi_test.ci.local') - b64_cert = base64.b64encode(cert).decode() - config = { - ca_cert_option: b64_cert, - } - model.set_application_config( - 'gnocchi', - config - ) - model.block_until_all_units_idle() - - cert_location = '/usr/local/share/ca-certificates' - cert_name = 'gnocchi-external.crt' - cmd = 'ls ' + cert_location + '/' + cert_name - logging.info("Validating that the file {} is created in \ - {}".format(cert_name, cert_location)) - result = model.run_on_unit('gnocchi/0', cmd) - self.assertEqual(result['Code'], '0') - - linked_cert_location = '/etc/ssl/certs' - linked_cert_name = 'gnocchi-external.pem' - cmd = 'ls ' + linked_cert_location + '/' + linked_cert_name - logging.info("Validating that the link {} is created in \ - {}".format(linked_cert_name, linked_cert_location)) - result = model.run_on_unit('gnocchi/0', cmd) - self.assertEqual(result['Code'], '0') - def test_s3_list_gnocchi_buckets(self): """Verify that the gnocchi buckets were created in the S3 backend.""" kwargs = { From cf04bbfc7dec010bac9e08126d3b176594ea47ed Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Thu, 20 Aug 2020 11:02:07 -0500 Subject: [PATCH 44/64] move new test in separate class --- zaza/openstack/charm_tests/gnocchi/tests.py | 56 +++++++++++---------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index e3b9ae1..808f374 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -65,32 +65,8 @@ class GnocchiTest(test_utils.OpenStackBaseTest): logging.info("Testing pause and resume") -class GnocchiS3Test(test_utils.OpenStackBaseTest): - """Test Gnocchi for S3 storage backend.""" - - @classmethod - def setUpClass(cls): - """Run class setup for running tests.""" - super(GnocchiS3Test, cls).setUpClass() - - session = openstack_utils.get_overcloud_keystone_session() - ks_client = openstack_utils.get_keystone_session_client(session) - - # Get token data so we can glean our user_id and project_id - token_data = ks_client.tokens.get_token_data(session.get_token()) - project_id = token_data['token']['project']['id'] - user_id = token_data['token']['user']['id'] - - # Store URL to service providing S3 compatible API - for entry in token_data['token']['catalog']: - if entry['type'] == 's3': - for endpoint in entry['endpoints']: - if endpoint['interface'] == 'public': - cls.s3_region = endpoint['region'] - cls.s3_endpoint = endpoint['url'] - - # Create AWS compatible application credentials in Keystone - cls.ec2_creds = ks_client.ec2.create(user_id, project_id) +class GnocchiExternalCATest(test_utils.OpenStackBaseTest): + """Test Gnocchi for external root CA config option.""" def test_upload_external_cert(self): """Verify that the external CA is uploaded correctly.""" @@ -123,6 +99,34 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): result = model.run_on_unit('gnocchi/0', cmd) self.assertEqual(result['Code'], '0') + +class GnocchiS3Test(test_utils.OpenStackBaseTest): + """Test Gnocchi for S3 storage backend.""" + + @classmethod + def setUpClass(cls): + """Run class setup for running tests.""" + super(GnocchiS3Test, cls).setUpClass() + + session = openstack_utils.get_overcloud_keystone_session() + ks_client = openstack_utils.get_keystone_session_client(session) + + # Get token data so we can glean our user_id and project_id + token_data = ks_client.tokens.get_token_data(session.get_token()) + project_id = token_data['token']['project']['id'] + user_id = token_data['token']['user']['id'] + + # Store URL to service providing S3 compatible API + for entry in token_data['token']['catalog']: + if entry['type'] == 's3': + for endpoint in entry['endpoints']: + if endpoint['interface'] == 'public': + cls.s3_region = endpoint['region'] + cls.s3_endpoint = endpoint['url'] + + # Create AWS compatible application credentials in Keystone + cls.ec2_creds = ks_client.ec2.create(user_id, project_id) + def test_s3_list_gnocchi_buckets(self): """Verify that the gnocchi buckets were created in the S3 backend.""" kwargs = { From 6d0a59802bd676f4b8565bc005dc94c6cc97ed57 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Thu, 20 Aug 2020 11:35:38 -0500 Subject: [PATCH 45/64] fix merge conflict --- zaza/openstack/charm_tests/gnocchi/tests.py | 73 ++++++++++----------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index 808f374..5147486 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -16,15 +16,12 @@ """Encapsulate Gnocchi testing.""" -import base64 import boto3 import logging import pprint from gnocchiclient.v1 import client as gnocchi_client -import zaza.model as model import zaza.openstack.charm_tests.test_utils as test_utils -import zaza.openstack.utilities as utilities import zaza.openstack.utilities.openstack as openstack_utils @@ -65,41 +62,6 @@ class GnocchiTest(test_utils.OpenStackBaseTest): logging.info("Testing pause and resume") -class GnocchiExternalCATest(test_utils.OpenStackBaseTest): - """Test Gnocchi for external root CA config option.""" - - def test_upload_external_cert(self): - """Verify that the external CA is uploaded correctly.""" - logging.info('Changing value for trusted-external-ca-cert.') - ca_cert_option = 'trusted-external-ca-cert' - ppk, cert = utilities.cert.generate_cert('gnocchi_test.ci.local') - b64_cert = base64.b64encode(cert).decode() - config = { - ca_cert_option: b64_cert, - } - model.set_application_config( - 'gnocchi', - config - ) - model.block_until_all_units_idle() - - cert_location = '/usr/local/share/ca-certificates' - cert_name = 'gnocchi-external.crt' - cmd = 'ls ' + cert_location + '/' + cert_name - logging.info("Validating that the file {} is created in \ - {}".format(cert_name, cert_location)) - result = model.run_on_unit('gnocchi/0', cmd) - self.assertEqual(result['Code'], '0') - - linked_cert_location = '/etc/ssl/certs' - linked_cert_name = 'gnocchi-external.pem' - cmd = 'ls ' + linked_cert_location + '/' + linked_cert_name - logging.info("Validating that the link {} is created in \ - {}".format(linked_cert_name, linked_cert_location)) - result = model.run_on_unit('gnocchi/0', cmd) - self.assertEqual(result['Code'], '0') - - class GnocchiS3Test(test_utils.OpenStackBaseTest): """Test Gnocchi for S3 storage backend.""" @@ -148,3 +110,38 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): break else: AssertionError('Bucket "{}" not found'.format(gnocchi_bkt)) + + +class GnocchiExternalCATest(test_utils.OpenStackBaseTest): + """Test Gnocchi for external root CA config option.""" + + def test_upload_external_cert(self): + """Verify that the external CA is uploaded correctly.""" + logging.info('Changing value for trusted-external-ca-cert.') + ca_cert_option = 'trusted-external-ca-cert' + ppk, cert = utilities.cert.generate_cert('gnocchi_test.ci.local') + b64_cert = base64.b64encode(cert).decode() + config = { + ca_cert_option: b64_cert, + } + model.set_application_config( + 'gnocchi', + config + ) + model.block_until_all_units_idle() + + cert_location = '/usr/local/share/ca-certificates' + cert_name = 'gnocchi-external.crt' + cmd = 'ls ' + cert_location + '/' + cert_name + logging.info("Validating that the file {} is created in \ + {}".format(cert_name, cert_location)) + result = model.run_on_unit('gnocchi/0', cmd) + self.assertEqual(result['Code'], '0') + + linked_cert_location = '/etc/ssl/certs' + linked_cert_name = 'gnocchi-external.pem' + cmd = 'ls ' + linked_cert_location + '/' + linked_cert_name + logging.info("Validating that the link {} is created in \ + {}".format(linked_cert_name, linked_cert_location)) + result = model.run_on_unit('gnocchi/0', cmd) + self.assertEqual(result['Code'], '0') \ No newline at end of file From 1dc8ee4287f3d7237f4478f699deac216be707fc Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Thu, 20 Aug 2020 11:37:03 -0500 Subject: [PATCH 46/64] add imports --- zaza/openstack/charm_tests/gnocchi/tests.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index 5147486..7bd4523 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -16,12 +16,15 @@ """Encapsulate Gnocchi testing.""" +import base64 import boto3 import logging import pprint from gnocchiclient.v1 import client as gnocchi_client +import zaza.model as model import zaza.openstack.charm_tests.test_utils as test_utils +import zaza.openstack.utilities as utilities import zaza.openstack.utilities.openstack as openstack_utils @@ -144,4 +147,4 @@ class GnocchiExternalCATest(test_utils.OpenStackBaseTest): logging.info("Validating that the link {} is created in \ {}".format(linked_cert_name, linked_cert_location)) result = model.run_on_unit('gnocchi/0', cmd) - self.assertEqual(result['Code'], '0') \ No newline at end of file + self.assertEqual(result['Code'], '0') From 28de49811e556c3963206929d30d4c5c686e2950 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Thu, 20 Aug 2020 13:47:12 -0500 Subject: [PATCH 47/64] test block function instead of ls --- zaza/openstack/charm_tests/gnocchi/tests.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index 7bd4523..73c32a9 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -133,13 +133,15 @@ class GnocchiExternalCATest(test_utils.OpenStackBaseTest): ) model.block_until_all_units_idle() - cert_location = '/usr/local/share/ca-certificates' - cert_name = 'gnocchi-external.crt' - cmd = 'ls ' + cert_location + '/' + cert_name - logging.info("Validating that the file {} is created in \ - {}".format(cert_name, cert_location)) - result = model.run_on_unit('gnocchi/0', cmd) - self.assertEqual(result['Code'], '0') + # cert_location = '/usr/local/share/ca-certificates' + # cert_name = 'gnocchi-external.crt' + # cmd = 'ls ' + cert_location + '/' + cert_name + # logging.info("Validating that the file {} is created in \ + # {}".format(cert_name, cert_location)) + # result = model.run_on_unit('gnocchi/0', cmd) + remote_file = '/usr/local/share/ca-certificates/gnocchi-external.crt' + result = model.block_until_file_ready('gnocchi', remote_file, b64_cert) + self.assertTrue(result) linked_cert_location = '/etc/ssl/certs' linked_cert_name = 'gnocchi-external.pem' From cece0d3096fff512303c23de10f611e1e27be768 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Thu, 20 Aug 2020 13:47:12 -0500 Subject: [PATCH 48/64] test block function instead of ls --- zaza/openstack/charm_tests/gnocchi/tests.py | 36 ++++++++++++--------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index 7bd4523..b099fa6 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -76,7 +76,7 @@ class GnocchiS3Test(test_utils.OpenStackBaseTest): session = openstack_utils.get_overcloud_keystone_session() ks_client = openstack_utils.get_keystone_session_client(session) - # Get token data so we can glean our user_id and project_id + # Get token data so we can clean our user_id and project_id token_data = ks_client.tokens.get_token_data(session.get_token()) project_id = token_data['token']['project']['id'] user_id = token_data['token']['user']['id'] @@ -133,18 +133,24 @@ class GnocchiExternalCATest(test_utils.OpenStackBaseTest): ) model.block_until_all_units_idle() - cert_location = '/usr/local/share/ca-certificates' - cert_name = 'gnocchi-external.crt' - cmd = 'ls ' + cert_location + '/' + cert_name - logging.info("Validating that the file {} is created in \ - {}".format(cert_name, cert_location)) - result = model.run_on_unit('gnocchi/0', cmd) - self.assertEqual(result['Code'], '0') + # cert_location = '/usr/local/share/ca-certificates' + # cert_name = 'gnocchi-external.crt' + # cmd = 'ls ' + cert_location + '/' + cert_name + # logging.info("Validating that the file {} is created in \ + # {}".format(cert_name, cert_location)) + # result = model.run_on_unit('gnocchi/0', cmd) + remote_file = '/usr/local/share/ca-certificates/gnocchi-external.crt' + logging.info("Validating that {} is created.".format(remote_file)) + result = model.block_until_file_ready('gnocchi', remote_file, b64_cert) + self.assertTrue(result) - linked_cert_location = '/etc/ssl/certs' - linked_cert_name = 'gnocchi-external.pem' - cmd = 'ls ' + linked_cert_location + '/' + linked_cert_name - logging.info("Validating that the link {} is created in \ - {}".format(linked_cert_name, linked_cert_location)) - result = model.run_on_unit('gnocchi/0', cmd) - self.assertEqual(result['Code'], '0') + # linked_cert_location = '/etc/ssl/certs' + # linked_cert_name = 'gnocchi-external.pem' + # cmd = 'ls ' + linked_cert_location + '/' + linked_cert_name + # logging.info("Validating that the link {} is created in \ + # {}".format(linked_cert_name, linked_cert_location)) + # result = model.run_on_unit('gnocchi/0', cmd) + remote_linked_file = '/etc/ssl/certs/gnocchi-external.pem' + result = model.block_until_file_ready('gnocchi', remote_linked_file, b64_cert) + # self.assertEqual(result['Code'], '0') + self.assertTrue(result) From 6b822ee8ecec179ad5d888666d79f2decbc48172 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Thu, 20 Aug 2020 16:03:04 -0500 Subject: [PATCH 49/64] replace ls with file_has_contents --- zaza/openstack/charm_tests/gnocchi/tests.py | 28 ++++++--------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/zaza/openstack/charm_tests/gnocchi/tests.py b/zaza/openstack/charm_tests/gnocchi/tests.py index b099fa6..ed4bac0 100644 --- a/zaza/openstack/charm_tests/gnocchi/tests.py +++ b/zaza/openstack/charm_tests/gnocchi/tests.py @@ -133,24 +133,12 @@ class GnocchiExternalCATest(test_utils.OpenStackBaseTest): ) model.block_until_all_units_idle() - # cert_location = '/usr/local/share/ca-certificates' - # cert_name = 'gnocchi-external.crt' - # cmd = 'ls ' + cert_location + '/' + cert_name - # logging.info("Validating that the file {} is created in \ - # {}".format(cert_name, cert_location)) - # result = model.run_on_unit('gnocchi/0', cmd) - remote_file = '/usr/local/share/ca-certificates/gnocchi-external.crt' - logging.info("Validating that {} is created.".format(remote_file)) - result = model.block_until_file_ready('gnocchi', remote_file, b64_cert) - self.assertTrue(result) + files = [ + '/usr/local/share/ca-certificates/gnocchi-external.crt', + '/etc/ssl/certs/gnocchi-external.pem', + ] - # linked_cert_location = '/etc/ssl/certs' - # linked_cert_name = 'gnocchi-external.pem' - # cmd = 'ls ' + linked_cert_location + '/' + linked_cert_name - # logging.info("Validating that the link {} is created in \ - # {}".format(linked_cert_name, linked_cert_location)) - # result = model.run_on_unit('gnocchi/0', cmd) - remote_linked_file = '/etc/ssl/certs/gnocchi-external.pem' - result = model.block_until_file_ready('gnocchi', remote_linked_file, b64_cert) - # self.assertEqual(result['Code'], '0') - self.assertTrue(result) + for file in files: + logging.info("Validating that {} is created.".format(file)) + model.block_until_file_has_contents('gnocchi', file, 'CERTIFICATE') + logging.info("Found {} successfully.".format(file)) From 0ca011afecc86bccac819ee80eaf8625e9ff886d Mon Sep 17 00:00:00 2001 From: David Ames Date: Thu, 20 Aug 2020 14:28:18 -0700 Subject: [PATCH 50/64] Ceph rados benchmarking --- .../charm_tests/ceph/benchmarking/__init__.py | 15 +++ .../charm_tests/ceph/benchmarking/tests.py | 124 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 zaza/openstack/charm_tests/ceph/benchmarking/__init__.py create mode 100644 zaza/openstack/charm_tests/ceph/benchmarking/tests.py diff --git a/zaza/openstack/charm_tests/ceph/benchmarking/__init__.py b/zaza/openstack/charm_tests/ceph/benchmarking/__init__.py new file mode 100644 index 0000000..74fd9bd --- /dev/null +++ b/zaza/openstack/charm_tests/ceph/benchmarking/__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 benchmarking ceph.""" diff --git a/zaza/openstack/charm_tests/ceph/benchmarking/tests.py b/zaza/openstack/charm_tests/ceph/benchmarking/tests.py new file mode 100644 index 0000000..9bbf60e --- /dev/null +++ b/zaza/openstack/charm_tests/ceph/benchmarking/tests.py @@ -0,0 +1,124 @@ +# 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. + +"""Ceph Benchmark Tests.""" + +import logging +import re +import unittest + +import zaza.model + + +class BenchmarkTests(unittest.TestCase): + """Ceph Bencharmk Tests.""" + + @classmethod + def setUpClass(cls): + """Run class setup for running ceph benchmark tests.""" + super().setUpClass() + cls.results_match = "^[A-Z].*" + cls.pool = "zaza_benchmarks" + cls.test_results = {} + cls.time_in_secs = 30 + + def parse_bench_results(self, results_string): + """Parse bench results from string. + + :param results string: Output from rados bench command. + With newlines due to juju run's output. + :type results_string: string + :returns: Dictionary of results summary + :rtype: dict + """ + _results = {} + _lines = results_string.split("\n") + for _line in _lines: + _line = _line.strip() + if re.match(self.results_match, _line): + _keyvalues = _line.split(":") + try: + _results[_keyvalues[0].strip()] = _keyvalues[1].strip() + except IndexError: + # Skipping detailed output for summary details + pass + return _results + + def run_rados_bench(self, action, params=None): + """Run rados bench. + + :param action: String rados bench command i.e. write, rand, seq + :type action: string + :param params: List of string extra parameters to rados bench command + :type params: List[strings] + :returns: Unit run dict result + :rtype: dict + """ + _cmd = "rados bench -p {} {} {}".format( + self.pool, self.time_in_secs, action) + if params: + _cmd += " " + _cmd += " ".join(params) + logging.info( + "Running '{}' for {} seconds ...".format(_cmd, self.time_in_secs)) + _result = zaza.model.run_on_leader( + "ceph-mon", _cmd, timeout=self.time_in_secs + 60) + return _result + + def test_001_create_pool(self): + """Create ceph pool.""" + _cmd = "ceph osd pool create {} 100 100".format(self.pool) + _result = zaza.model.run_on_leader( + "ceph-mon", _cmd) + if _result.get("Code") and not _result.get("Code").startswith('0'): + if "already exists" in _result.get("Stderr", ""): + logging.warning( + "Ceph osd pool {} already exits.".format(self.pool)) + else: + logging.error("Ceph osd pool create failed") + raise Exception(_result.get("Stderr", "")) + + def test_100_rados_bench_write(self): + """Rados bench write test.""" + _result = self.run_rados_bench("write", params=["--no-cleanup"]) + self.test_results["write"] = ( + self.parse_bench_results(_result.get("Stdout", ""))) + + def test_200_rados_bench_read_seq(self): + """Rados bench read sequential test.""" + _result = self.run_rados_bench("seq") + self.test_results["read_seq"] = ( + self.parse_bench_results(_result.get("Stdout", ""))) + + def test_300_rados_bench_read_rand(self): + """Rados bench read random test.""" + _result = self.run_rados_bench("rand") + self.test_results["read_rand"] = ( + self.parse_bench_results(_result.get("Stdout", ""))) + + def test_998_rados_cleanup(self): + """Cleanup rados bench data.""" + _cmd = "rados -p {} cleanup".format(self.pool) + _result = zaza.model.run_on_leader("ceph-mon", _cmd) + if _result.get("Code") and not _result.get("Code").startswith('0'): + logging.warning("rados cleanup failed") + + def test_999_print_rados_bench_results(self): + """Print rados bench results.""" + print("######## Begin Ceph Results ########") + for test, results in self.test_results.items(): + print("##### {} ######".format(test)) + for key, value in results.items(): + print("{}: {}".format(key, value)) + print("######## End Ceph Results ########") From 287cc778d4d4a6aad0cd117d0646fde058f0822f Mon Sep 17 00:00:00 2001 From: Alex Kavanagh <567675+ajkavanagh@users.noreply.github.com> Date: Fri, 21 Aug 2020 11:24:11 +0100 Subject: [PATCH 51/64] Extend the timeouts for create / backup / snapshot test (#390) On OSCI it's variable whether the test will pass as it's very close to the timeout. Extend it to give the test a reliable chance to pass. --- zaza/openstack/charm_tests/cinder_backup/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zaza/openstack/charm_tests/cinder_backup/tests.py b/zaza/openstack/charm_tests/cinder_backup/tests.py index b3f7ce7..7d3442e 100644 --- a/zaza/openstack/charm_tests/cinder_backup/tests.py +++ b/zaza/openstack/charm_tests/cinder_backup/tests.py @@ -97,7 +97,7 @@ class CinderBackupTest(test_utils.OpenStackBaseTest): self.cinder_client.volumes, cinder_vol.id, wait_iteration_max_time=180, - stop_after_attempt=15, + stop_after_attempt=30, expected_status='available', msg='Volume status wait') @@ -109,7 +109,7 @@ class CinderBackupTest(test_utils.OpenStackBaseTest): self.cinder_client.backups, vol_backup.id, wait_iteration_max_time=180, - stop_after_attempt=15, + stop_after_attempt=30, expected_status='available', msg='Volume status wait') # Delete the volume From cac63150d93026774f134b64f28eb2c246d2d2e1 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Wed, 26 Aug 2020 12:59:40 +0200 Subject: [PATCH 52/64] Moved test to its own new class --- zaza/openstack/charm_tests/neutron/tests.py | 50 +++++++++++---------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/zaza/openstack/charm_tests/neutron/tests.py b/zaza/openstack/charm_tests/neutron/tests.py index 37a99db..b313488 100644 --- a/zaza/openstack/charm_tests/neutron/tests.py +++ b/zaza/openstack/charm_tests/neutron/tests.py @@ -147,29 +147,6 @@ class NeutronGatewayTest(NeutronPluginApiSharedTests): self.assertIn('qos', ovs_agent['configurations']['extensions']) - def test_800_ovs_bridges_are_managed_by_us(self): - """Checking OVS bridges' external-id. - - OVS bridges created by us should be marked as managed by us in their - external-id. See - http://docs.openvswitch.org/en/latest/topics/integration/ - """ - for unit in zaza.model.get_units(self._APP_NAME, - model_name=self.model_name): - for bridge_name in ('br-int', 'br-ex'): - logging.info( - 'Checking that the bridge {}:{}'.format( - unit.name, bridge_name - ) + ' is marked as managed by us' - ) - expected_external_id = 'charm-neutron-gateway=managed' - actual_external_id = zaza.model.run_on_unit( - unit.entity_id, - 'ovs-vsctl br-get-external-id {}'.format(bridge_name), - model_name=self.model_name - )['Stdout'].strip() - self.assertEqual(actual_external_id, expected_external_id) - def test_900_restart_on_config_change(self): """Checking restart happens on config change. @@ -612,6 +589,33 @@ class NeutronOpenvSwitchTest(NeutronPluginApiSharedTests): logging.info('Testing pause resume') +class NeutronOvsVsctlTest(NeutronPluginApiSharedTests): + """Test 'ovs-vsctl'-related functionality on Neutron charms.""" + + def test_800_ovs_bridges_are_managed_by_us(self): + """Checking OVS bridges' external-id. + + OVS bridges created by us should be marked as managed by us in their + external-id. See + http://docs.openvswitch.org/en/latest/topics/integration/ + """ + for unit in zaza.model.get_units(self.application_name, + model_name=self.model_name): + for bridge_name in ('br-int', 'br-ex'): + logging.info( + 'Checking that the bridge {}:{}'.format( + unit.name, bridge_name + ) + ' is marked as managed by us' + ) + expected_external_id = 'charm-neutron-gateway=managed' + actual_external_id = zaza.model.run_on_unit( + unit.entity_id, + 'ovs-vsctl br-get-external-id {}'.format(bridge_name), + model_name=self.model_name + )['Stdout'].strip() + self.assertEqual(actual_external_id, expected_external_id) + + class NeutronNetworkingBase(test_utils.OpenStackBaseTest): """Base for checking openstack instances have valid networking.""" From 52dc1354ffd170fcaa2fb802d3bb4cb32810fb77 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Wed, 26 Aug 2020 14:19:00 +0200 Subject: [PATCH 53/64] Fix setUpClass' signature --- zaza/openstack/charm_tests/neutron/tests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/neutron/tests.py b/zaza/openstack/charm_tests/neutron/tests.py index b313488..62aa33b 100644 --- a/zaza/openstack/charm_tests/neutron/tests.py +++ b/zaza/openstack/charm_tests/neutron/tests.py @@ -34,9 +34,10 @@ import zaza.openstack.utilities.openstack as openstack_utils class NeutronPluginApiSharedTests(test_utils.OpenStackBaseTest): """Shared tests for Neutron Plugin API Charms.""" + @classmethod def setUpClass(cls): """Run class setup for running Neutron Openvswitch tests.""" - super(NeutronPluginApiSharedTests, cls).setUpClass() + super(NeutronPluginApiSharedTests, cls).setUpClass(cls) cls.current_os_release = openstack_utils.get_os_release() cls.bionic_stein = openstack_utils.get_os_release('bionic_stein') From fe82bc76f50265bd7b883235f2ec602fb615e36a Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Wed, 26 Aug 2020 16:34:28 +0200 Subject: [PATCH 54/64] Fix setUpClass' signature --- zaza/openstack/charm_tests/neutron/tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zaza/openstack/charm_tests/neutron/tests.py b/zaza/openstack/charm_tests/neutron/tests.py index 62aa33b..5d2f019 100644 --- a/zaza/openstack/charm_tests/neutron/tests.py +++ b/zaza/openstack/charm_tests/neutron/tests.py @@ -37,7 +37,7 @@ class NeutronPluginApiSharedTests(test_utils.OpenStackBaseTest): @classmethod def setUpClass(cls): """Run class setup for running Neutron Openvswitch tests.""" - super(NeutronPluginApiSharedTests, cls).setUpClass(cls) + super(NeutronPluginApiSharedTests, cls).setUpClass() cls.current_os_release = openstack_utils.get_os_release() cls.bionic_stein = openstack_utils.get_os_release('bionic_stein') @@ -109,7 +109,7 @@ class NeutronGatewayTest(NeutronPluginApiSharedTests): @classmethod def setUpClass(cls): """Run class setup for running Neutron Gateway tests.""" - super(NeutronGatewayTest, cls).setUpClass(cls) + super(NeutronGatewayTest, cls).setUpClass() cls.services = cls._get_services() # set up clients @@ -427,7 +427,7 @@ class NeutronOpenvSwitchTest(NeutronPluginApiSharedTests): @classmethod def setUpClass(cls): """Run class setup for running Neutron Openvswitch tests.""" - super(NeutronOpenvSwitchTest, cls).setUpClass(cls) + super(NeutronOpenvSwitchTest, cls).setUpClass() # set up client cls.neutron_client = ( From 3fb4380c66c510b14d44584329c034ffa6f915f5 Mon Sep 17 00:00:00 2001 From: James Page Date: Fri, 28 Aug 2020 09:19:16 +0100 Subject: [PATCH 55/64] Improve ceph proxy permissions check Update the cinder-ceph permissions check to look for permissions specific to the cinder-ceph usage of ceph; this ensures that if we add glance and nova to the bundle (which creates additional permissions) the existing test will continue to pass. --- zaza/openstack/charm_tests/ceph/tests.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/zaza/openstack/charm_tests/ceph/tests.py b/zaza/openstack/charm_tests/ceph/tests.py index d20486f..45b4920 100644 --- a/zaza/openstack/charm_tests/ceph/tests.py +++ b/zaza/openstack/charm_tests/ceph/tests.py @@ -766,16 +766,23 @@ class CephProxyTest(unittest.TestCase): msg = 'cinder-ceph pool was not found upon querying ceph-mon/0' raise zaza_exceptions.CephPoolNotFound(msg) - expected = "pool=cinder-ceph, allow class-read " \ - "object_prefix rbd_children" + # Checking for cinder-ceph specific permissions makes + # the test more rugged when we add additional relations + # to ceph for other applications (such as glance and nova). + expected_permissions = [ + "allow rwx pool=cinder-ceph", + "allow class-read object_prefix rbd_children", + ] cmd = "sudo ceph auth get client.cinder-ceph" result = zaza_model.run_on_unit('ceph-mon/0', cmd) output = result.get('Stdout').strip() - if expected not in output: - msg = ('cinder-ceph pool restriction was not configured correctly.' - ' Found: {}'.format(output)) - raise zaza_exceptions.CephPoolNotConfigured(msg) + for expected in expected_permissions: + if expected not in output: + msg = ('cinder-ceph pool restriction ({}) was not' + ' configured correctly.' + ' Found: {}'.format(expected, output)) + raise zaza_exceptions.CephPoolNotConfigured(msg) class CephPrometheusTest(unittest.TestCase): From dd2802057f7512b4ee537dfb09762c3992460835 Mon Sep 17 00:00:00 2001 From: Chris MacNaughton Date: Fri, 28 Aug 2020 13:55:42 +0200 Subject: [PATCH 56/64] Ensure that octavia sets up application_name When running in a deployment that is focused on bundles rather than a specific charm, the Octavia setup can fail because there is no configured charm_name in the tests.yaml. This change ensures that the Octavia setup can continue. --- zaza/openstack/charm_tests/octavia/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/octavia/setup.py b/zaza/openstack/charm_tests/octavia/setup.py index 729fb16..677c368 100644 --- a/zaza/openstack/charm_tests/octavia/setup.py +++ b/zaza/openstack/charm_tests/octavia/setup.py @@ -92,7 +92,7 @@ def configure_octavia(): del test_config['target_deploy_status']['octavia'] _singleton = zaza.openstack.charm_tests.test_utils.OpenStackBaseTest() - _singleton.setUpClass() + _singleton.setUpClass(application_name='octavia') with _singleton.config_change(cert_config, cert_config): # wait for configuration to be applied then return pass From b38c270036a3e3efeb4d18c288786439fd2301b1 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Wed, 2 Sep 2020 11:51:04 +0200 Subject: [PATCH 57/64] Adapt list of swift services on focal-victoria --- zaza/openstack/charm_tests/swift/tests.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/zaza/openstack/charm_tests/swift/tests.py b/zaza/openstack/charm_tests/swift/tests.py index 75f82b7..fa73c16 100644 --- a/zaza/openstack/charm_tests/swift/tests.py +++ b/zaza/openstack/charm_tests/swift/tests.py @@ -114,6 +114,17 @@ class SwiftStorageTests(test_utils.OpenStackBaseTest): 'swift-object-replicator', 'swift-object-updater', 'swift-container-sync'] + + focal_victoria = openstack_utils.get_os_release('focal_victoria') + if self.current_os_release < focal_victoria: + services += ['swift-account-replicator', + 'swift-container-replicator', + 'swift-object-replicator'] + else: + services += ['swift-account-server', + 'swift-container-server', + 'swift-object-server'] + with self.pause_resume(services): logging.info("Testing pause resume") From 23c3dde105b481ef34749132f993d4452c56e53a Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Wed, 2 Sep 2020 13:40:08 +0200 Subject: [PATCH 58/64] Fix SwiftStorageTests.test_901_pause_resume --- zaza/openstack/charm_tests/swift/tests.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/zaza/openstack/charm_tests/swift/tests.py b/zaza/openstack/charm_tests/swift/tests.py index fa73c16..62fe02c 100644 --- a/zaza/openstack/charm_tests/swift/tests.py +++ b/zaza/openstack/charm_tests/swift/tests.py @@ -104,19 +104,17 @@ class SwiftStorageTests(test_utils.OpenStackBaseTest): services = ['swift-account-server', 'swift-account-auditor', 'swift-account-reaper', - 'swift-account-replicator', 'swift-container-server', 'swift-container-auditor', - 'swift-container-replicator', 'swift-container-updater', 'swift-object-server', 'swift-object-auditor', - 'swift-object-replicator', 'swift-object-updater', 'swift-container-sync'] + current_os_release = openstack_utils.get_os_release() focal_victoria = openstack_utils.get_os_release('focal_victoria') - if self.current_os_release < focal_victoria: + if current_os_release < focal_victoria: services += ['swift-account-replicator', 'swift-container-replicator', 'swift-object-replicator'] From 828883bb70b3d786cece6a467f93b2b03d52cc30 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Thu, 3 Sep 2020 10:06:18 +0100 Subject: [PATCH 59/64] Make test_check_pool_types handle missing apps (#405) Currently test_check_pool_types will fail when it tries to check the corresponding pool type for an application that is not present in the deployment, this patch changes the behaviour to skip missing applications. --- zaza/openstack/charm_tests/ceph/tests.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/zaza/openstack/charm_tests/ceph/tests.py b/zaza/openstack/charm_tests/ceph/tests.py index 45b4920..001fc0f 100644 --- a/zaza/openstack/charm_tests/ceph/tests.py +++ b/zaza/openstack/charm_tests/ceph/tests.py @@ -818,8 +818,15 @@ class CheckPoolTypes(unittest.TestCase): ('cinder-ceph', 'cinder-ceph')] runtime_pool_details = zaza_ceph.get_ceph_pool_details() for app, pool_name in app_pools: - juju_pool_config = zaza_model.get_application_config(app).get( - 'pool-type') + try: + app_config = zaza_model.get_application_config(app) + except KeyError: + logging.info( + 'Skipping pool check of %s, application %s not present', + pool_name, + app) + continue + juju_pool_config = app_config.get('pool-type') if juju_pool_config: expected_pool_type = juju_pool_config['value'] else: From 66d95d65d7637b6b4f56c575ed552f13ebfa02b3 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh <567675+ajkavanagh@users.noreply.github.com> Date: Fri, 4 Sep 2020 12:33:42 +0100 Subject: [PATCH 60/64] Fix series-upgrade tests (#406) A couple of changes here: * Ensure that the post-upgrade-hook runs BEFORE the config-changed to set the openstack-origin/source back to distro. The former behaviour breaks keystone quite badly. * Ensure that the charm name is used, as discovered from the model, for rabbitmq-server and percona-cluster to cope with different names for the application in the model vs the charm name from the charm-store. * Check whether the machine needs to be rebooted after the dist-upgrade (before the do-release-upgrade), and reboot the machine first. Otherwise, do-release-upgrade will fail. --- requirements.txt | 2 +- .../charm_tests/series_upgrade/tests.py | 4 +- zaza/openstack/utilities/series_upgrade.py | 38 +++++++++++++++++-- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index fa0e460..8b15edc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ aiounittest async_generator boto3 -juju +juju!=2.8.3 # blacklist 2.8.3 as it appears to have a connection bug juju_wait PyYAML<=4.2,>=3.0 flake8>=2.2.4 diff --git a/zaza/openstack/charm_tests/series_upgrade/tests.py b/zaza/openstack/charm_tests/series_upgrade/tests.py index 4b06dbb..c595448 100644 --- a/zaza/openstack/charm_tests/series_upgrade/tests.py +++ b/zaza/openstack/charm_tests/series_upgrade/tests.py @@ -81,7 +81,7 @@ class SeriesUpgradeTest(unittest.TestCase): logging.info( "Running complete-cluster-series-upgrade action on leader") model.run_action_on_leader( - 'rabbitmq-server', + charm_name, 'complete-cluster-series-upgrade', action_params={}) model.block_until_all_units_idle() @@ -90,7 +90,7 @@ class SeriesUpgradeTest(unittest.TestCase): logging.info( "Running complete-cluster-series-upgrade action on leader") model.run_action_on_leader( - 'mysql', + charm_name, 'complete-cluster-series-upgrade', action_params={}) model.block_until_all_units_idle() diff --git a/zaza/openstack/utilities/series_upgrade.py b/zaza/openstack/utilities/series_upgrade.py index f12f77b..97ba153 100644 --- a/zaza/openstack/utilities/series_upgrade.py +++ b/zaza/openstack/utilities/series_upgrade.py @@ -14,11 +14,13 @@ """Collection of functions for testing series upgrade.""" +import asyncio import collections import copy import concurrent import logging import os +import time from zaza import model from zaza.charm_lifecycle import utils as cl_utils @@ -642,14 +644,14 @@ def series_upgrade(unit_name, machine_num, model.block_until_unit_wl_status(unit_name, "blocked") logging.info("Waiting for model idleness") model.block_until_all_units_idle() + logging.info("Complete series upgrade on {}".format(machine_num)) + model.complete_series_upgrade(machine_num) + model.block_until_all_units_idle() logging.info("Set origin on {}".format(application)) # Allow for charms which have neither source nor openstack-origin if origin: os_utils.set_origin(application, origin) model.block_until_all_units_idle() - logging.info("Complete series upgrade on {}".format(machine_num)) - model.complete_series_upgrade(machine_num) - model.block_until_all_units_idle() logging.info("Running run_post_upgrade_functions {}".format( post_upgrade_functions)) run_post_upgrade_functions(post_upgrade_functions) @@ -882,6 +884,21 @@ def dist_upgrade(unit_name): """-o "Dpkg::Options::=--force-confdef" """ """-o "Dpkg::Options::=--force-confold" dist-upgrade""") model.run_on_unit(unit_name, dist_upgrade_cmd) + rdict = model.run_on_unit(unit_name, "cat /var/run/reboot-required") + if "Stdout" in rdict and "restart" in rdict["Stdout"].lower(): + logging.info("dist-upgrade required reboot {}".format(unit_name)) + os_utils.reboot(unit_name) + logging.info("Waiting for workload status 'unknown' on {}" + .format(unit_name)) + model.block_until_unit_wl_status(unit_name, "unknown") + logging.info("Waiting for workload status to return to normal on {}" + .format(unit_name)) + model.block_until_unit_wl_status( + unit_name, "unknown", negate_match=True) + logging.info("Waiting for model idleness") + # pause for a big + time.sleep(5.0) + model.block_until_all_units_idle() async def async_dist_upgrade(unit_name): @@ -902,6 +919,21 @@ async def async_dist_upgrade(unit_name): """-o "Dpkg::Options::=--force-confdef" """ """-o "Dpkg::Options::=--force-confold" dist-upgrade""") await model.async_run_on_unit(unit_name, dist_upgrade_cmd) + rdict = await model.async_run_on_unit(unit_name, + "cat /var/run/reboot-required") + if "Stdout" in rdict and "restart" in rdict["Stdout"].lower(): + logging.info("dist-upgrade required reboot {}".format(unit_name)) + await os_utils.async_reboot(unit_name) + logging.info("Waiting for workload status 'unknown' on {}" + .format(unit_name)) + await model.async_block_until_unit_wl_status(unit_name, "unknown") + logging.info("Waiting for workload status to return to normal on {}" + .format(unit_name)) + await model.async_block_until_unit_wl_status( + unit_name, "unknown", negate_match=True) + logging.info("Waiting for model idleness") + await asyncio.sleep(5.0) + await model.async_block_until_all_units_idle() def do_release_upgrade(unit_name): From bb9899650aca21092afadb41ec6442258d43a100 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Mon, 7 Sep 2020 12:34:55 +0000 Subject: [PATCH 61/64] Skip ceph pool type check if relation is missing --- zaza/openstack/charm_tests/ceph/tests.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/zaza/openstack/charm_tests/ceph/tests.py b/zaza/openstack/charm_tests/ceph/tests.py index 001fc0f..99285a3 100644 --- a/zaza/openstack/charm_tests/ceph/tests.py +++ b/zaza/openstack/charm_tests/ceph/tests.py @@ -826,6 +826,15 @@ class CheckPoolTypes(unittest.TestCase): pool_name, app) continue + rel_id = zaza_model.get_relation_id( + app, + 'ceph-mon', + remote_interface_name='client') + if not rel_id: + logging.info( + 'Skipping pool check of %s, ceph relation not present', + app) + continue juju_pool_config = app_config.get('pool-type') if juju_pool_config: expected_pool_type = juju_pool_config['value'] From 86547a4efc44bde88d2e335b166c05d0d4c9c5c4 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Mon, 7 Sep 2020 15:09:14 +0200 Subject: [PATCH 62/64] Add more insights on trilio test failures (#400) * Add more insights on trilio test failures * Fix import * Fix wrong signature --- zaza/openstack/charm_tests/trilio/tests.py | 59 ++++++++++++++++++---- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/zaza/openstack/charm_tests/trilio/tests.py b/zaza/openstack/charm_tests/trilio/tests.py index 99ef3e0..991b7f5 100644 --- a/zaza/openstack/charm_tests/trilio/tests.py +++ b/zaza/openstack/charm_tests/trilio/tests.py @@ -21,15 +21,16 @@ import tenacity import zaza.model as zaza_model -import zaza.openstack.charm_tests.test_utils as test_utils -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.test_utils as test_utils import zaza.openstack.configure.guest as guest_utils +import zaza.openstack.utilities.openstack as openstack_utils +from zaza.utilities import juju as juju_utils def _resource_reaches_status( - unit, auth_args, command, resource_id, target_status + unit, auth_args, status_command, full_status_command, resource_id, + target_status ): """Wait for a workload resource to reach a status. @@ -37,8 +38,12 @@ def _resource_reaches_status( :type unit: zaza_model.Unit :param auth_args: authentication arguments for command :type auth_args: str - :param command: command to execute - :type command: str + :param status_command: command to execute to get the resource status that + is expected to reach target_status + :type status_command: str + :param full_status_command: command to execute to get insights on why the + resource failed to reach target_status + :type full_status_command: str :param resource_id: resource ID to monitor :type resource_id: str :param target_status: status to monitor for @@ -47,7 +52,7 @@ def _resource_reaches_status( resource_status = ( juju_utils.remote_run( unit, - remote_cmd=command.format( + remote_cmd=status_command.format( auth_args=auth_args, resource_id=resource_id ), timeout=180, @@ -63,7 +68,20 @@ def _resource_reaches_status( ) if resource_status == target_status: return - raise Exception("Resource not ready: {}".format(resource_status)) + + full_resource_status = ( + juju_utils.remote_run( + unit, + remote_cmd=full_status_command.format( + auth_args=auth_args, resource_id=resource_id + ), + timeout=180, + fatal=True, + ) + .strip() + ) + + raise Exception("Resource not ready:\n{}".format(full_resource_status)) class WorkloadmgrCLIHelper(object): @@ -78,7 +96,12 @@ class WorkloadmgrCLIHelper(object): WORKLOAD_STATUS_CMD = ( "openstack {auth_args} workload show " "-f value -c status " - " {resource_id} " + "{resource_id}" + ) + + WORKLOAD_FULL_STATUS_CMD = ( + "openstack {auth_args} workload show " + "{resource_id}" ) SNAPSHOT_CMD = ( @@ -94,7 +117,12 @@ class WorkloadmgrCLIHelper(object): SNAPSHOT_STATUS_CMD = ( "openstack {auth_args} workload snapshot show " "-f value -c status " - "{resource_id} " + "{resource_id}" + ) + + SNAPSHOT_FULL_STATUS_CMD = ( + "openstack {auth_args} workload snapshot show " + "{resource_id}" ) ONECLICK_RESTORE_CMD = ( @@ -110,7 +138,13 @@ class WorkloadmgrCLIHelper(object): RESTORE_STATUS_CMD = ( "openstack {auth_args} workloadmgr restore show " - "-f value -c status {resource_id}" + "-f value -c status " + "{resource_id}" + ) + + RESTORE_FULL_STATUS_CMD = ( + "openstack {auth_args} workloadmgr restore show " + "{resource_id}" ) def __init__(self, keystone_client): @@ -193,6 +227,7 @@ class WorkloadmgrCLIHelper(object): self.trilio_wlm_unit, self.auth_args, self.WORKLOAD_STATUS_CMD, + self.WORKLOAD_FULL_STATUS_CMD, workload_id, "available", ) @@ -235,6 +270,7 @@ class WorkloadmgrCLIHelper(object): self.trilio_wlm_unit, self.auth_args, self.SNAPSHOT_STATUS_CMD, + self.SNAPSHOT_FULL_STATUS_CMD, snapshot_id, "available", ) @@ -275,6 +311,7 @@ class WorkloadmgrCLIHelper(object): self.trilio_wlm_unit, self.auth_args, self.RESTORE_STATUS_CMD, + self.RESTORE_FULL_STATUS_CMD, restore_id, "available", ) From c75f2fcb6d4843a249ed7f5f1c38a1b605c9c3c2 Mon Sep 17 00:00:00 2001 From: Chris MacNaughton Date: Tue, 8 Sep 2020 08:40:30 +0200 Subject: [PATCH 63/64] Ensure that zaza-openstack-tests can talk to an IPv6 Keystone When configuring the address to use to talk to Keystone, the format_addr helper should be used to ensure that an IPv4 or IPv6 address can be correctly handled. --- unit_tests/charm_tests/test_utils.py | 12 ------------ zaza/openstack/charm_tests/test_utils.py | 18 ------------------ zaza/openstack/charm_tests/vault/utils.py | 4 ++-- zaza/openstack/utilities/openstack.py | 3 +++ 4 files changed, 5 insertions(+), 32 deletions(-) diff --git a/unit_tests/charm_tests/test_utils.py b/unit_tests/charm_tests/test_utils.py index a415360..280e2e2 100644 --- a/unit_tests/charm_tests/test_utils.py +++ b/unit_tests/charm_tests/test_utils.py @@ -30,15 +30,3 @@ class TestOpenStackBaseTest(unittest.TestCase): MyTestClass.setUpClass('foo', 'bar') _setUpClass.assert_called_with('foo', 'bar') - - -class TestUtils(unittest.TestCase): - - def test_format_addr(self): - self.assertEquals('1.2.3.4', test_utils.format_addr('1.2.3.4')) - self.assertEquals( - '[2001:db8::42]', test_utils.format_addr('2001:db8::42')) - with self.assertRaises(ValueError): - test_utils.format_addr('999.999.999.999') - with self.assertRaises(ValueError): - test_utils.format_addr('2001:db8::g') diff --git a/zaza/openstack/charm_tests/test_utils.py b/zaza/openstack/charm_tests/test_utils.py index 83595b0..2994629 100644 --- a/zaza/openstack/charm_tests/test_utils.py +++ b/zaza/openstack/charm_tests/test_utils.py @@ -14,7 +14,6 @@ """Module containing base class for implementing charm tests.""" import contextlib import logging -import ipaddress import subprocess import tenacity import unittest @@ -541,20 +540,3 @@ class OpenStackBaseTest(BaseCharmTest): instance_2 = self.retrieve_guest( '{}-ins-1'.format(self.RESOURCE_PREFIX)) return instance_1, instance_2 - - -def format_addr(addr): - """Validate and format IP address. - - :param addr: IPv6 or IPv4 address - :type addr: str - :returns: Address string, optionally encapsulated in brackets([]) - :rtype: str - :raises: ValueError - """ - ipaddr = ipaddress.ip_address(addr) - if isinstance(ipaddr, ipaddress.IPv6Address): - fmt = '[{}]' - else: - fmt = '{}' - return fmt.format(ipaddr) diff --git a/zaza/openstack/charm_tests/vault/utils.py b/zaza/openstack/charm_tests/vault/utils.py index a708d78..9814243 100644 --- a/zaza/openstack/charm_tests/vault/utils.py +++ b/zaza/openstack/charm_tests/vault/utils.py @@ -27,7 +27,7 @@ import yaml import collections import zaza.model -import zaza.openstack.charm_tests.test_utils as test_utils +import zaza.utilities.networking as network_utils AUTH_FILE = "vault_tests.yaml" CharmVaultClient = collections.namedtuple( @@ -102,7 +102,7 @@ def get_unit_api_url(ip): transport = 'http' if vault_config['ssl-cert']['value']: transport = 'https' - return '{}://{}:8200'.format(transport, test_utils.format_addr(ip)) + return '{}://{}:8200'.format(transport, network_utils.format_addr(ip)) def get_hvac_client(vault_url, cacert=None): diff --git a/zaza/openstack/utilities/openstack.py b/zaza/openstack/utilities/openstack.py index 14c708c..34d7e7b 100644 --- a/zaza/openstack/utilities/openstack.py +++ b/zaza/openstack/utilities/openstack.py @@ -68,6 +68,8 @@ from zaza.openstack.utilities import ( exceptions, generic as generic_utils, ) +import zaza.utilities.networking as network_utils + CIRROS_RELEASE_URL = 'http://download.cirros-cloud.net/version/released' CIRROS_IMAGE_URL = 'http://download.cirros-cloud.net' @@ -1704,6 +1706,7 @@ def get_overcloud_auth(address=None, model_name=None): if not address: address = get_keystone_ip(model_name=model_name) + address = network_utils.format_addr(address) password = juju_utils.leader_get( 'keystone', From e6300dba20578a24a067ec449d6677413a964069 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Tue, 8 Sep 2020 12:11:05 +0200 Subject: [PATCH 64/64] Add HaclusterScalebackTest (#399) Co-authored-by: Alvaro Uria --- zaza/openstack/charm_tests/hacluster/tests.py | 67 ++++++++++++++++++- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/zaza/openstack/charm_tests/hacluster/tests.py b/zaza/openstack/charm_tests/hacluster/tests.py index 199d915..af47b76 100644 --- a/zaza/openstack/charm_tests/hacluster/tests.py +++ b/zaza/openstack/charm_tests/hacluster/tests.py @@ -21,17 +21,22 @@ import os import zaza.openstack.charm_tests.test_utils as test_utils import zaza.openstack.configure.hacluster +import zaza.utilities.juju as juju_utils -class HaclusterTest(test_utils.OpenStackBaseTest): - """hacluster tests.""" +class HaclusterBaseTest(test_utils.OpenStackBaseTest): + """Base class for hacluster tests.""" @classmethod def setUpClass(cls): """Run class setup for running hacluster tests.""" - super(HaclusterTest, cls).setUpClass() + super(HaclusterBaseTest, cls).setUpClass() cls.vip = os.environ.get("TEST_VIP00") + +class HaclusterTest(HaclusterBaseTest): + """hacluster tests.""" + def test_900_action_cleanup(self): """The services can be cleaned up.""" zaza.model.run_action_on_leader( @@ -69,3 +74,59 @@ class HaclusterTest(test_utils.OpenStackBaseTest): self._toggle_maintenance_and_wait('true') self._toggle_maintenance_and_wait('false') + + +class HaclusterScalebackTest(HaclusterBaseTest): + """hacluster scaleback tests.""" + + @classmethod + def setUpClass(cls): + """Run class setup for running hacluster scaleback tests.""" + super(HaclusterScalebackTest, cls).setUpClass() + test_config = cls.test_config['tests_options']['hacluster'] + cls._principle_app_name = test_config['principle-app-name'] + cls._hacluster_charm_name = test_config['hacluster-charm-name'] + + def test_930_scaleback(self): + """Remove a unit and add a new one.""" + principle_units = sorted(zaza.model.get_status().applications[ + self._principle_app_name]['units'].keys()) + self.assertEqual(len(principle_units), 3) + doomed_principle_unit = principle_units[0] + other_principle_unit = principle_units[1] + doomed_hacluster_unit = juju_utils.get_subordinate_units( + [doomed_principle_unit], charm_name=self._hacluster_charm_name)[0] + other_hacluster_unit = juju_utils.get_subordinate_units( + [other_principle_unit], charm_name=self._hacluster_charm_name)[0] + + logging.info('Pausing unit {}'.format(doomed_hacluster_unit)) + zaza.model.run_action( + doomed_hacluster_unit, + 'pause', + raise_on_failure=True) + logging.info('OK') + + logging.info('Removing {}'.format(doomed_principle_unit)) + zaza.model.destroy_unit( + self._principle_app_name, + doomed_principle_unit, + wait_disappear=True) + logging.info('OK') + + logging.info('Waiting for model to settle') + zaza.model.block_until_unit_wl_status(other_hacluster_unit, 'blocked') + zaza.model.block_until_unit_wl_status(other_principle_unit, 'blocked') + zaza.model.block_until_all_units_idle() + logging.info('OK') + + logging.info('Adding an hacluster unit') + zaza.model.add_unit(self._principle_app_name, wait_appear=True) + logging.info('OK') + + logging.info('Waiting for model to settle') + zaza.model.block_until_unit_wl_status(other_hacluster_unit, 'active') + # NOTE(lourot): the principle application remains blocked after scaling + # back up until lp:1400481 is solved. + zaza.model.block_until_unit_wl_status(other_principle_unit, 'blocked') + zaza.model.block_until_all_units_idle() + logging.debug('OK')