From c1a3c0e54e675380ec85f8092527981efa8ce078 Mon Sep 17 00:00:00 2001 From: Andre Date: Sat, 29 Jan 2022 14:46:53 +0000 Subject: [PATCH] Add functional tests for add multiple subnets feature This change adds negative and positive functional tests for the new feature add multiple subnets per az. Older functional tests impacted by the feature are fixed. Finally, the new flags `run_share_server_multiple_subnets_tests` and `run_network_allocation_update_tests` are added and configured in the community jobs for Dummy DHSS=True and Container Driver. Partially-Implements: blueprint multiple-share-network-subnets Change-Id: I99547e1873646fb1494a454f67b14c7293342beb --- manila_tempest_tests/common/waiters.py | 28 +++ manila_tempest_tests/config.py | 12 +- .../services/share/v2/json/shares_client.py | 23 ++ .../tests/api/admin/test_share_servers.py | 7 +- .../api/admin/test_share_servers_manage.py | 17 +- .../tests/api/test_replication.py | 139 +++++++++++++ .../tests/api/test_share_groups.py | 129 ++++++++++++ .../tests/api/test_share_network_subnets.py | 23 +- .../test_share_network_subnets_negative.py | 13 +- .../tests/api/test_share_networks_negative.py | 21 ++ .../api/test_share_servers_multiple_subnet.py | 196 ++++++++++++++++++ ..._share_servers_multiple_subnet_negative.py | 90 ++++++++ manila_tempest_tests/utils.py | 10 + zuul.d/manila-tempest-jobs.yaml | 4 + 14 files changed, 696 insertions(+), 16 deletions(-) create mode 100644 manila_tempest_tests/tests/api/test_share_servers_multiple_subnet.py create mode 100644 manila_tempest_tests/tests/api/test_share_servers_multiple_subnet_negative.py diff --git a/manila_tempest_tests/common/waiters.py b/manila_tempest_tests/common/waiters.py index 24b30de3..8a97c8ee 100644 --- a/manila_tempest_tests/common/waiters.py +++ b/manila_tempest_tests/common/waiters.py @@ -217,3 +217,31 @@ def wait_for_restore(client, share_id, version=LATEST_MICROVERSION): 'timeout': client.build_timeout, }) raise exceptions.TimeoutException(message) + + +def wait_for_subnet_create_check(client, share_network_id, + neutron_net_id=None, + neutron_subnet_id=None, + availability_zone=None): + result = client.subnet_create_check( + share_network_id, neutron_net_id=neutron_net_id, + neutron_subnet_id=neutron_subnet_id, + availability_zone=availability_zone) + start = int(time.time()) + while not result['compatible']: + time.sleep(client.build_interval) + result = client.subnet_create_check( + share_network_id, neutron_net_id=neutron_net_id, + neutron_subnet_id=neutron_subnet_id, + availability_zone=availability_zone) + if result['compatible']: + break + elif int(time.time()) - start >= client.build_timeout or ( + result['compatible'] is False): + message = ('Subnet create check failed within the ' + 'required time %(timeout)s seconds for share network ' + '%(share_network)s.' % { + 'timeout': client.build_timeout, + 'share_network': share_network_id, + }) + raise exceptions.TimeoutException(message) diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py index f5022af9..1758064a 100644 --- a/manila_tempest_tests/config.py +++ b/manila_tempest_tests/config.py @@ -40,7 +40,7 @@ ShareGroup = [ "This value is only used to validate the versions " "response from Manila."), cfg.StrOpt("max_api_microversion", - default="2.69", + default="2.70", help="The maximum api microversion is configured to be the " "value of the latest microversion supported by Manila."), cfg.StrOpt("region", @@ -287,6 +287,16 @@ ShareGroup = [ default=False, help="Defines whether to run share servers migration tests. " "Enable this option if the used driver supports it."), + cfg.BoolOpt("run_share_server_multiple_subnet_tests", + default=False, + help="Defines whether to run the share server multiple " + "subnets tests. Enable this option if the used driver " + "supports it."), + cfg.BoolOpt("run_network_allocation_update_tests", + default=False, + help="Defines whether to run the network allocation update " + "tests. Enable this option if the used driver " + "supports it."), cfg.StrOpt("image_with_share_tools", default="manila-service-image-master", diff --git a/manila_tempest_tests/services/share/v2/json/shares_client.py b/manila_tempest_tests/services/share/v2/json/shares_client.py index 3837049a..8c7da43c 100644 --- a/manila_tempest_tests/services/share/v2/json/shares_client.py +++ b/manila_tempest_tests/services/share/v2/json/shares_client.py @@ -1953,6 +1953,29 @@ class SharesV2Client(shares_client.SharesClient): self.expected_success(202, resp.status) return rest_client.ResponseBody(resp, body) + def subnet_create_check( + self, share_network_id, neutron_net_id=None, + neutron_subnet_id=None, availability_zone=None, + reset=False, version=LATEST_MICROVERSION): + body = { + 'share_network_subnet_create_check': { + 'neutron_net_id': neutron_net_id, + 'neutron_subnet_id': neutron_subnet_id, + 'availability_zone': availability_zone, + 'reset': reset, + } + } + + body = json.dumps(body) + resp, body = self.post( + f'share-networks/{share_network_id}/action', + body, headers=EXPERIMENTAL, extra_headers=True, + version=version) + self.expected_success(202, resp.status) + + body = json.loads(body) + return rest_client.ResponseBody(resp, body) + ############### def share_server_migration_check( diff --git a/manila_tempest_tests/tests/api/admin/test_share_servers.py b/manila_tempest_tests/tests/api/admin/test_share_servers.py index b406a926..01cc9fca 100644 --- a/manila_tempest_tests/tests/api/admin/test_share_servers.py +++ b/manila_tempest_tests/tests/api/admin/test_share_servers.py @@ -196,8 +196,13 @@ class ShareServersAdminTest(base.BaseSharesAdminTest): if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.49"): keys.append("is_auto_deletable") keys.append("identifier") - if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.51"): + if utils.is_microversion_ge( + CONF.share.max_api_microversion, "2.51") and ( + utils.is_microversion_lt( + CONF.share.max_api_microversion, "2.70")): keys.append("share_network_subnet_id") + if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.70"): + keys.append("share_network_subnet_ids") # all expected keys are present for key in keys: self.assertIn(key, server.keys()) diff --git a/manila_tempest_tests/tests/api/admin/test_share_servers_manage.py b/manila_tempest_tests/tests/api/admin/test_share_servers_manage.py index 126dee1e..8fdc22b1 100644 --- a/manila_tempest_tests/tests/api/admin/test_share_servers_manage.py +++ b/manila_tempest_tests/tests/api/admin/test_share_servers_manage.py @@ -66,6 +66,12 @@ class ManageShareServersTest(base.BaseSharesAdminTest): msg = ("Manage share server with share network subnet is " "supported starting from microversion '2.51'.") raise self.skipException(msg) + check_multiple_subnet = utils.is_microversion_ge( + CONF.share.max_api_microversion, '2.70') + if check_multiple_subnet: + network_subnet = 'share_network_subnet_ids' + else: + network_subnet = 'share_network_subnet_id' # create a new share network to make sure that a new share server # will be created original_share_network = self.shares_v2_client.get_share_network( @@ -91,7 +97,7 @@ class ManageShareServersTest(base.BaseSharesAdminTest): neutron_subnet_id=share_network['neutron_subnet_id'], availability_zone=az )['share_network_subnet'] - params = {'share_network_subnet_id': az_subnet['id']} + params = {network_subnet: az_subnet['id']} # create share share = self.create_share( @@ -119,7 +125,7 @@ class ManageShareServersTest(base.BaseSharesAdminTest): "identifier", ] if add_subnet_field: - keys.append('share_network_subnet_id') + keys.append(network_subnet) # all expected keys are present for key in keys: self.assertIn(key, share_server) @@ -127,9 +133,10 @@ class ManageShareServersTest(base.BaseSharesAdminTest): # check that the share server is initially auto-deletable self.assertIs(True, share_server["is_auto_deletable"]) self.assertIsNotNone(share_server["identifier"]) - if add_subnet_field: - self.assertEqual(az_subnet["id"], - share_server["share_network_subnet_id"]) + if add_subnet_field and check_multiple_subnet: + self.assertIn(az_subnet["id"], share_server[network_subnet]) + elif add_subnet_field and not check_multiple_subnet: + self.assertEqual(az_subnet["id"], share_server[network_subnet]) self._unmanage_share_and_wait(share) diff --git a/manila_tempest_tests/tests/api/test_replication.py b/manila_tempest_tests/tests/api/test_replication.py index 722f02fe..20ba8fe2 100644 --- a/manila_tempest_tests/tests/api/test_replication.py +++ b/manila_tempest_tests/tests/api/test_replication.py @@ -187,6 +187,145 @@ class ReplicationTest(base.BaseSharesMixedTest): # Delete subnet self.shares_v2_client.delete_subnet(self.sn_id, subnet['id']) + @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND) + @testtools.skipIf( + not CONF.share.multitenancy_enabled, "Only for multitenancy.") + @testtools.skipIf( + not CONF.share.run_share_server_multiple_subnet_tests, + "Share server multiple subnet tests are disabled.") + @testtools.skipIf(CONF.share.share_network_id != "", + "This test is not suitable for pre-existing " + "share networks.") + @utils.skip_if_microversion_not_supported("2.70") + @decorators.idempotent_id('4235e789-dbd6-47a8-8f2e-d70edf78e532') + def test_add_delete_share_replica_multiple_subnets(self): + extra_specs = { + "replication_type": self.replication_type, + "driver_handles_share_servers": CONF.share.multitenancy_enabled, + "share_server_multiple_subnet_support": True, + } + share_type = self.create_share_type( + extra_specs=extra_specs, client=self.admin_client) + default_subnet = utils.share_network_get_default_subnet( + self.share_network) + new_share_network_id = self.create_share_network( + cleanup_in_class=False)['id'] + subnet_data = { + 'neutron_net_id': default_subnet.get('neutron_net_id'), + 'neutron_subnet_id': default_subnet.get('neutron_subnet_id'), + 'share_network_id': new_share_network_id, + 'availability_zone': self.replica_zone, + } + subnet1 = self.create_share_network_subnet(**subnet_data) + subnet2 = self.create_share_network_subnet(**subnet_data) + # Creating a third subnet in share replica az + subnet_data.update({'availability_zone': self.share_zone}) + subnet3 = self.create_share_network_subnet(**subnet_data) + # Create the share and share replica + share = self.create_share( + share_type_id=share_type['id'], cleanup_in_class=False, + availability_zone=self.share_zone, + share_network_id=new_share_network_id) + share = self.admin_client.get_share(share['id'])['share'] + replica = self.create_share_replica(share['id'], self.replica_zone) + replica = self.admin_client.get_share_replica( + replica['id'])['share_replica'] + share_server = self.admin_client.show_share_server( + replica['share_server_id'])['share_server'] + self.assertIn(subnet1['id'], + share_server['share_network_subnet_ids']) + self.assertIn(subnet2['id'], + share_server['share_network_subnet_ids']) + # Delete the replica + self.delete_share_replica(replica['id']) + # Delete share + self.shares_v2_client.delete_share(share['id']) + self.shares_v2_client.wait_for_resource_deletion(share_id=share['id']) + # Delete subnets + self.shares_v2_client.delete_subnet( + new_share_network_id, subnet1['id']) + self.shares_v2_client.delete_subnet( + new_share_network_id, subnet2['id']) + self.shares_v2_client.delete_subnet( + new_share_network_id, subnet3['id']) + + @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND) + @testtools.skipIf( + not CONF.share.multitenancy_enabled, "Only for multitenancy.") + @testtools.skipIf( + not CONF.share.run_network_allocation_update_tests, + "Share server network allocation update tests are disabled.") + @testtools.skipIf(CONF.share.share_network_id != "", + "This test is not suitable for pre-existing " + "share_network.") + @utils.skip_if_microversion_not_supported("2.70") + @decorators.idempotent_id('26694947-d4a0-46c8-99e8-2e0eca1b6a08') + def test_add_delete_share_replica_network_allocation_update(self): + extra_specs = { + "replication_type": self.replication_type, + "driver_handles_share_servers": CONF.share.multitenancy_enabled, + "network_allocation_update_support": True, + } + share_type = self.create_share_type(extra_specs=extra_specs) + + default_subnet = utils.share_network_get_default_subnet( + self.share_network) + new_share_network_id = self.create_share_network( + cleanup_in_class=False)['id'] + subnet_data = { + 'neutron_net_id': default_subnet.get('neutron_net_id'), + 'neutron_subnet_id': default_subnet.get('neutron_subnet_id'), + 'share_network_id': new_share_network_id, + 'availability_zone': self.share_zone, + } + subnet1 = self.create_share_network_subnet(**subnet_data) + subnet_data.update({'availability_zone': self.replica_zone}) + subnet2 = self.create_share_network_subnet(**subnet_data) + # Create the share and share replica + share = self.create_share( + share_type_id=share_type['id'], cleanup_in_class=False, + availability_zone=self.share_zone, + share_network_id=new_share_network_id) + share = self.admin_client.get_share(share['id'])['share'] + + replica = self.create_share_replica(share['id'], self.replica_zone) + replica = self.admin_client.get_share_replica( + replica['id'])['share_replica'] + + # Waits until the check is completed and positive + waiters.wait_for_subnet_create_check( + self.shares_v2_client, new_share_network_id, + neutron_net_id=subnet_data['neutron_net_id'], + neutron_subnet_id=subnet_data['neutron_subnet_id'], + availability_zone=self.replica_zone) + # Creating a third subnet in replica zone to trigger the network + # allocation update + subnet3 = self.create_share_network_subnet(**subnet_data) + waiters.wait_for_resource_status( + self.admin_client, replica['share_server_id'], + constants.SERVER_STATE_ACTIVE, + resource_name="share_server", + status_attr="status") + share_server = self.admin_client.show_share_server( + replica['share_server_id'] + )['share_server'] + self.assertIn(subnet2['id'], + share_server['share_network_subnet_ids']) + self.assertIn(subnet3['id'], + share_server['share_network_subnet_ids']) + # Delete the replica + self.delete_share_replica(replica['id']) + # Delete share + self.shares_v2_client.delete_share(share['id']) + self.shares_v2_client.wait_for_resource_deletion(share_id=share['id']) + # Delete subnets + self.shares_v2_client.delete_subnet( + new_share_network_id, subnet1['id']) + self.shares_v2_client.delete_subnet( + new_share_network_id, subnet2['id']) + self.shares_v2_client.delete_subnet( + new_share_network_id, subnet3['id']) + @decorators.idempotent_id('00e12b41-b95d-494a-99be-e584aae10f5c') @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND) def test_add_access_rule_create_replica_delete_rule(self): diff --git a/manila_tempest_tests/tests/api/test_share_groups.py b/manila_tempest_tests/tests/api/test_share_groups.py index 3b56161e..b2034817 100644 --- a/manila_tempest_tests/tests/api/test_share_groups.py +++ b/manila_tempest_tests/tests/api/test_share_groups.py @@ -15,12 +15,14 @@ import ddt from tempest import config +from tempest.lib.common.utils import data_utils from tempest.lib import decorators from tempest.lib import exceptions as lib_exc import testtools from testtools import testcase as tc from manila_tempest_tests.common import constants +from manila_tempest_tests.common import waiters from manila_tempest_tests.tests.api import base from manila_tempest_tests import utils @@ -263,3 +265,130 @@ class ShareGroupsTest(base.BaseSharesMixedTest): # Verify that share always has the same AZ as share group does self.assertEqual( share_group['availability_zone'], share['availability_zone']) + + @utils.skip_if_microversion_not_supported("2.70") + @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND) + @testtools.skipUnless(CONF.share.multitenancy_enabled, + "Multitenancy is disabled.") + @testtools.skipUnless(CONF.share.run_share_server_multiple_subnet_tests, + "Share server multiple subnet tests are disabled.") + @testtools.skipIf(CONF.share.share_network_id != "", + "This test is not suitable for pre-existing " + "share networks.") + @ddt.data(False, True) + @decorators.idempotent_id('17fd1867-03a3-43d0-9be3-daf90b6c5e02') + def test_create_sg_and_share_with_multiple_subnets( + self, network_allocation_update): + if network_allocation_update and not ( + CONF.share.run_network_allocation_update_tests): + raise self.skipException( + 'Network allocation update tests are disabled.') + extra_specs = { + 'driver_handles_share_servers': CONF.share.multitenancy_enabled, + 'share_server_multiple_subnet_support': True, + } + if network_allocation_update: + extra_specs['network_allocation_update_support'] = True + share_type = self.create_share_type(extra_specs=extra_specs) + sg_type_name = data_utils.rand_name("tempest-manila") + sg_type = self.create_share_group_type( + name=sg_type_name, share_types=share_type['id'], + client=self.admin_shares_v2_client) + # Get list of existing availability zones, at least one always + # should exist + azs = self.get_availability_zones_matching_share_type(share_type) + if len(azs) == 0: + raise self.skipException( + "No AZs were found. Make sure there is at least one " + "configured.") + share_network = self.shares_v2_client.get_share_network( + self.shares_v2_client.share_network_id)['share_network'] + new_share_network_id = self.create_share_network( + cleanup_in_class=False)['id'] + + default_subnet = utils.share_network_get_default_subnet( + share_network) + subnet_data = { + 'neutron_net_id': default_subnet.get('neutron_net_id'), + 'neutron_subnet_id': default_subnet.get('neutron_subnet_id'), + 'share_network_id': new_share_network_id, + 'availability_zone': azs[0] + } + subnet1 = self.create_share_network_subnet(**subnet_data) + if not network_allocation_update: + subnet2 = self.create_share_network_subnet(**subnet_data) + + sg_kwargs = { + 'share_group_type_id': sg_type['id'], + 'share_type_ids': [share_type['id']], + 'share_network_id': new_share_network_id, + 'availability_zone': azs[0], + 'version': constants.MIN_SHARE_GROUP_MICROVERSION, + 'cleanup_in_class': False, + } + + # Create share group + share_group = self.create_share_group(**sg_kwargs) + + # Get latest share group info + share_group = self.shares_v2_client.get_share_group( + share_group['id'])['share_group'] + + self.assertIn('availability_zone', share_group) + self.assertEqual(azs[0], share_group['availability_zone']) + + # Test 'consistent_snapshot_support' as part of 2.33 API change + self.assertIn('consistent_snapshot_support', share_group) + self.assertIn( + share_group['consistent_snapshot_support'], ('host', 'pool', None)) + + share_data = { + 'share_type_id': share_type['id'], + 'share_group_id': share_group['id'], + 'share_network_id': new_share_network_id, + 'availability_zone': azs[0], + 'cleanup_in_class': False, + } + + # Create share in share group + share = self.create_share(**share_data) + + # Get latest share info + share = self.admin_shares_v2_client.get_share(share['id'])['share'] + # Verify that share always has the same AZ as share group does + self.assertEqual( + share_group['availability_zone'], share['availability_zone']) + + # Get share server info + share_server = self.admin_shares_v2_client.show_share_server( + share['share_server_id'])['share_server'] + if network_allocation_update: + waiters.wait_for_subnet_create_check( + self.shares_v2_client, new_share_network_id, + neutron_net_id=subnet_data['neutron_net_id'], + neutron_subnet_id=subnet_data['neutron_subnet_id'], + availability_zone=azs[0]) + + subnet2 = self.create_share_network_subnet(**subnet_data) + waiters.wait_for_resource_status( + self.admin_shares_v2_client, share['share_server_id'], + constants.SERVER_STATE_ACTIVE, + resource_name="share_server", + status_attr="status") + share_server = self.admin_shares_v2_client.show_share_server( + share['share_server_id'])['share_server'] + # Check if share server has multiple subnets + self.assertIn(subnet1['id'], share_server['share_network_subnet_ids']) + self.assertIn(subnet2['id'], share_server['share_network_subnet_ids']) + # Delete share + params = {"share_group_id": share_group['id']} + self.shares_v2_client.delete_share( + share['id'], + params=params, + version=constants.MIN_SHARE_GROUP_MICROVERSION) + self.shares_client.wait_for_resource_deletion(share_id=share['id']) + # Delete subnet + self.shares_v2_client.delete_subnet( + new_share_network_id, subnet1['id']) + self.shares_v2_client.delete_subnet( + new_share_network_id, subnet2['id']) diff --git a/manila_tempest_tests/tests/api/test_share_network_subnets.py b/manila_tempest_tests/tests/api/test_share_network_subnets.py index 0f651c94..cdf2162c 100644 --- a/manila_tempest_tests/tests/api/test_share_network_subnets.py +++ b/manila_tempest_tests/tests/api/test_share_network_subnets.py @@ -127,6 +127,8 @@ class ShareNetworkSubnetsTest(base.BaseSharesMixedTest): msg = ("This test needs at least two compatible storage " "availability zones.") raise self.skipException(msg) + check_multiple_subnet = utils.is_microversion_ge( + CONF.share.max_api_microversion, '2.70') original_share_network = self.shares_v2_client.get_share_network( self.shares_v2_client.share_network_id @@ -173,8 +175,12 @@ class ShareNetworkSubnetsTest(base.BaseSharesMixedTest): # Match new subnet content self.assertDictContainsSubset(data, subnet) # Match share server subnet - self.assertEqual(subnet['id'], - share_server['share_network_subnet_id']) + if check_multiple_subnet: + self.assertIn(subnet['id'], + share_server['share_network_subnet_ids']) + else: + self.assertIn(subnet['id'], + share_server['share_network_subnet_id']) # Delete share self.shares_v2_client.delete_share(share['id']) self.shares_v2_client.wait_for_resource_deletion(share_id=share['id']) @@ -197,10 +203,11 @@ class ShareNetworkSubnetsTest(base.BaseSharesMixedTest): msg = ("This test needs at least two compatible storage " "availability zones.") raise self.skipException(msg) + check_multiple_subnet = utils.is_microversion_ge( + CONF.share.max_api_microversion, '2.70') original_share_network = self.shares_v2_client.get_share_network( - self.shares_v2_client.share_network_id - )['share_network'] + self.shares_v2_client.share_network_id)['share_network'] share_net_info = ( utils.share_network_get_default_subnet(original_share_network)) share_network = self.create_share_network( @@ -254,8 +261,12 @@ class ShareNetworkSubnetsTest(base.BaseSharesMixedTest): # Default subnet was created during share network creation self.assertIsNone(default_subnet['availability_zone']) # Match share server subnet - self.assertEqual(expected_subnet_id, - share_server['share_network_subnet_id']) + if not check_multiple_subnet: + self.assertEqual( + expected_subnet_id, share_server['share_network_subnet_id']) + else: + self.assertIn( + expected_subnet_id, share_server['share_network_subnet_ids']) if create_share_with_az: self.assertEqual(destination_az, share['availability_zone']) diff --git a/manila_tempest_tests/tests/api/test_share_network_subnets_negative.py b/manila_tempest_tests/tests/api/test_share_network_subnets_negative.py index 4af34f2a..7b0ca556 100644 --- a/manila_tempest_tests/tests/api/test_share_network_subnets_negative.py +++ b/manila_tempest_tests/tests/api/test_share_network_subnets_negative.py @@ -27,6 +27,7 @@ from manila_tempest_tests import utils CONF = config.CONF +LATEST_MICROVERSION = CONF.share.max_api_microversion @ddt.ddt @@ -72,6 +73,7 @@ class ShareNetworkSubnetsNegativeTest(base.BaseSharesAdminTest): @decorators.idempotent_id('13f397bf-5e3a-42b0-b4f9-9cd2dbbb0955') @tc.attr(base.TAG_NEGATIVE, base.TAG_API) @ddt.data(True, False) + @utils.skip_if_is_microversion_ge(LATEST_MICROVERSION, "2.70") def test_add_share_network_subnet_in_same_az_exists(self, is_default): share_network = self.shares_v2_client.create_share_network( )['share_network'] @@ -231,7 +233,8 @@ class ShareNetworkSubnetsNegativeTest(base.BaseSharesAdminTest): # Get a compatible availability zone az = self.get_availability_zones_matching_share_type( self.share_type)[0] - + check_multiple_subnets = utils.is_microversion_ge( + CONF.share.max_api_microversion, '2.70') original_share_network = self.shares_v2_client.get_share_network( self.shares_v2_client.share_network_id )['share_network'] @@ -269,8 +272,12 @@ class ShareNetworkSubnetsNegativeTest(base.BaseSharesAdminTest): share['share_server_id'] )['share_server'] # Match share server subnet - self.assertEqual(subnet['id'], - share_server['share_network_subnet_id']) + if check_multiple_subnets: + self.assertIn(subnet['id'], + share_server['share_network_subnet_ids']) + else: + self.assertEqual(subnet['id'], + share_server['share_network_subnet_id']) # Assert that the user cannot delete a subnet that contain shares self.assertRaises(lib_exc.Conflict, diff --git a/manila_tempest_tests/tests/api/test_share_networks_negative.py b/manila_tempest_tests/tests/api/test_share_networks_negative.py index 17e0c12d..33060685 100644 --- a/manila_tempest_tests/tests/api/test_share_networks_negative.py +++ b/manila_tempest_tests/tests/api/test_share_networks_negative.py @@ -195,3 +195,24 @@ class ShareNetworksNegativeTest(base.BaseSharesMixedTest): self.shares_v2_client.create_share_network, availability_zone='inexistent-availability-zone', ) + + @utils.skip_if_microversion_not_supported("2.70") + @tc.attr(base.TAG_NEGATIVE, base.TAG_API) + @decorators.idempotent_id('f6f47c64-6821-4d4a-aa7d-3b0244158197') + def test_check_add_share_network_subnet_share_network_not_found(self): + data = self.generate_subnet_data() + self.assertRaises(lib_exc.NotFound, + self.shares_v2_client.subnet_create_check, + 'fake_inexistent_id', + **data) + + @utils.skip_if_microversion_not_supported("2.70") + @tc.attr(base.TAG_NEGATIVE, base.TAG_API) + @decorators.idempotent_id('d9a487fb-6638-4f93-8b69-3e1a85bfbc7d') + def test_check_add_share_network_subnet_az_not_found(self): + share_network = self.create_share_network() + data = {'availability_zone': 'non-existent-az'} + + self.assertRaises(lib_exc.BadRequest, + self.shares_v2_client.subnet_create_check, + share_network['id'], **data) diff --git a/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet.py b/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet.py new file mode 100644 index 00000000..fccbf03b --- /dev/null +++ b/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet.py @@ -0,0 +1,196 @@ +# Copyright 2022 NetApp Inc. +# All Rights Reserved. +# +# 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. + +from tempest import config +from tempest.lib import decorators +import testtools +from testtools import testcase as tc + +from manila_tempest_tests.common import constants +from manila_tempest_tests.common import waiters +from manila_tempest_tests.tests.api import base +from manila_tempest_tests import utils + +CONF = config.CONF + + +class ShareServerMultipleSubnetTest(base.BaseSharesMixedTest): + + @classmethod + def skip_checks(cls): + super(ShareServerMultipleSubnetTest, cls).skip_checks() + if not CONF.share.multitenancy_enabled: + raise cls.skipException('Multitenancy tests are disabled.') + if not CONF.share.run_share_server_multiple_subnet_tests and not ( + CONF.share.run_network_allocation_update_tests): + raise cls.skipException( + 'Share server multiple subnets and network allocation ' + 'update tests are disabled.') + if CONF.share.share_network_id != "": + raise cls.skipException( + 'These tests are not suitable for pre-existing ' + 'share_network.') + utils.check_skip_if_microversion_not_supported("2.70") + + @classmethod + def resource_setup(cls): + super(ShareServerMultipleSubnetTest, cls).resource_setup() + cls.extra_specs = { + 'driver_handles_share_servers': CONF.share.multitenancy_enabled, + } + if CONF.share.run_share_server_multiple_subnet_tests: + cls.extra_specs['share_server_multiple_subnet_support'] = True + if CONF.share.run_network_allocation_update_tests: + cls.extra_specs['network_allocation_update_support'] = True + share_type = cls.create_share_type(extra_specs=cls.extra_specs) + cls.share_type_id = share_type['id'] + + cls.zones = cls.get_availability_zones_matching_share_type( + share_type) + if len(cls.zones) == 0: + msg = ("These tests need at least one compatible " + "availability zone.") + raise cls.skipException(msg) + + cls.share_network = cls.alt_shares_v2_client.get_share_network( + cls.alt_shares_v2_client.share_network_id)['share_network'] + cls.default_subnet = utils.share_network_get_default_subnet( + cls.share_network) + + @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND) + @testtools.skipIf( + not CONF.share.run_share_server_multiple_subnet_tests, + "Share network multiple subnets tests are disabled.") + @decorators.idempotent_id('5600bd52-ecb4-47d3-a4e8-3e6565cb0b80') + def test_create_share_on_multiple_subnets_same_az(self): + share_network_id = self.create_share_network( + cleanup_in_class=False)["id"] + subnet_data = { + 'neutron_net_id': self.default_subnet.get('neutron_net_id'), + 'neutron_subnet_id': self.default_subnet.get('neutron_subnet_id'), + 'share_network_id': share_network_id, + 'availability_zone': self.zones[0], + } + subnet1 = self.create_share_network_subnet(**subnet_data) + subnet2 = self.create_share_network_subnet(**subnet_data) + + share = self.create_share( + share_type_id=self.share_type_id, + share_network_id=share_network_id, + availability_zone=self.zones[0]) + self.assertIn(share['status'], ('creating', 'available')) + + share = self.admin_shares_v2_client.get_share(share['id'])['share'] + share_server = self.admin_shares_v2_client.show_share_server( + share['share_server_id'] + )['share_server'] + self.assertIn(subnet1['id'], + share_server['share_network_subnet_ids']) + self.assertIn(subnet2['id'], + share_server['share_network_subnet_ids']) + + # Delete share + self.shares_v2_client.delete_share(share['id']) + self.shares_v2_client.wait_for_resource_deletion(share_id=share['id']) + # Delete the subnets + self.shares_v2_client.delete_subnet(share_network_id, subnet1['id']) + self.shares_v2_client.delete_subnet(share_network_id, subnet2['id']) + + @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND) + @testtools.skipIf( + not CONF.share.run_network_allocation_update_tests, + "Share server network allocation update are disabled.") + @decorators.idempotent_id('2a9debd5-47a3-42cc-823b-2b9de435a5e4') + def test_create_share_with_network_allocation_update(self): + share_network_id = self.create_share_network( + cleanup_in_class=False)["id"] + subnet_data = { + 'neutron_net_id': self.default_subnet.get('neutron_net_id'), + 'neutron_subnet_id': self.default_subnet.get('neutron_subnet_id'), + 'share_network_id': share_network_id, + 'availability_zone': self.zones[0], + } + subnet1 = self.create_share_network_subnet(**subnet_data) + + share = self.create_share( + share_type_id=self.share_type_id, + share_network_id=share_network_id, + availability_zone=self.zones[0]) + self.assertIn(share['status'], ('creating', 'available')) + share = self.admin_shares_v2_client.get_share(share['id'])['share'] + + waiters.wait_for_subnet_create_check( + self.shares_v2_client, share_network_id, + neutron_net_id=subnet_data['neutron_net_id'], + neutron_subnet_id=subnet_data['neutron_subnet_id'], + availability_zone=self.zones[0]) + subnet2 = self.create_share_network_subnet(**subnet_data) + + waiters.wait_for_resource_status( + self.admin_shares_v2_client, share['share_server_id'], + constants.SERVER_STATE_ACTIVE, + resource_name="share_server", + status_attr="status") + share_server = self.admin_shares_v2_client.show_share_server( + share['share_server_id'] + )['share_server'] + + self.assertIn(subnet1['id'], + share_server['share_network_subnet_ids']) + self.assertIn(subnet2['id'], + share_server['share_network_subnet_ids']) + + # Delete share + self.shares_v2_client.delete_share(share['id']) + self.shares_v2_client.wait_for_resource_deletion(share_id=share['id']) + # Delete subnets + self.shares_v2_client.delete_subnet(share_network_id, subnet1['id']) + self.shares_v2_client.delete_subnet(share_network_id, subnet2['id']) + + @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND) + @testtools.skipIf( + not CONF.share.run_network_allocation_update_tests, + "Share server network allocation update are disabled.") + @decorators.idempotent_id('2624f9a7-660b-4f91-89b8-c026b3bb8d1f') + def test_share_network_subnet_create_check(self): + """The share network subnet create check compatibility test.""" + + share_network_id = self.create_share_network( + cleanup_in_class=False)["id"] + subnet_data = { + 'neutron_net_id': self.default_subnet.get('neutron_net_id'), + 'neutron_subnet_id': self.default_subnet.get('neutron_subnet_id'), + 'share_network_id': share_network_id, + 'availability_zone': self.zones[0], + } + subnet1 = self.create_share_network_subnet(**subnet_data) + + share = self.create_share( + share_type_id=self.share_type_id, + share_network_id=share_network_id, + availability_zone=self.zones[0] + ) + self.assertIn(share['status'], ('creating', 'available')) + waiters.wait_for_subnet_create_check( + self.shares_v2_client, share_network_id, + neutron_net_id=subnet_data['neutron_net_id'], + neutron_subnet_id=subnet_data['neutron_subnet_id'], + availability_zone=self.zones[0]) + + # Delete share + self.shares_v2_client.delete_share(share['id']) + self.shares_v2_client.wait_for_resource_deletion(share_id=share['id']) + # Delete subnets + self.shares_v2_client.delete_subnet(share_network_id, subnet1['id']) diff --git a/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet_negative.py b/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet_negative.py new file mode 100644 index 00000000..522f34e0 --- /dev/null +++ b/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet_negative.py @@ -0,0 +1,90 @@ +# Copyright 2022 NetApp Inc. +# All Rights Reserved. +# +# 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. + +from tempest import config +from tempest.lib.common.utils import test_utils +from tempest.lib import decorators +from testtools import testcase as tc + +from manila_tempest_tests import share_exceptions +from manila_tempest_tests.tests.api import base +from manila_tempest_tests import utils + +CONF = config.CONF + + +class ShareServerMultipleSubNegativeTest(base.BaseSharesMixedTest): + + @classmethod + def skip_checks(cls): + super(ShareServerMultipleSubNegativeTest, cls).skip_checks() + if not CONF.share.multitenancy_enabled: + raise cls.skipException('Multitenancy tests are disabled.') + utils.check_skip_if_microversion_not_supported("2.70") + + @classmethod + def resource_setup(cls): + super(ShareServerMultipleSubNegativeTest, cls).resource_setup() + cls.share_network = cls.alt_shares_v2_client.get_share_network( + cls.alt_shares_v2_client.share_network_id)['share_network'] + + @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND) + @decorators.idempotent_id('1e2a9415-b02f-4c02-812d-bedc361f92ce') + def test_create_share_multiple_subnets_to_unsupported_backend(self): + extra_specs = { + 'driver_handles_share_servers': CONF.share.multitenancy_enabled, + 'share_server_multiple_subnet_support': False + } + share_type = self.create_share_type(extra_specs=extra_specs) + pools = self.get_pools_matching_share_type( + share_type, client=self.admin_shares_v2_client) + zones = self.get_availability_zones_matching_share_type( + share_type) + if not pools or not zones: + raise self.skipException("At least one backend that supports " + "adding multiple subnets into a share " + "network is needed for this test.") + extra_specs = {'pool_name': pools[0]['pool'], + 'availability_zone': zones[0]} + self.admin_shares_v2_client.update_share_type_extra_specs( + share_type['id'], extra_specs) + + share_network_id = self.create_share_network( + cleanup_in_class=True)["id"] + default_subnet = utils.share_network_get_default_subnet( + self.share_network) + subnet_data = { + 'neutron_net_id': default_subnet.get('neutron_net_id'), + 'neutron_subnet_id': default_subnet.get('neutron_subnet_id'), + 'share_network_id': share_network_id, + 'availability_zone': zones[0], + 'cleanup_in_class': False + } + subnet1 = self.create_share_network_subnet(**subnet_data) + self.addCleanup(test_utils.call_and_ignore_notfound_exc, + self.shares_v2_client.delete_subnet, + share_network_id, subnet1['id']) + subnet2 = self.create_share_network_subnet(**subnet_data) + self.addCleanup(test_utils.call_and_ignore_notfound_exc, + self.shares_v2_client.delete_subnet, + share_network_id, subnet2['id']) + self.assertRaises( + share_exceptions.ShareBuildErrorException, + self.create_share, + share_type_id=share_type['id'], + share_network_id=share_network_id, + availability_zone=zones[0], + cleanup_in_class=False + ) diff --git a/manila_tempest_tests/utils.py b/manila_tempest_tests/utils.py index d0860f0f..5ecfb367 100644 --- a/manila_tempest_tests/utils.py +++ b/manila_tempest_tests/utils.py @@ -94,6 +94,16 @@ def skip_if_microversion_not_supported(microversion): return lambda f: f +def skip_if_is_microversion_ge(left, right): + """Skip if version for left is greater than or equal to the right one.""" + + if is_microversion_ge(left, right): + reason = ("Skipped. Test requires microversion " + "< than '%s'." % right) + return testtools.skip(reason) + return lambda f: f + + def check_skip_if_microversion_not_supported(microversion): """Callable method for tests that are microversion-specific.""" if not is_microversion_supported(microversion): diff --git a/zuul.d/manila-tempest-jobs.yaml b/zuul.d/manila-tempest-jobs.yaml index 494b285d..a68aafe3 100644 --- a/zuul.d/manila-tempest-jobs.yaml +++ b/zuul.d/manila-tempest-jobs.yaml @@ -246,6 +246,8 @@ backend_names: LONDON,PARIS multi_backend: true run_share_server_migration_tests: true + run_share_server_multiple_subnet_tests: true + run_network_allocation_update_tests: true - job: name: manila-tempest-plugin-generic @@ -490,6 +492,8 @@ run_replication_tests: true run_revert_to_snapshot_tests: true run_share_server_migration_tests: true + run_share_server_multiple_subnet_tests: true + run_network_allocation_update_tests: true - job: name: manila-tempest-plugin-glusterfs-native