From 8e55f588c8b4efec3889cbf6a3a6bf590ca98ef5 Mon Sep 17 00:00:00 2001 From: Felipe Reyes Date: Sat, 14 Mar 2020 22:04:48 -0300 Subject: [PATCH 01/77] 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 06ed9eecf02aaeeb4166d3b7b042e030beaa2f0b Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Tue, 16 Jun 2020 17:01:05 -0500 Subject: [PATCH 02/77] 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 03/77] 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 04/77] 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 05/77] 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 06/77] 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 07/77] 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 08/77] 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 fe54ebeb985d94882c788656ba4398e9d2e79224 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Wed, 1 Jul 2020 16:20:29 +0200 Subject: [PATCH 09/77] Minor improvements to keystone tests --- zaza/openstack/charm_tests/keystone/tests.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/zaza/openstack/charm_tests/keystone/tests.py b/zaza/openstack/charm_tests/keystone/tests.py index 9f1d31d..ed5f42a 100644 --- a/zaza/openstack/charm_tests/keystone/tests.py +++ b/zaza/openstack/charm_tests/keystone/tests.py @@ -21,7 +21,7 @@ import keystoneauth1 import zaza.model import zaza.openstack.utilities.exceptions as zaza_exceptions -import zaza.openstack.utilities.juju as juju_utils +import zaza.utilities.juju as juju_utils import zaza.openstack.utilities.openstack as openstack_utils import zaza.charm_lifecycle.utils as lifecycle_utils import zaza.openstack.charm_tests.test_utils as test_utils @@ -262,6 +262,7 @@ class AuthenticationAuthorizationTest(BaseKeystoneTest): openrc['OS_CACERT'] = openstack_utils.KEYSTONE_LOCAL_CACERT openrc['OS_AUTH_URL'] = ( openrc['OS_AUTH_URL'].replace('http', 'https')) + logging.info('keystone IP {}'.format(ip)) keystone_session = openstack_utils.get_keystone_session( openrc) keystone_client = openstack_utils.get_keystone_session_client( @@ -319,10 +320,7 @@ class AuthenticationAuthorizationTest(BaseKeystoneTest): 'OS_PROJECT_DOMAIN_NAME': DEMO_DOMAIN, 'OS_PROJECT_NAME': DEMO_PROJECT, } - with self.config_change( - {'preferred-api-version': self.default_api_version}, - {'preferred-api-version': self.api_v3}, - application_name="keystone"): + with self.v3_keystone_preferred(): for ip in self.keystone_ips: openrc.update( {'OS_AUTH_URL': 'http://{}:5000/v3'.format(ip)}) From 670292c6830d765b779d98ac4111b0a94446fb17 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Wed, 1 Jul 2020 18:05:06 +0200 Subject: [PATCH 10/77] Remove more 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 bb834f0..4c79a7d 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 0df72bf47c88a69a20010255e60bd18a43e4d23c Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Fri, 3 Jul 2020 08:04:09 +0200 Subject: [PATCH 11/77] Make shared resource cleanup method class instance method At present the shared resource cleanup method is a class method. This will prohibit descendents of the class to influence whether cleanup should be run. Remove the @classmethod decorator so that it will make its descisions based on class instance variables set by the descendent. --- zaza/openstack/charm_tests/test_utils.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/zaza/openstack/charm_tests/test_utils.py b/zaza/openstack/charm_tests/test_utils.py index 4b4e1d0..6abb381 100644 --- a/zaza/openstack/charm_tests/test_utils.py +++ b/zaza/openstack/charm_tests/test_utils.py @@ -100,8 +100,7 @@ class BaseCharmTest(unittest.TestCase): run_resource_cleanup = False - @classmethod - def resource_cleanup(cls): + def resource_cleanup(self): """Cleanup any resources created during the test run. Override this method with a method which removes any resources @@ -111,12 +110,13 @@ class BaseCharmTest(unittest.TestCase): """ pass - @classmethod - def tearDown(cls): + # this must be a class instance method otherwise descentents will not be + # able to influence if cleanup should be run. + def tearDown(self): """Run teardown for test class.""" - if cls.run_resource_cleanup: + if self.run_resource_cleanup: logging.info('Running resource cleanup') - cls.resource_cleanup() + self.resource_cleanup() @classmethod def setUpClass(cls, application_name=None, model_alias=None): @@ -440,6 +440,7 @@ class OpenStackBaseTest(BaseCharmTest): cls.nova_client = ( openstack_utils.get_nova_session_client(cls.keystone_session)) + def launch_guest(self, guest_name, userdata=None): """Launch two guests to use in tests. From 29356f6419ef76dd182c63322bdba5edcfcefac9 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Fri, 3 Jul 2020 08:14:50 +0200 Subject: [PATCH 12/77] Move cleanup of launched instances to common class Use the nomenclature for resource cleanup as defined in the `BaseCharmTest`. --- zaza/openstack/charm_tests/neutron/tests.py | 20 +------------------- zaza/openstack/charm_tests/test_utils.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/zaza/openstack/charm_tests/neutron/tests.py b/zaza/openstack/charm_tests/neutron/tests.py index 2ddfda4..620767e 100644 --- a/zaza/openstack/charm_tests/neutron/tests.py +++ b/zaza/openstack/charm_tests/neutron/tests.py @@ -616,24 +616,6 @@ class NeutronNetworkingBase(test_utils.OpenStackBaseTest): super(NeutronNetworkingBase, cls).setUpClass() cls.neutron_client = ( openstack_utils.get_neutron_session_client(cls.keystone_session)) - # NOTE(fnordahl): in the event of a test failure we do not want to run - # tear down code as it will make debugging a problem virtually - # impossible. To alleviate each test method will set the - # `run_tearDown` instance variable at the end which will let us run - # tear down only when there were no failure. - cls.run_tearDown = False - - @classmethod - def tearDown(cls): - """Remove test resources.""" - if cls.run_tearDown: - logging.info('Running teardown') - for server in cls.nova_client.servers.list(): - if server.name.startswith(cls.RESOURCE_PREFIX): - openstack_utils.delete_resource( - cls.nova_client.servers, - server.id, - msg="server") @tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, max=60), reraise=True, stop=tenacity.stop_after_attempt(8)) @@ -803,7 +785,7 @@ class NeutronNetworkingTest(NeutronNetworkingBase): self.launch_guests() instance_1, instance_2 = self.retrieve_guests() self.check_connectivity(instance_1, instance_2) - self.run_tearDown = True + self.run_resource_cleanup = True class NeutronNetworkingVRRPTests(NeutronNetworkingBase): diff --git a/zaza/openstack/charm_tests/test_utils.py b/zaza/openstack/charm_tests/test_utils.py index 6abb381..83595b0 100644 --- a/zaza/openstack/charm_tests/test_utils.py +++ b/zaza/openstack/charm_tests/test_utils.py @@ -440,6 +440,20 @@ class OpenStackBaseTest(BaseCharmTest): cls.nova_client = ( openstack_utils.get_nova_session_client(cls.keystone_session)) + def resource_cleanup(self): + """Remove test resources.""" + try: + logging.info('Removing instances launched by test ({}*)' + .format(self.RESOURCE_PREFIX)) + for server in self.nova_client.servers.list(): + if server.name.startswith(self.RESOURCE_PREFIX): + openstack_utils.delete_resource( + self.nova_client.servers, + server.id, + msg="server") + except AttributeError: + # Test did not define self.RESOURCE_PREFIX, ignore. + pass def launch_guest(self, guest_name, userdata=None): """Launch two guests to use in tests. From b4f155d5e32ff45c50500bbedd073ea0485db223 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Fri, 3 Jul 2020 08:28:38 +0200 Subject: [PATCH 13/77] octavia: Consume common helpers for test instance lifecycle --- zaza/openstack/charm_tests/octavia/setup.py | 19 ------- zaza/openstack/charm_tests/octavia/tests.py | 56 +++++++++++++-------- 2 files changed, 34 insertions(+), 41 deletions(-) diff --git a/zaza/openstack/charm_tests/octavia/setup.py b/zaza/openstack/charm_tests/octavia/setup.py index 55f8903..729fb16 100644 --- a/zaza/openstack/charm_tests/octavia/setup.py +++ b/zaza/openstack/charm_tests/octavia/setup.py @@ -98,25 +98,6 @@ def configure_octavia(): pass -def prepare_payload_instance(): - """Prepare a instance we can use as payload test.""" - session = openstack.get_overcloud_keystone_session() - keystone = openstack.get_keystone_session_client(session) - neutron = openstack.get_neutron_session_client(session) - project_id = openstack.get_project_id( - keystone, 'admin', domain_name='admin_domain') - openstack.add_neutron_secgroup_rules( - neutron, - project_id, - [{'protocol': 'tcp', - 'port_range_min': '80', - 'port_range_max': '80', - 'direction': 'ingress'}]) - zaza.openstack.configure.guest.launch_instance( - glance_setup.LTS_IMAGE_NAME, - userdata='#cloud-config\npackages:\n - apache2\n') - - def centralized_fip_network(): """Create network with centralized router for connecting lb and fips. diff --git a/zaza/openstack/charm_tests/octavia/tests.py b/zaza/openstack/charm_tests/octavia/tests.py index b189484..7049942 100644 --- a/zaza/openstack/charm_tests/octavia/tests.py +++ b/zaza/openstack/charm_tests/octavia/tests.py @@ -48,12 +48,13 @@ class LBAASv2Test(test_utils.OpenStackBaseTest): def setUpClass(cls): """Run class setup for running LBaaSv2 service tests.""" super(LBAASv2Test, cls).setUpClass() - - cls.keystone_session = openstack_utils.get_overcloud_keystone_session() + cls.keystone_client = openstack_utils.get_keystone_session_client( + cls.keystone_session) cls.neutron_client = openstack_utils.get_neutron_session_client( cls.keystone_session) cls.octavia_client = openstack_utils.get_octavia_session_client( cls.keystone_session) + cls.RESOURCE_PREFIX = 'zaza-octavia' # NOTE(fnordahl): in the event of a test failure we do not want to run # tear down code as it will make debugging a problem virtually @@ -63,28 +64,24 @@ class LBAASv2Test(test_utils.OpenStackBaseTest): cls.run_tearDown = False # List of load balancers created by this test cls.loadbalancers = [] - # LIst of floating IPs created by this test + # List of floating IPs created by this test cls.fips = [] - @classmethod - def tearDown(cls): - """Remove resources created during test execution. - - Note that resources created in the configure step prior to executing - the test should not be touched here. - """ - if not cls.run_tearDown: - return - for lb in cls.loadbalancers: - cls.octavia_client.load_balancer_delete(lb['id'], cascade=True) + def resource_cleanup(self): + """Remove resources created during test execution.""" + for lb in self.loadbalancers: + self.octavia_client.load_balancer_delete(lb['id'], cascade=True) try: - cls.wait_for_lb_resource( - cls.octavia_client.load_balancer_show, lb['id'], + self.wait_for_lb_resource( + self.octavia_client.load_balancer_show, lb['id'], provisioning_status='DELETED') except osc_lib.exceptions.NotFound: pass - for fip in cls.fips: - cls.neutron_client.delete_floatingip(fip) + for fip in self.fips: + self.neutron_client.delete_floatingip(fip) + # we run the parent resource_cleanup last as it will remove instances + # referenced as members in the above cleaned up load balancers + super(LBAASv2Test, self).resource_cleanup() @staticmethod @tenacity.retry(retry=tenacity.retry_if_exception_type(AssertionError), @@ -238,12 +235,27 @@ class LBAASv2Test(test_utils.OpenStackBaseTest): def test_create_loadbalancer(self): """Create load balancer.""" - nova_client = openstack_utils.get_nova_session_client( - self.keystone_session) + # Prepare payload instances + # First we allow communication to port 80 by adding a security group + # rule + project_id = openstack_utils.get_project_id( + self.keystone_client, 'admin', domain_name='admin_domain') + openstack_utils.add_neutron_secgroup_rules( + self.neutron_client, + project_id, + [{'protocol': 'tcp', + 'port_range_min': '80', + 'port_range_max': '80', + 'direction': 'ingress'}]) + + # Then we request two Ubuntu instances with the Apache web server + # installed + instance_1, instance_2 = self.launch_guests( + userdata='#cloud-config\npackages:\n - apache2\n') # Get IP of the prepared payload instances payload_ips = [] - for server in nova_client.servers.list(): + for server in (instance_1, instance_2): payload_ips.append(server.networks['private'][0]) self.assertTrue(len(payload_ips) > 0) @@ -274,4 +286,4 @@ class LBAASv2Test(test_utils.OpenStackBaseTest): lb_fp['floating_ip_address'])) # If we get here, it means the tests passed - self.run_tearDown = True + self.run_resource_cleanup = True From 37dfa53bafec129d167331dc839e083a0fdd8d66 Mon Sep 17 00:00:00 2001 From: Chris MacNaughton Date: Fri, 3 Jul 2020 14:44:08 +0200 Subject: [PATCH 14/77] ensure that we add the bare cinder endpoint When there is a cinderv2 or cinderv3 endpoint, we should enable the bare cinder bits in the tempest config. --- zaza/openstack/charm_tests/tempest/setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zaza/openstack/charm_tests/tempest/setup.py b/zaza/openstack/charm_tests/tempest/setup.py index 1cdaf1e..af1ee4c 100644 --- a/zaza/openstack/charm_tests/tempest/setup.py +++ b/zaza/openstack/charm_tests/tempest/setup.py @@ -235,6 +235,9 @@ def get_tempest_context(): 'cinder': add_cinder_config, 'keystone': add_keystone_config} ctxt['enabled_services'] = get_service_list(keystone_session) + if set(['cinderv2', 'cinderv3']) \ + .intersection(set(ctxt['enabled_services'])): + ctxt['enabled_services'].append('cinder') ctxt['disabled_services'] = list( set(TEMPEST_SVC_LIST) - set(ctxt['enabled_services'])) add_application_ips(ctxt) From 849e08528b4e57a23f4c342c501f6927a5b98ebe Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Fri, 3 Jul 2020 10:27:24 +0200 Subject: [PATCH 15/77] octavia: fix pause/resume test and temporarily disable it Re-enable when LP: #1886202 is fixed. --- zaza/openstack/charm_tests/octavia/tests.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/octavia/tests.py b/zaza/openstack/charm_tests/octavia/tests.py index 7049942..e84ac3e 100644 --- a/zaza/openstack/charm_tests/octavia/tests.py +++ b/zaza/openstack/charm_tests/octavia/tests.py @@ -38,7 +38,20 @@ class CharmOperationTest(test_utils.OpenStackBaseTest): Pause service and check services are stopped, then resume and check they are started. """ - self.pause_resume(['apache2']) + services = [ + 'apache2', + 'octavia-health-manager', + 'octavia-housekeeping', + 'octavia-worker', + ] + if openstack_utils.ovn_present(): + services.append('octavia-driver-agent') + logging.info('Skipping pause resume test LP: #1886202...') + return + logging.info('Testing pause resume (services="{}")' + .format(services)) + with self.pause_resume(services, pgrep_full=True): + pass class LBAASv2Test(test_utils.OpenStackBaseTest): From d0981d64af57b062376de6676bc4f846ba74f5d8 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 7 Jul 2020 08:57:40 +0000 Subject: [PATCH 16/77] Add auto_initialize_no_validation_no_wait When vault is in its own model with no clients then vault needs to be initialised without waiting for clients to start executing and without validating a client has recieved the cert. To achieve this, this PR adds auto_initialize_no_validation_no_wait. --- zaza/openstack/charm_tests/vault/setup.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/zaza/openstack/charm_tests/vault/setup.py b/zaza/openstack/charm_tests/vault/setup.py index 0709162..4db90b5 100644 --- a/zaza/openstack/charm_tests/vault/setup.py +++ b/zaza/openstack/charm_tests/vault/setup.py @@ -16,6 +16,7 @@ import base64 import functools +import logging import requests import tempfile @@ -99,7 +100,7 @@ async def async_mojo_unseal_by_unit(): unit_name, './hooks/update-status') -def auto_initialize(cacert=None, validation_application='keystone'): +def auto_initialize(cacert=None, validation_application='keystone', wait=True): """Auto initialize vault for testing. Generate a csr and uploading a signed certificate. @@ -114,6 +115,7 @@ def auto_initialize(cacert=None, validation_application='keystone'): :returns: None :rtype: None """ + logging.info('Running auto_initialize') basic_setup(cacert=cacert, unseal_and_authorize=True) action = vault_utils.run_get_csr() @@ -131,10 +133,11 @@ def auto_initialize(cacert=None, validation_application='keystone'): root_ca=cacertificate, allowed_domains='openstack.local') - zaza.model.wait_for_agent_status() - test_config = lifecycle_utils.get_charm_config(fatal=False) - zaza.model.wait_for_application_states( - states=test_config.get('target_deploy_status', {})) + if wait: + zaza.model.wait_for_agent_status() + test_config = lifecycle_utils.get_charm_config(fatal=False) + zaza.model.wait_for_application_states( + states=test_config.get('target_deploy_status', {})) if validation_application: validate_ca(cacertificate, application=validation_application) @@ -163,6 +166,12 @@ auto_initialize_no_validation = functools.partial( validation_application=None) +auto_initialize_no_validation_no_wait = functools.partial( + auto_initialize, + validation_application=None, + wait=False) + + def validate_ca(cacertificate, application="keystone", port=5000): """Validate Certificate Authority against application. From 8f44ab681ae6ed44939038726c914d77e0d4d4f4 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 7 Jul 2020 09:03:36 +0000 Subject: [PATCH 17/77] Add wait_for_cacert wait_for_cacert will wait for keystone to recieve and install a cacert. This is particularly useful when the certificate issuer is in a different model. --- zaza/openstack/charm_tests/keystone/setup.py | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/zaza/openstack/charm_tests/keystone/setup.py b/zaza/openstack/charm_tests/keystone/setup.py index 6dbb7c1..748439f 100644 --- a/zaza/openstack/charm_tests/keystone/setup.py +++ b/zaza/openstack/charm_tests/keystone/setup.py @@ -14,8 +14,12 @@ """Code for setting up keystone.""" +import logging + import keystoneauth1 +import zaza.charm_lifecycle.utils as lifecycle_utils +import zaza.model import zaza.openstack.utilities.openstack as openstack_utils from zaza.openstack.charm_tests.keystone import ( BaseKeystoneTest, @@ -30,6 +34,25 @@ from zaza.openstack.charm_tests.keystone import ( ) +def wait_for_cacert(model_name=None): + """Wait for keystone to install a cacert. + + :param model_name: Name of model to query. + :type model_name: str + """ + logging.info("Waiting for cacert") + zaza.model.block_until_file_has_contents( + 'keystone', + openstack_utils.KEYSTONE_REMOTE_CACERT, + 'CERTIFICATE', + model_name=model_name) + zaza.model.block_until_all_units_idle(model_name=model_name) + test_config = lifecycle_utils.get_charm_config(fatal=False) + zaza.model.wait_for_application_states( + states=test_config.get('target_deploy_status', {}), + model_name=model_name) + + def add_demo_user(): """Add a demo user to the current deployment.""" def _v2(): From 9c52b3390d4c6d391b002fba15ff3ce5c3324d83 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 7 Jul 2020 09:06:35 +0000 Subject: [PATCH 18/77] Add LTSGuestCreateVolumeBackedTest Add a test class which launches a volume backed instance. --- zaza/openstack/charm_tests/nova/tests.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zaza/openstack/charm_tests/nova/tests.py b/zaza/openstack/charm_tests/nova/tests.py index 1b12240..c4dd5b4 100644 --- a/zaza/openstack/charm_tests/nova/tests.py +++ b/zaza/openstack/charm_tests/nova/tests.py @@ -56,6 +56,16 @@ class LTSGuestCreateTest(BaseGuestCreateTest): glance_setup.LTS_IMAGE_NAME) +class LTSGuestCreateVolumeBackedTest(BaseGuestCreateTest): + """Tests to launch a LTS image.""" + + def test_launch_small_instance(self): + """Launch a Bionic instance and test connectivity.""" + zaza.openstack.configure.guest.launch_instance( + glance_setup.LTS_IMAGE_NAME, + use_boot_volume=True) + + class NovaCompute(test_utils.OpenStackBaseTest): """Run nova-compute specific tests.""" From a829b372e0d6c5bb3e759a40b84e83ac56e409ee Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 7 Jul 2020 09:56:25 +0000 Subject: [PATCH 19/77] Use model tmpdor for key storage Use the model specific tmp dir to store the test ssh private key. This avoids the key being overwritten in CMR tests. Depends-On: https://github.com/openstack-charmers/zaza/pull/371 --- zaza/openstack/utilities/openstack.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/utilities/openstack.py b/zaza/openstack/utilities/openstack.py index 28ba29a..034cae3 100644 --- a/zaza/openstack/utilities/openstack.py +++ b/zaza/openstack/utilities/openstack.py @@ -2202,7 +2202,8 @@ def get_private_key_file(keypair_name): :returns: Path to file containing key :rtype: str """ - return 'tests/id_rsa_{}'.format(keypair_name) + tmp_dir = deployment_env.get_tmpdir() + return '{}/id_rsa_{}'.format(tmp_dir, keypair_name) def write_private_key(keypair_name, key): From 76fdb61e0f1b364713a4ecbc474fd3f8b900deb9 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 7 Jul 2020 12:18:01 +0000 Subject: [PATCH 20/77] Fix up unit tests --- .../.test_zaza_utilities_openstack.py.swp | Bin 0 -> 73728 bytes .../utilities/test_zaza_utilities_openstack.py | 10 ++++++++-- zaza/openstack/utilities/.openstack.py.swp | Bin 0 -> 16384 bytes 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 unit_tests/utilities/.test_zaza_utilities_openstack.py.swp create mode 100644 zaza/openstack/utilities/.openstack.py.swp diff --git a/unit_tests/utilities/.test_zaza_utilities_openstack.py.swp b/unit_tests/utilities/.test_zaza_utilities_openstack.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..2ef70f74efe45a7a7db302b8b0e7266b3d9ec4d8 GIT binary patch literal 73728 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%0AjUXHRl4eUJnLYj8_^v1 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 literal 0 HcmV?d00001 diff --git a/unit_tests/utilities/test_zaza_utilities_openstack.py b/unit_tests/utilities/test_zaza_utilities_openstack.py index a6aa190..b0d76b0 100644 --- a/unit_tests/utilities/test_zaza_utilities_openstack.py +++ b/unit_tests/utilities/test_zaza_utilities_openstack.py @@ -581,17 +581,23 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): nova_mock.keypairs.create.assert_called_once_with(name='mykeys') def test_get_private_key_file(self): + self.patch_object(openstack_utils.deployment_env, 'get_tmpdir', + return_value=False) + self.get_tmpdir.return_value = '/tmp/zaza-model1' self.assertEqual( openstack_utils.get_private_key_file('mykeys'), - 'tests/id_rsa_mykeys') + '/tmp/zaza-model1/id_rsa_mykeys') def test_write_private_key(self): + self.patch_object(openstack_utils.deployment_env, 'get_tmpdir', + return_value=False) + self.get_tmpdir.return_value = '/tmp/zaza-model1' m = mock.mock_open() with mock.patch( 'zaza.openstack.utilities.openstack.open', m, create=False ): openstack_utils.write_private_key('mykeys', 'keycontents') - m.assert_called_once_with('tests/id_rsa_mykeys', 'w') + m.assert_called_once_with('/tmp/zaza-model1/id_rsa_mykeys', 'w') handle = m() handle.write.assert_called_once_with('keycontents') diff --git a/zaza/openstack/utilities/.openstack.py.swp b/zaza/openstack/utilities/.openstack.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..8928f8e097d100e8cc7cc6552fcb436d622ccf75 GIT binary patch literal 16384 zcmeHOZHy#E8E*OT1ov%

9!J($7W~T3Y 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 Date: Tue, 7 Jul 2020 12:24:32 +0000 Subject: [PATCH 21/77] Remove swap files --- .../.test_zaza_utilities_openstack.py.swp | Bin 73728 -> 0 bytes zaza/openstack/utilities/.openstack.py.swp | Bin 16384 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 unit_tests/utilities/.test_zaza_utilities_openstack.py.swp delete mode 100644 zaza/openstack/utilities/.openstack.py.swp diff --git a/unit_tests/utilities/.test_zaza_utilities_openstack.py.swp b/unit_tests/utilities/.test_zaza_utilities_openstack.py.swp deleted file mode 100644 index 2ef70f74efe45a7a7db302b8b0e7266b3d9ec4d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73728 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%0AjUXHRl4eUJnLYj8_^v1 zW5>pZM@nYe5Nb@F8jZw~s+2HKE=5ehW4~LsTg*Z6tygauneg^HC}W$Ym?0^cj`fy>;%Hz1Vv{ zC$!C;>RWqM=-^9i#Lb9W6du=!z4(3c(v4GuBE4re+zi_@#N1yTm{mk}EPm(m#797? zjrfsb#h=HmL$OD@{woG}%%ZmwmnyUHF}~WH+(ihuFNI~w+ofGbqrLDeUUaL-J|hy! z9Po%&f^x$$dTQM}=r${bHeLbI`qF}?f*pysz_VM=ZmFFqteo^)L{*y6?@QL*=xy8& zl;TWPW6DuiWQTWEN1FA>{yv~O?Clhjjh9!TOmv_+@`eqgvbjY=POk{Y#A>(~d)-Qj z<%&+^#ODfADwO{(B-TIEqSIt%IL#KO(7lWH=eEu~=fYXU*RO&zz>|TT zHSjR-ee`=-?=N%yF9f^6CLn7AZbHYegNKqF8|ly9pFkJy1ks` zC+h%S4K4#u0Z#qmj`_xZx(*yYQBu|p+iqKKK#+lR*< z9(m8COA7@+-@^7(Xtw11rR>y>xt$kJ>y~8%YAwrYBC`KvmLnmy4ULQsjcl149pAob zbo=J5mnFqX>P6#(cM2t;4{-)nx$sJ-P8$E!p3XI@=Bd&Awj-S zY`QB>+qRqK0Fa@5oHJHmaak;2XsJPw+u8HQfyv*fHWh@GPrHHJw1p+o4gfv0YBD>g zg}?u=4Q66WOXGcak7om`|Xi!)5BcFK_0=D3zz{J0;a6m%i1p4_k+@wQa;Y zt4?zCV3oGKG`4(^eWezYd$`WMTT;)aS)oU#htQ*KNbi)xjyU~_ld-4->A}6-vO_l< zIsSvRGc3DO&st=ZM_b`lQjkfK1HN*9o!642pHsw6dA zw#8Bsq}kizk${$5q-<6%IPX2w8m;#`$8PeI0oAVFN8Nkcsmt<0z+?htydP@c`b;X| zksm_JFJYF}yc;?|NVx1_-c)nchA{{uy&O{JMbcVy7|DAOtx0+RdFzpkgu)o>5+bou zMC)DGO7wo08Vs`6BH&3x$S0+H$6UsQi$PRN{Ozjs3Db4!-^k_NYPkmQ4+h6Y21W3M z)jP*ExaXFx3`z3ub*ld$nyy~|j;`Mzh$kcdFH1;*B6iA3F-Y1)V5>D}1Z|aNEG&e! zX|&zpUUnWxOx2i+Q`RnD3Z+uQF~8MP?Y=8ktx0l{R3e$<9M>Wimfo;}be?LB7}&z- zp>3hpIZkg9EH=eyO6od`B>F$#3AbP*}`Tsc(-G3O|iJmWe z`+XF=3Y5S$uoWB!?m*Z71b72j2AjYm!M~vEe*(M^Q zvfwU?=|C-#3Xayvt}Mx`!(7Oq3SxFIDM?+f!x@sVLyNnwO$L!yMAz$b+#c1$C9%E29_n_})-(r+jORopra0aCoRZAt(s37D z+%wiBwonk(KuQIRgw(cIjyP5){1}$hp!glT89&fiuLN(3XE2uQmEbYM(Bf66;WrVK zf26Z?jzBF`tY3}rM zx1YXi`{}cnWeq>jNKni2KB6q|THJ%M!MWeL@C)vxc=Ihe+Eg5bO|sp{GhOUx`gWtX zjR0|rc`C?i9oFd?UNePy^8v9<2R7;4gMG>)Ru$6i9s1Nq&!%aq@57L4Z$2q1b&_N# z%;A^5RBN-e(b2^%m3@&G4&-AEA`t2PlH)WB{`2-ze7*J5_WWp4FCB_-_$67ElX~Z* zp7}6(Pz}59s47d`(Q;Qc_({gb)> zM+4FQuLH6N;5?A||D(Yd(ewWlTn=V|?By@}{=FZ(9;^Y;`%eQWgRi6OzX!-3fU`i> z^?x5d|3lyA52I04*^o_`nkFmS;DxC7n(ec+{FKbQs^z%S78WqrS_?YH;-6BbJ^ z7+h-i2WI3M_Ls1`%JOBc7vKNSg|WnHp_=_#z^1!b!&4zi3UJ3KySrz~~^ zv2!%zgO1DqjF^i>FrAjcEjyS)hwr9x*>gb#&ps%D0CkYIIV?O09*n0c>55X&@)s zpyvqWFHeXUu@|F($B;1Y%2;A&2$`}#D{3}U1!dm_c2&G^X3u%kJLarPmH^qQ3wP!( zo}QWAxo0<3G=63LHIE}e-cKNZ)}CEcJ9jG+l2oX#Fy=igZc_45vy&00(I#||lUSE} z2rh=GL!#nkw;lzOm|- z@wG@v4VjlzMPOBvp#E;Kem670Kn<(f@Cx>Ulo`!xJ4uZruzGltJjP)Hk%ZJ~McyP_ zT-G>J7q4Zvxi;jBg++ySvW`-hRiz!G&pKe9$JVKDXlF_diM3z9Nkxt}Ew;`Sru$?> z%^tBv66PaZ?$AEGGkz0WhZ!r7brsy^8daVoUSbo!B%oI-Hm6xRvZqec3pPn#M;&? zF14%6Z0vqzZQ54eR+`vaTG;Ar-85b<7q*U;Mz)P?+cL7H=!{Jij3mP56%IPo?iu|t znXF?`GkVsGLl|aH>m*H!I=Ev!eeGp^v^?=A@bbf0`(#Twg=qe>b=q z$Qb~e!Li`$==pN)Ulp7KMEAcNJ^!!4yFmf`Hh2X1F}l92`M&{({=XmOfEXNZMc=;` z90Zqwjo^47Hh`aizXN{=E&(Tl&!h8S1 zK-U-je*x?UvabKL@KX5sGU4fZE*WmfrR1%%l9{coR8iLP(u(Yq)=SLNN}WS1y$LF- zPwA2;r(SXfhO8an+#)s}tg_A=%K|g(`+1KVe)1de@Jz2d>BXA%;oP7+JJs%P=>jgG zl}dVNb}5a`gShyC;`-UF;kwOK9e!Tv1#uItmPjQ_NJ24TTG#soY)GHI^Xa>$w|fzN zx6y)HJuYWVrRAmLCC0)50y*h&tBTt{E~k(t8I*kAx^T+J+bf|}ck+uAakU_aG~@Wk zkSV4)D>i$oMDi}WM(vn8fv9+*Q5cRx)^_Rh=R0d^kM_ZI@J&yd8u=#ffqF!;75(gf z$x!sLdZ51L7JG+ag%RMPx!zLc7!> zmmZ0=-8>aJx~)Wsm0wCiJ2JqQv`+PJng&ynd?@MBlG$}FK|@)WVc-fD3pR$g^OjUP z5*yT3ds|PuCX$AowUtIt3COfmru8D~PKi|&Dv^{FH2J!JDJ1|rEW{E~N$rc_pms<``BD7*7>(QJR1P}Zz#a6T9 z{Bs%?FODr63VF~JB;|G3i4AYh4#uZ|hW0kN@d@9mJeISqP1?k!WL27b;NKbO%H8`K zp`dS9mdTef%M97OB-w`9X_vi-4%GH7?<@H2G&Tfv`!MX(wC5MBRYz+ZtEgQtND!4@F< z{@)0iU>wNVfWHYoM0sDdY06Yxbg1-Mn@Ii0@hC z_+K){s9wnO0~zNF_!0fJOIWEjxqceh@$5mTPql5e&_0xYR4KkeOK!QgJRobbVtGhF z@<}|UK$S?xkYWVIYhX4tw`-3V&)f6eCnvW5jec)#NS{@6yw$R6PBX3SDwh28QgMmP z^ByF1wjn)Lk-b;fywW$U;>(}{J}O1Sp5hB+Pd!XY4aueM_R#8rRPCnLPRb-*+xFo5 z`s5qZB71Fhkw}No_9F59h7`FspNafDgw~jps+#xL4I+|7`^Fjxrdw*{w%1-Gw=#PZ zT5Tjm`?ebirU@3=h%%dpE-j=*WIm>l^bK{e2w`-d({;E*&hZsIiexdYvcV z<}>o!Oi8ETzO@Ho^6Z_Rk_IQ2#ib~v3C%o#d14662Z_7O@iu6b-fq>0`i|Av1vS-M zlhpB!OeM+u#eG{r|5F=az)6uN5eoC|n;;~93lq5wYAn{aR5S^p-wW!NIkuI@e$~m> zcuNR{Ix;phqNrD8@S77|{5GcZlyuv-B6(^xACr)EG#QDO&{))MYY=;=%|y})*xg$L zkr)AU`1(Ne_kXJN3!7qMABvuN&{<0K|3P%pH=&=({C}YT-++$4A6x>C2cJa0m%aa1 zfb98yH25I;{Q)or{tbQpR`5!&0~`%Li!T3iPy`F$N#JMb^s>L-)nEba1mfp^Joqbg z`fI@=I14-pJQn;K_zAlGmw~MJe+QTYli*?C)9ClIuYVn!4ITsJ9KRocZvdJ5uYson z(fz-Jo_`|{{eLg`9U%MteGa?=RKPRAG`JW2{^Q`y;7yOl$!QaEzHv{4C8->T~xunYEQvHlN+TGhbIpSMB&}dZh;vb|Ah4wlMYhK87 zMb8JpV*=7Smz6Qu;=?<5I*kKK=y=~_wq}%kf-`#O+|AgBkl~@1wL3r8U6!s%#;EnA z!1(X2)YdPB*o(-ve~2y|HJP%dNynyBTD?HnB?NQHo!z^%V`zhpaRFg=rGg$}~5(@{Ezwtk`x zV}>WJRLkHV5Yx>tFX5~!hj$ETgby_9g=)ilyZZ0grtIOV+*U#>f|+2%wCyzqBS(FN+u%qt!NsaOp_pv4 zEMrIox1CDES*IW#`MJ3xQFA>`s|4jXJ|CQw?Ct!|UPnerIB`i=vdk)m59xZmH&nE) zL%9~QQ0v*k7?P~WI#0wMKD-|n7zw#Id@jF&OL;J*f?wvRIvaM!%-mU1b5jY;HI#kB z5iIJNH0cIM-Wxf2{clnt;I+~N|bnceY1a< zrdJQ5qqNs)u-Obo29sV~l>b26QI5BRx_PNOHH|T0U*YenFLE8SQBAwZwtg9Zd})YH z*rTj_U=n+0ZQuZ|U~)HL%%KwMjWv%5o%*aa(f^-0x9)My4 z_zm!7bpGE5Vgr~5r-M7u{r??^EkNe{)8JGf^Zx$_l)-u6H^9l@8`uC|3gn!>5|DHL zzJ%^C_JCdBap0@y`(g*U9880wfcW`;9=sh~0!{>*A z-vu{;KLB-bK9K$WQb6YT-vH*oHgGEV8an-F!DqnRKpl*MAEV2E0K5^%-hL;8Uk5*e zZ=&N1|I``^pMQt;5r~L<+TvSw*}1;8(5g0Dd3TA^st2+QxU;(~A53Qpg?e${#HQ|B zw}qC0cyc0_rkzxX2-XRgYO7TyD@wVdTd&s`I>^#?nY?G@KkVNd9UJn=7-D~>{W*8Z zToTZ>NM7uqNIe%s(reW#NV3`hknw_YNfxdRY~+0ft=4OQAv-#z2RUIQk0fHpi9A#} zq$FdxR02Kp|B)n&)*HF6l4c~T%3vNsWOZne&!3ch2FA`P>s0JCj7mJt(rIK8%%3r z$MmQ^l2*Hlkg^)FbN)uVw8Kh8OO_F$w}eJ@LKHnExyYr{X}hUeC>aTLYKw-F*9&E- z{JE^FsW|n(^d6h(IOu6g_>}s)1T-$kb$=~;rDbqhXt|Y=-}>4nX6C3QL?@CYn2}at zuZ=L%vRkV8EZR_GPde3#$9nU>DNB>Aif9hZP0!Bd&z+mQP;66AzjV@KQ=1*%yfoFg zmvvN?+M1lyCZn+?({#purd|7$oDm&Yer+}D9@FD4tiyuRprL2XDm410Lr6*Z;S1K` zC1h5YpHf83BN8I})f`xh8B7)Z21RY9QwyVtVzQ%c93$Ps6=)+VTb_+aQE|2(9BFA5 zWw$TFmSA*x&*x&TYF#3nEWA-=rOQ6h>)isH15|@4A*~R3dC=vig5FOx2_eJ?FU!+y zQBB@WIdk^Z`P0f$tW5-6`xRejwlZ8#hvKJTmn*R+>A(h|)E*26eCh2oacN1&Qk%ZE zy^UYDN?)DyyajcSpY#UR(>!(_iFK=kcdyz6>cx98CJ&0(P>ejH>83(eTR)OK<;h{l zO0)onB`Q*w!%DzwLrJoxsf^W%EozF(Z7n0dB}MFohA~8bdRwMu$_s#{8@3 z|ETe)D}4Pw*#Ga7==-k#F9**CXMlSM_z_S8PXymU&wn+@gCQ^o{u>?ttKb^&6fg;n z1OJGgFZ=z8&M*7@KN19M{Xd4z{~92^0FMFppznVV{2lmP@M<9E{yhQw2)$qY{a*(T zf;@O87z01V2Jn8c4{QW-=HJh-1Nh3wS4Z2RH!sfQNuvu?5K4f3go?89Wi(1y8RB!dtbzB4QK>dJ(U> zS;=2()zQJ~&Jwj&YTF-#ZO3l=b_xS5iS*J`OPfbVOXZ2p1;bd^CS!BFOVf-Prd5(V5X%op=e`>{2`xoIa)2+0)BQhH^!c^v*T1 zxG`@j=x%A!$XKhBk(Anv(88t~E1cTQG{%=B!jlMPJ(qEzuyt%?)53_k^L!+w^_MYL zJgEV-1+~l865gJUON9e@+3R|8Y(x*p!f?zhGgf8eIJTi!;s9`$)5V=-ZINI}-sX+}hP!NFT+zmxo)kTSMobUP+T=+X>cKXW4q$>G~Q zGAltaB1s)KuiDFG!pK2bC|868%@%pIe%9Pya@VTfhmu!Dremz^nHN!h8ckkzypbU5 z5TN_6%(3E1^#?{@Z#rhUsHVFZhI$*wjasu`s<*~YB<~>CS#i3L6jThiN?*bmJHp=% z2K%T`a%{TF{qZpmW|8ZNGQRwxk*71a*ys* zQZq=6oHuElMj-B9bK=ufD0=NU)z~6xD!5hHq!2z%xoD6QIkZ%Rg1&iPu za5nf~;8yGe*MWmT_6x`WIXB>w*a@x&{~Hv*6nGN&AvS_9f@{H5;2Gc)a1{6vHiB=1 zH-Q5{>;_}t5#ZtAHf#kq0NF!eJNPMfg1-R22lj%qz-iz_a0hiF_3~b+8~>78mP^TO zREm1+Q`AcF*vcN|O_Ned`~R|P9yWR_v9pK-@#j&MP98q6`yhY0l@d=&iJ%>ej#hH( zb_33=x(f@HwRISibCOcotzan2=yZ*S+e^c5F;!P6dA()6S<8C`TxTNDids)(y!>)> zO&su;@hZ-UZ1B2khhwrl&ra6Z7ZsNi-)$lR>~Aoc-6?bGINcVSE%tLPxaK}x5=bu3 zyPGW`!(lJmEUqN(Y`?8EIy$zwP)=}&HOk`8;ufOs$u}&bOM_{z z#GYeeN}|i92I)$nQzHVM-%`-VXxUYuC`s)Lm4=O7}fL{RF)Cj*%V1_K+scJtM&3GTEZsW^RP8B$jGpkS0&D zGE`loZ7HnO=zCakF?`nZOhXt$H4Bf}w#?TC22E<4dI-|(#gwl|?=nUY(nCVj9Eu@# z;Ug(RyV?we-Kws{cchq=CmV%=M?m%KtJSK01GrYusyT~)Rj zu}-oVL|ev&!6n*y1wx0nvJG2Pz_i}-Pco|O`W6b?Tk z2@Nt`7o}7Ot-+~V3=P`Ik)%&JRURnR7e@!v{2x>Q$92ZiDFoyUuqGiEa=Ya))1{M& fNt*pKuqCyJ>5CwWVP}?N>8@Cdl)4{=A58s!W(kc5 diff --git a/zaza/openstack/utilities/.openstack.py.swp b/zaza/openstack/utilities/.openstack.py.swp deleted file mode 100644 index 8928f8e097d100e8cc7cc6552fcb436d622ccf75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeHOZHy#E8E*OT1ov%

9!J($7W~T3Y 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 Date: Tue, 7 Jul 2020 12:32:54 +0000 Subject: [PATCH 22/77] Fix another unit test --- unit_tests/utilities/test_zaza_utilities_openstack.py | 8 ++++---- 1 file 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 b0d76b0..0b504b1 100644 --- a/unit_tests/utilities/test_zaza_utilities_openstack.py +++ b/unit_tests/utilities/test_zaza_utilities_openstack.py @@ -582,16 +582,14 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): def test_get_private_key_file(self): self.patch_object(openstack_utils.deployment_env, 'get_tmpdir', - return_value=False) - self.get_tmpdir.return_value = '/tmp/zaza-model1' + return_value='/tmp/zaza-model1') self.assertEqual( openstack_utils.get_private_key_file('mykeys'), '/tmp/zaza-model1/id_rsa_mykeys') def test_write_private_key(self): self.patch_object(openstack_utils.deployment_env, 'get_tmpdir', - return_value=False) - self.get_tmpdir.return_value = '/tmp/zaza-model1' + return_value='/tmp/zaza-model1') m = mock.mock_open() with mock.patch( 'zaza.openstack.utilities.openstack.open', m, create=False @@ -602,6 +600,8 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): handle.write.assert_called_once_with('keycontents') def test_get_private_key(self): + self.patch_object(openstack_utils.deployment_env, 'get_tmpdir', + return_value='/tmp/zaza-model1') self.patch_object(openstack_utils.os.path, "isfile", return_value=True) m = mock.mock_open(read_data='myprivkey') From 7f122b8611198fc8b36f9a8ce6942e7224b1197c Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 7 Jul 2020 12:38:18 +0000 Subject: [PATCH 23/77] Last unit test fix (passing locally, was failing in travis) --- unit_tests/utilities/test_zaza_utilities_openstack.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unit_tests/utilities/test_zaza_utilities_openstack.py b/unit_tests/utilities/test_zaza_utilities_openstack.py index 0b504b1..549ed71 100644 --- a/unit_tests/utilities/test_zaza_utilities_openstack.py +++ b/unit_tests/utilities/test_zaza_utilities_openstack.py @@ -613,6 +613,8 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): 'myprivkey') def test_get_private_key_file_missing(self): + self.patch_object(openstack_utils.deployment_env, 'get_tmpdir', + return_value='/tmp/zaza-model1') self.patch_object(openstack_utils.os.path, "isfile", return_value=False) self.assertIsNone(openstack_utils.get_private_key('mykeys')) From 7265b79769c05abc28373da0e5ecd60df8c87b17 Mon Sep 17 00:00:00 2001 From: David Ames Date: Wed, 8 Jul 2020 10:27:01 -0700 Subject: [PATCH 24/77] Fixes for MySQL testing * Better handle workload state checking during cold start * Switch from debug to info to see log messages --- zaza/openstack/charm_tests/mysql/tests.py | 53 +++++++++++------------ 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/zaza/openstack/charm_tests/mysql/tests.py b/zaza/openstack/charm_tests/mysql/tests.py index 7bf0979..bf61932 100644 --- a/zaza/openstack/charm_tests/mysql/tests.py +++ b/zaza/openstack/charm_tests/mysql/tests.py @@ -149,7 +149,7 @@ class MySQLCommonTests(MySQLBaseTest): set_alternate = {"max-connections": "1000"} # Make config change, check for service restarts - logging.debug("Setting max connections ...") + logging.info("Setting max connections ...") self.restart_on_changed( self.conf_file, set_default, @@ -198,7 +198,7 @@ class PerconaClusterBaseTest(MySQLBaseTest): output = zaza.model.run_on_leader( self.application, cmd)["Stdout"].strip() value = re.search(r"^.+?\s+(.+)", output).group(1) - logging.debug("%s = %s" % (attr, value)) + logging.info("%s = %s" % (attr, value)) return value def is_pxc_bootstrapped(self): @@ -236,7 +236,7 @@ class PerconaClusterBaseTest(MySQLBaseTest): cmd = "ip -br addr" result = zaza.model.run_on_unit(unit.entity_id, cmd) output = result.get("Stdout").strip() - logging.debug(output) + logging.info(output) if self.vip in output: logging.info("vip ({}) running in {}".format( self.vip, @@ -333,12 +333,12 @@ class PerconaClusterColdStartTest(PerconaClusterBaseTest): juju_utils.get_machine_uuids_for_application(self.application)) # Stop Nodes # Avoid hitting an update-status hook - logging.debug("Wait till model is idle ...") + logging.info("Wait till model is idle ...") zaza.model.block_until_all_units_idle() logging.info("Stopping instances: {}".format(_machines)) for uuid in _machines: self.nova_client.servers.stop(uuid) - logging.debug("Wait till all machines are shutoff ...") + logging.info("Wait till all machines are shutoff ...") for uuid in _machines: openstack_utils.resource_reaches_status(self.nova_client.servers, uuid, @@ -357,7 +357,7 @@ class PerconaClusterColdStartTest(PerconaClusterBaseTest): 'unknown', negate_match=True) - logging.debug("Wait till model is idle ...") + logging.info("Wait till model is idle ...") # XXX If a hook was executing on a unit when it was powered off # it comes back in an error state. try: @@ -366,7 +366,7 @@ class PerconaClusterColdStartTest(PerconaClusterBaseTest): self.resolve_update_status_errors() zaza.model.block_until_all_units_idle() - logging.debug("Wait for application states ...") + logging.info("Wait for application states ...") for unit in zaza.model.get_units(self.application): try: zaza.model.run_on_unit(unit.entity_id, "hooks/update-status") @@ -389,7 +389,7 @@ class PerconaClusterColdStartTest(PerconaClusterBaseTest): _non_leaders[0], "bootstrap-pxc", action_params={}) - logging.debug("Wait for application states ...") + logging.info("Wait for application states ...") for unit in zaza.model.get_units(self.application): zaza.model.run_on_unit(unit.entity_id, "hooks/update-status") states = {"percona-cluster": { @@ -403,7 +403,7 @@ class PerconaClusterColdStartTest(PerconaClusterBaseTest): self.application, "notify-bootstrapped", action_params={}) - logging.debug("Wait for application states ...") + logging.info("Wait for application states ...") for unit in zaza.model.get_units(self.application): zaza.model.run_on_unit(unit.entity_id, "hooks/update-status") test_config = lifecycle_utils.get_charm_config(fatal=False) @@ -532,12 +532,12 @@ class MySQLInnoDBClusterColdStartTest(MySQLBaseTest): juju_utils.get_machine_uuids_for_application(self.application)) # Stop Nodes # Avoid hitting an update-status hook - logging.debug("Wait till model is idle ...") + logging.info("Wait till model is idle ...") zaza.model.block_until_all_units_idle() logging.info("Stopping instances: {}".format(_machines)) for uuid in _machines: self.nova_client.servers.stop(uuid) - logging.debug("Wait till all machines are shutoff ...") + logging.info("Wait till all machines are shutoff ...") for uuid in _machines: openstack_utils.resource_reaches_status(self.nova_client.servers, uuid, @@ -550,38 +550,37 @@ class MySQLInnoDBClusterColdStartTest(MySQLBaseTest): for uuid in _machines: self.nova_client.servers.start(uuid) + logging.info( + "Wait till all {} units are in state 'unkown' ..." + .format(self.application)) for unit in zaza.model.get_units(self.application): zaza.model.block_until_unit_wl_status( unit.entity_id, 'unknown', negate_match=True) - logging.debug("Wait till model is idle ...") + logging.info("Wait till model is idle ...") try: zaza.model.block_until_all_units_idle() except zaza.model.UnitError: self.resolve_update_status_errors() zaza.model.block_until_all_units_idle() - logging.debug("Clear error hooks after reboot ...") + logging.info("Clear error hooks after reboot ...") for unit in zaza.model.get_units(self.application): try: zaza.model.run_on_unit(unit.entity_id, "hooks/update-status") except zaza.model.UnitError: self.resolve_update_status_errors() zaza.model.run_on_unit(unit.entity_id, "hooks/update-status") - logging.debug("Wait for application states blocked ...") - states = { - self.application: { - "workload-status": "blocked", - "workload-status-message": - "MySQL InnoDB Cluster not healthy: None"}, - "mysql-router": { - "workload-status": "blocked", - "workload-status-message": - "Failed to connect to MySQL"}} - zaza.model.wait_for_application_states(states=states) + logging.info( + "Wait till all {} units are in state 'blocked' ..." + .format(self.application)) + for unit in zaza.model.get_units(self.application): + zaza.model.block_until_unit_wl_status( + unit.entity_id, + 'blocked') logging.info("Execute reboot-cluster-from-complete-outage " "action after cold boot ...") @@ -592,15 +591,15 @@ class MySQLInnoDBClusterColdStartTest(MySQLBaseTest): unit.entity_id, "reboot-cluster-from-complete-outage", action_params={}) - if "Success" in action.data["results"].get("outcome"): + if "Success" in action.data.get("results", {}).get("outcome", ""): break else: - logging.info(action.data["results"].get("output")) + logging.info(action.data.get("results", {}).get("output", "")) assert "Success" in action.data["results"]["outcome"], ( "Reboot cluster from complete outage action failed: {}" .format(action.data)) - logging.debug("Wait for application states ...") + logging.info("Wait for application states ...") for unit in zaza.model.get_units(self.application): zaza.model.run_on_unit(unit.entity_id, "hooks/update-status") test_config = lifecycle_utils.get_charm_config(fatal=False) From 701e751398e4fe9077b5cc20f9f5d59875ad5e00 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Thu, 9 Jul 2020 10:15:56 +0000 Subject: [PATCH 25/77] Add check that guest is up On a recent mosci run the guest used for a restart test had a pending power task which stopped the restart test working properly. This PR adds an assertion that the guest is ready. --- zaza/openstack/charm_tests/masakari/tests.py | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/zaza/openstack/charm_tests/masakari/tests.py b/zaza/openstack/charm_tests/masakari/tests.py index d9490d2..6f27d17 100644 --- a/zaza/openstack/charm_tests/masakari/tests.py +++ b/zaza/openstack/charm_tests/masakari/tests.py @@ -134,6 +134,26 @@ class MasakariTest(test_utils.OpenStackBaseTest): vm_uuid, model_name=self.model_name) + @tenacity.retry(wait=tenacity.wait_exponential(multiplier=2, max=60), + reraise=True, stop=tenacity.stop_after_attempt(5), + retry=tenacity.retry_if_exception_type(AssertionError)) + def wait_for_guest_ready(self, vm_name): + """Wait for the guest to be ready. + + :param vm_name: Name of guest to check. + :type vm_name: str + """ + guest_ready_attr_checks = [ + ('OS-EXT-STS:task_state', None), + ('status', 'ACTIVE'), + ('OS-EXT-STS:power_state', 1), + ('OS-EXT-STS:vm_state', 'active')] + guest = self.nova_client.servers.find(name=vm_name) + logging.info('Checking guest {} attributes'.format(vm_name)) + for (attr, required_state) in guest_ready_attr_checks: + logging.info('Checking {} is {}'.format(attr, required_state)) + assert getattr(guest, attr) == required_state + def test_instance_failover(self): """Test masakari managed guest migration.""" # Workaround for Bug #1874719 @@ -168,6 +188,7 @@ class MasakariTest(test_utils.OpenStackBaseTest): model_name=self.model_name) openstack_utils.enable_all_nova_services(self.nova_client) zaza.openstack.configure.masakari.enable_hosts() + self.wait_for_guest_ready(vm_name) def test_instance_restart_on_fail(self): """Test single guest crash and recovery.""" @@ -178,6 +199,7 @@ class MasakariTest(test_utils.OpenStackBaseTest): self.current_release)) vm_name = 'zaza-test-instance-failover' vm = self.ensure_guest(vm_name) + self.wait_for_guest_ready(vm_name) _, unit_name = self.get_guests_compute_info(vm_name) logging.info('{} is running on {}'.format(vm_name, unit_name)) guest_pid = self.get_guest_qemu_pid( From 0287664d9243efbc26994739f095f910bf12a047 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Thu, 9 Jul 2020 16:35:00 +0200 Subject: [PATCH 26/77] Fix paramiko.ssh_exception.BadAuthenticationType (#355) Fix paramiko.ssh_exception.BadAuthenticationType when SSHing to a new Ubuntu instance. Note that paramiko still has a few issues around authentication: https://github.com/paramiko/paramiko/pull/1106/files This paramiko PR also shows that password='' isn't the same as password=None --- unit_tests/utilities/test_zaza_utilities_openstack.py | 2 +- zaza/openstack/utilities/openstack.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unit_tests/utilities/test_zaza_utilities_openstack.py b/unit_tests/utilities/test_zaza_utilities_openstack.py index 549ed71..a3ad423 100644 --- a/unit_tests/utilities/test_zaza_utilities_openstack.py +++ b/unit_tests/utilities/test_zaza_utilities_openstack.py @@ -773,7 +773,7 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): privkey='myprivkey') paramiko_mock.connect.assert_called_once_with( '10.0.0.10', - password='', + password=None, pkey='akey', username='bob') diff --git a/zaza/openstack/utilities/openstack.py b/zaza/openstack/utilities/openstack.py index 034cae3..bf7a041 100644 --- a/zaza/openstack/utilities/openstack.py +++ b/zaza/openstack/utilities/openstack.py @@ -2401,7 +2401,7 @@ def ssh_command(username, ssh.connect(ip, username=username, password=password) else: key = paramiko.RSAKey.from_private_key(io.StringIO(privkey)) - ssh.connect(ip, username=username, password='', pkey=key) + ssh.connect(ip, username=username, password=None, pkey=key) logging.info("Running {} on {}".format(command, vm_name)) stdin, stdout, stderr = ssh.exec_command(command) if verify and callable(verify): From 4546e821c1bf1bf868c2a6c852a97df937b7b660 Mon Sep 17 00:00:00 2001 From: David Ames Date: Thu, 9 Jul 2020 09:27:25 -0700 Subject: [PATCH 27/77] Wait for resolving update-status hook errors The cold boot restart takes too long on update-status. Make the timeout longer. --- zaza/openstack/charm_tests/mysql/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/mysql/tests.py b/zaza/openstack/charm_tests/mysql/tests.py index bf61932..c9a767d 100644 --- a/zaza/openstack/charm_tests/mysql/tests.py +++ b/zaza/openstack/charm_tests/mysql/tests.py @@ -521,7 +521,7 @@ class MySQLInnoDBClusterColdStartTest(MySQLBaseTest): zaza.model.resolve_units( application_name=self.application, erred_hook='update-status', - wait=True) + wait=True, timeout=180) def test_100_reboot_cluster_from_complete_outage(self): """Reboot cluster from complete outage. From f6e2a40166caefcdd9b8ce0bc9e2389791551a11 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Mon, 13 Jul 2020 14:33:35 +0000 Subject: [PATCH 28/77] Set application_name for ceph-radosgw tests Set application_name for ceph-radosgw tests so that the tests can be run without relying on getting the application name from the tests.yaml. --- zaza/openstack/charm_tests/ceph/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/ceph/tests.py b/zaza/openstack/charm_tests/ceph/tests.py index ff79820..c25df2a 100644 --- a/zaza/openstack/charm_tests/ceph/tests.py +++ b/zaza/openstack/charm_tests/ceph/tests.py @@ -544,7 +544,7 @@ class CephRGWTest(test_utils.OpenStackBaseTest): @classmethod def setUpClass(cls): """Run class setup for running ceph low level tests.""" - super(CephRGWTest, cls).setUpClass() + super(CephRGWTest, cls).setUpClass(application_name='ceph-radosgw') @property def expected_apps(self): From 65cc6aa604912291dc9772db1b59473414f83d49 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Mon, 13 Jul 2020 14:36:59 +0000 Subject: [PATCH 29/77] Stop assuming RegionOne in ceph-radosgw tests Stop assuming RegionOne in ceph-radosgw tests and get the region from the app config instead. --- zaza/openstack/charm_tests/ceph/tests.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/ceph/tests.py b/zaza/openstack/charm_tests/ceph/tests.py index ff79820..b4ae613 100644 --- a/zaza/openstack/charm_tests/ceph/tests.py +++ b/zaza/openstack/charm_tests/ceph/tests.py @@ -622,7 +622,9 @@ class CephRGWTest(test_utils.OpenStackBaseTest): 'multisite configuration') logging.info('Checking Swift REST API') keystone_session = zaza_openstack.get_overcloud_keystone_session() - region_name = 'RegionOne' + region_name = zaza_model.get_application_config( + self.application_name, + model_name=self.model_name)['region']['value'] swift_client = zaza_openstack.get_swift_session_client( keystone_session, region_name, From 9e395f69b76e6a70f053c2a35cf4741a7dfca7f0 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Mon, 13 Jul 2020 14:42:43 +0000 Subject: [PATCH 30/77] Add ceph_ready config step Add a ceph_ready config step which just checks that the ceph units are in a 'active' 'Unit is Ready.*' state. This is useful when encryption at rest is being used and the tests.yaml assumes that the units are going to come up blocked. Once vault is ready the tests then need to wait for the ceph units to become unblocked. --- zaza/openstack/charm_tests/ceph/setup.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/zaza/openstack/charm_tests/ceph/setup.py b/zaza/openstack/charm_tests/ceph/setup.py index c53ff3c..c93fec8 100644 --- a/zaza/openstack/charm_tests/ceph/setup.py +++ b/zaza/openstack/charm_tests/ceph/setup.py @@ -14,7 +14,23 @@ """Setup for ceph-osd deployments.""" +import logging +import zaza.model + def basic_setup(): """Run basic setup for ceph-osd.""" pass + + +def ceph_ready(): + """Wait for ceph to be ready. + + Wait for ceph to be ready. This is useful if the target_deploy_status in + the tests.yaml is expecting ceph to be in a blocked state. After ceph + has been unblocked the deploy may need to wait to be ceph to be ready. + """ + logging.info("Waiting for ceph units to settle") + zaza.model.wait_for_application_states() + zaza.model.block_until_all_units_idle() + logging.info("Ceph units settled") From c4176881d00b1191b290f882e245f9ed1b4f4e31 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Mon, 13 Jul 2020 14:56:04 +0000 Subject: [PATCH 31/77] Fix spelling typo --- zaza/openstack/charm_tests/ceph/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/ceph/setup.py b/zaza/openstack/charm_tests/ceph/setup.py index c93fec8..87f213d 100644 --- a/zaza/openstack/charm_tests/ceph/setup.py +++ b/zaza/openstack/charm_tests/ceph/setup.py @@ -28,7 +28,7 @@ def ceph_ready(): Wait for ceph to be ready. This is useful if the target_deploy_status in the tests.yaml is expecting ceph to be in a blocked state. After ceph - has been unblocked the deploy may need to wait to be ceph to be ready. + has been unblocked the deploy may need to wait for ceph to be ready. """ logging.info("Waiting for ceph units to settle") zaza.model.wait_for_application_states() From e409b68639f75bc5b333b6d4074ae92602bc4216 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 14 Jul 2020 12:39:58 +0000 Subject: [PATCH 32/77] Add Groovy Victoria support --- zaza/openstack/utilities/os_versions.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/zaza/openstack/utilities/os_versions.py b/zaza/openstack/utilities/os_versions.py index b2e430e..648a73b 100644 --- a/zaza/openstack/utilities/os_versions.py +++ b/zaza/openstack/utilities/os_versions.py @@ -35,6 +35,7 @@ UBUNTU_OPENSTACK_RELEASE = OrderedDict([ ('disco', 'stein'), ('eoan', 'train'), ('focal', 'ussuri'), + ('groovy', 'victoria'), ]) @@ -57,6 +58,7 @@ OPENSTACK_CODENAMES = OrderedDict([ ('2019.1', 'stein'), ('2019.2', 'train'), ('2020.1', 'ussuri'), + ('2020.2', 'victoria'), ]) OPENSTACK_RELEASES_PAIRS = [ @@ -66,7 +68,8 @@ OPENSTACK_RELEASES_PAIRS = [ 'xenial_pike', 'artful_pike', 'xenial_queens', 'bionic_queens', 'bionic_rocky', 'cosmic_rocky', 'bionic_stein', 'disco_stein', 'bionic_train', - 'eoan_train', 'bionic_ussuri', 'focal_ussuri'] + 'eoan_train', 'bionic_ussuri', 'focal_ussuri', + 'focal-victoria', 'groovy-victoria'] # The ugly duckling - must list releases oldest to newest SWIFT_CODENAMES = OrderedDict([ @@ -106,6 +109,8 @@ SWIFT_CODENAMES = OrderedDict([ ['2.22.0']), ('ussuri', ['2.24.0']), + ('victoria', + ['2.25.0']), ]) # >= Liberty version->codename mapping @@ -121,6 +126,7 @@ PACKAGE_CODENAMES = { ('19', 'stein'), ('20', 'train'), ('21', 'ussuri'), + ('22', 'victoria'), ]), 'neutron-common': OrderedDict([ ('7', 'liberty'), @@ -133,6 +139,7 @@ PACKAGE_CODENAMES = { ('14', 'stein'), ('15', 'train'), ('16', 'ussuri'), + ('17', 'victoria'), ]), 'cinder-common': OrderedDict([ ('7', 'liberty'), @@ -145,6 +152,7 @@ PACKAGE_CODENAMES = { ('14', 'stein'), ('15', 'train'), ('16', 'ussuri'), + ('17', 'victoria'), ]), 'keystone': OrderedDict([ ('8', 'liberty'), @@ -157,6 +165,7 @@ PACKAGE_CODENAMES = { ('15', 'stein'), ('16', 'train'), ('17', 'ussuri'), + ('18', 'victoria'), ]), 'horizon-common': OrderedDict([ ('8', 'liberty'), @@ -169,6 +178,7 @@ PACKAGE_CODENAMES = { ('15', 'stein'), ('16', 'train'), ('17', 'ussuri'), + ('19', 'victoria'), ]), 'ceilometer-common': OrderedDict([ ('5', 'liberty'), @@ -181,6 +191,7 @@ PACKAGE_CODENAMES = { ('12', 'stein'), ('13', 'train'), ('14', 'ussuri'), + ('15', 'victoria'), ]), 'heat-common': OrderedDict([ ('5', 'liberty'), @@ -193,6 +204,7 @@ PACKAGE_CODENAMES = { ('12', 'stein'), ('13', 'train'), ('14', 'ussuri'), + ('15', 'victoria'), ]), 'glance-common': OrderedDict([ ('11', 'liberty'), @@ -205,6 +217,7 @@ PACKAGE_CODENAMES = { ('18', 'stein'), ('19', 'train'), ('20', 'ussuri'), + ('21', 'victoria'), ]), 'openstack-dashboard': OrderedDict([ ('8', 'liberty'), @@ -216,7 +229,8 @@ PACKAGE_CODENAMES = { ('14', 'rocky'), ('15', 'stein'), ('16', 'train'), - ('17', 'ussuri'), + ('18', 'ussuri'), + ('19', 'victoria'), ]), 'designate-common': OrderedDict([ ('1', 'liberty'), @@ -229,6 +243,7 @@ PACKAGE_CODENAMES = { ('8', 'stein'), ('9', 'train'), ('10', 'ussuri'), + ('11', 'victoria'), ]), 'ovn-common': OrderedDict([ ('2', 'train'), From 2d3eaa6e8478679c7bc62c8eef6b18ab96d7ed30 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 14 Jul 2020 12:42:29 +0000 Subject: [PATCH 33/77] Fix typo --- zaza/openstack/utilities/os_versions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/utilities/os_versions.py b/zaza/openstack/utilities/os_versions.py index 648a73b..fcf15df 100644 --- a/zaza/openstack/utilities/os_versions.py +++ b/zaza/openstack/utilities/os_versions.py @@ -69,7 +69,7 @@ OPENSTACK_RELEASES_PAIRS = [ 'bionic_queens', 'bionic_rocky', 'cosmic_rocky', 'bionic_stein', 'disco_stein', 'bionic_train', 'eoan_train', 'bionic_ussuri', 'focal_ussuri', - 'focal-victoria', 'groovy-victoria'] + 'focal_victoria', 'groovy_victoria'] # The ugly duckling - must list releases oldest to newest SWIFT_CODENAMES = OrderedDict([ From e729c64956ce6103fd6dca790139474e39278723 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 14 Jul 2020 12:53:07 +0000 Subject: [PATCH 34/77] Fixed existing codename errors spotted by coreycb --- zaza/openstack/utilities/os_versions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zaza/openstack/utilities/os_versions.py b/zaza/openstack/utilities/os_versions.py index fcf15df..b46fc21 100644 --- a/zaza/openstack/utilities/os_versions.py +++ b/zaza/openstack/utilities/os_versions.py @@ -108,7 +108,7 @@ SWIFT_CODENAMES = OrderedDict([ ('train', ['2.22.0']), ('ussuri', - ['2.24.0']), + ['2.24.0', '2.25.0']), ('victoria', ['2.25.0']), ]) @@ -177,7 +177,7 @@ PACKAGE_CODENAMES = { ('14', 'rocky'), ('15', 'stein'), ('16', 'train'), - ('17', 'ussuri'), + ('18', 'ussuri'), ('19', 'victoria'), ]), 'ceilometer-common': OrderedDict([ From 9896c8d9c54be397deef20ea104043a99d6e2ca2 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 14 Jul 2020 14:07:59 +0000 Subject: [PATCH 35/77] Skip test_003_test_overide_is_observed Skip test_003_test_overide_is_observed until Bug #1880959 is fix released. --- zaza/openstack/charm_tests/policyd/tests.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 88d6699..67b9909 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -401,6 +401,10 @@ class BasePolicydSpecialization(PolicydTest, def test_003_test_overide_is_observed(self): """Test that the override is observed by the underlying service.""" + if (openstack_utils.get_os_release() < + openstack_utils.get_os_release('groovy_victoria')): + raise unittest.SkipTest( + "Test skipped until Bug #1880959 is fix released") if self._test_name is None: logging.info("Doing policyd override for {}" .format(self._service_name)) From a670e274fafe032525bb8fd342262b87a47ae797 Mon Sep 17 00:00:00 2001 From: Andrew McLeod Date: Wed, 15 Jul 2020 16:52:27 +0200 Subject: [PATCH 36/77] add image arch and properties specification --- zaza/openstack/charm_tests/glance/setup.py | 26 +++++++++++++++++----- zaza/openstack/utilities/openstack.py | 10 ++++++++- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/zaza/openstack/charm_tests/glance/setup.py b/zaza/openstack/charm_tests/glance/setup.py index 8c7bd3e..ab0a3b3 100644 --- a/zaza/openstack/charm_tests/glance/setup.py +++ b/zaza/openstack/charm_tests/glance/setup.py @@ -16,6 +16,7 @@ import logging import zaza.openstack.utilities.openstack as openstack_utils +import zaza.utilities.deployment_env as deployment_env CIRROS_IMAGE_NAME = "cirros" CIRROS_ALT_IMAGE_NAME = "cirros_alt" @@ -31,7 +32,8 @@ def basic_setup(): """ -def add_image(image_url, glance_client=None, image_name=None, tags=[]): +def add_image(image_url, glance_client=None, image_name=None, tags=[], + properties=None): """Retrieve image from ``image_url`` and add it to glance. :param image_url: Retrievable URL with image data @@ -42,6 +44,8 @@ def add_image(image_url, glance_client=None, image_name=None, tags=[]): :type image_name: str :param tags: List of tags to add to image :type tags: list of str + :param properties: Properties to add to image + :type properties: dict """ if not glance_client: keystone_session = openstack_utils.get_overcloud_keystone_session() @@ -60,7 +64,8 @@ def add_image(image_url, glance_client=None, image_name=None, tags=[]): glance_client, image_url, image_name, - tags=tags) + tags=tags, + properties=properties) def add_cirros_image(glance_client=None, image_name=None): @@ -90,7 +95,8 @@ def add_cirros_alt_image(glance_client=None, image_name=None): add_cirros_image(glance_client, image_name) -def add_lts_image(glance_client=None, image_name=None, release=None): +def add_lts_image(glance_client=None, image_name=None, release=None, + properties=None): """Add an Ubuntu LTS image to the current deployment. :param glance: Authenticated glanceclient @@ -99,12 +105,22 @@ def add_lts_image(glance_client=None, image_name=None, release=None): :type image_name: str :param release: Name of ubuntu release. :type release: str + :param properties: Custom image properties + :type properties: dict """ + deploy_ctxt = deployment_env.get_deployment_context() + image_arch = deploy_ctxt.get('TEST_IMAGE_ARCH', 'amd64') + arch_image_properties = { + 'arm64': {'hw_firmware_type': 'uefi'}, + 'ppc64el': {'architecture': 'ppc64'}} + properties = properties or arch_image_properties.get(image_arch) + logging.info("Image architecture set to {}".format(image_arch)) image_name = image_name or LTS_IMAGE_NAME release = release or LTS_RELEASE image_url = openstack_utils.find_ubuntu_image( release=release, - arch='amd64') + arch=image_arch) add_image(image_url, glance_client=glance_client, - image_name=image_name) + image_name=image_name, + properties=properties) diff --git a/zaza/openstack/utilities/openstack.py b/zaza/openstack/utilities/openstack.py index bf7a041..76c80d2 100644 --- a/zaza/openstack/utilities/openstack.py +++ b/zaza/openstack/utilities/openstack.py @@ -2036,7 +2036,8 @@ def upload_image_to_glance(glance, local_path, image_name, disk_format='qcow2', return image -def create_image(glance, image_url, image_name, image_cache_dir=None, tags=[]): +def create_image(glance, image_url, image_name, image_cache_dir=None, tags=[], + properties=None): """Download the image and upload it to glance. Download an image from image_url and upload it to glance labelling @@ -2053,6 +2054,8 @@ def create_image(glance, image_url, image_name, image_cache_dir=None, tags=[]): :type image_cache_dir: Option[str, None] :param tags: Tags to add to image :type tags: list of str + :param properties: Properties and values to add to image + :type properties: dict :returns: glance image pointer :rtype: glanceclient.common.utils.RequestIdProxy """ @@ -2074,6 +2077,11 @@ def create_image(glance, image_url, image_name, image_cache_dir=None, tags=[]): logging.debug( 'applying tag to image: glance.image_tags.update({}, {}) = {}' .format(image.id, tags, result)) + + logging.info("Setting image properties: {}".format(properties)) + if properties: + result = glance.images.update(image.id, **properties) + return image From ce6cfe68b31ed3266964b8f118fb95d48e5ac12d Mon Sep 17 00:00:00 2001 From: Liam Young Date: Wed, 15 Jul 2020 20:07:17 +0000 Subject: [PATCH 37/77] Add defaut app name for vrrp tests Set a default application_name for vrrp tests so they can be called by charms other than the neutron-api gate tests. --- 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 620767e..ec6998d 100644 --- a/zaza/openstack/charm_tests/neutron/tests.py +++ b/zaza/openstack/charm_tests/neutron/tests.py @@ -613,7 +613,8 @@ class NeutronNetworkingBase(test_utils.OpenStackBaseTest): @classmethod def setUpClass(cls): """Run class setup for running Neutron API Networking tests.""" - super(NeutronNetworkingBase, cls).setUpClass() + super(NeutronNetworkingBase, cls).setUpClass( + application_name='neutron-api') cls.neutron_client = ( openstack_utils.get_neutron_session_client(cls.keystone_session)) From 158e8ce37f9d9240a396136e91c0387d840a2891 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Wed, 22 Jul 2020 10:40:29 +0200 Subject: [PATCH 38/77] 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 39/77] 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 40/77] 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 41/77] 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 42/77] 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 43/77] 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 44/77] 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 45/77] 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 46/77] 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 47/77] 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 48/77] 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 49/77] 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 50/77] 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 51/77] 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 52/77] 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 53/77] 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 54/77] 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 55/77] 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 56/77] 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 57/77] 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 58/77] 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 59/77] 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 60/77] 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 61/77] 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 62/77] 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 63/77] 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 64/77] 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 65/77] 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 66/77] 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 67/77] 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 68/77] 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 69/77] 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 70/77] 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 71/77] 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 72/77] 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 73/77] 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 74/77] 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 75/77] 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 76/77] 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 77/77] 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