# Copyright 2016 Andrew Kerr # 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. 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 CONF = config.CONF @ddt.ddt class ShareGroupsTest(base.BaseSharesMixedTest): """Covers share group functionality.""" @classmethod def skip_checks(cls): super(ShareGroupsTest, cls).skip_checks() if not CONF.share.run_share_group_tests: raise cls.skipException('Share Group tests disabled.') utils.check_skip_if_microversion_not_supported( constants.MIN_SHARE_GROUP_MICROVERSION) @classmethod def resource_setup(cls): super(ShareGroupsTest, cls).resource_setup() # create share type extra_specs = {} if CONF.share.capability_snapshot_support: extra_specs.update({'snapshot_support': True}) if CONF.share.capability_create_share_from_snapshot_support: extra_specs.update({'create_share_from_snapshot_support': True}) cls.share_type = cls.create_share_type(extra_specs=extra_specs) cls.share_type_id = cls.share_type['id'] # create share group type cls.share_group_type = cls._create_share_group_type() cls.share_group_type_id = cls.share_group_type['id'] @decorators.idempotent_id('809d5e3d-5a4b-458a-a985-853d59800da5') @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND) def test_create_populate_delete_share_group_min(self): # Create a share group share_group = self.create_share_group( cleanup_in_class=False, version=constants.MIN_SHARE_GROUP_MICROVERSION, share_group_type_id=self.share_group_type_id, share_type_ids=[self.share_type_id], ) keys = set(share_group.keys()) self.assertTrue( constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS.issubset(keys), 'At least one expected element missing from share group ' 'response. Expected %(expected)s, got %(actual)s.' % { "expected": constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS, "actual": keys} ) # Populate share = self.create_share( share_type_id=self.share_type_id, share_group_id=share_group['id'], cleanup_in_class=False, version=constants.MIN_SHARE_GROUP_MICROVERSION) # Delete 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']) self.shares_v2_client.delete_share_group( share_group['id'], version=constants.MIN_SHARE_GROUP_MICROVERSION) self.shares_v2_client.wait_for_resource_deletion( share_group_id=share_group['id']) # Verify self.assertRaises( lib_exc.NotFound, self.shares_v2_client.get_share_group, share_group['id']) self.assertRaises( lib_exc.NotFound, self.shares_client.get_share, share['id']) @decorators.idempotent_id('cf7984af-1e1d-4eaf-bf9a-d8ddf5cebd01') @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND) @testtools.skipUnless(CONF.share.run_snapshot_tests, "Snapshot tests are disabled.") def test_create_delete_empty_share_group_snapshot_min(self): # Create base share group share_group = self.create_share_group( share_group_type_id=self.share_group_type_id, share_type_ids=[self.share_type_id], cleanup_in_class=False, version=constants.MIN_SHARE_GROUP_MICROVERSION) # Create share group snapshot sg_snapshot = self.create_share_group_snapshot_wait_for_active( share_group["id"], cleanup_in_class=False, version=constants.MIN_SHARE_GROUP_MICROVERSION) keys = set(sg_snapshot.keys()) self.assertTrue( constants.SHARE_GROUP_SNAPSHOT_DETAIL_REQUIRED_KEYS.issubset(keys), 'At least one expected element missing from share group snapshot ' 'response. Expected %(e)s, got %(a)s.' % { "e": constants.SHARE_GROUP_SNAPSHOT_DETAIL_REQUIRED_KEYS, "a": keys}) sg_snapshot_members = sg_snapshot['members'] self.assertEmpty( sg_snapshot_members, 'Expected 0 share_group_snapshot members, got %s' % len( sg_snapshot_members)) # Delete snapshot self.shares_v2_client.delete_share_group_snapshot( sg_snapshot["id"], version=constants.MIN_SHARE_GROUP_MICROVERSION) self.shares_v2_client.wait_for_resource_deletion( share_group_snapshot_id=sg_snapshot["id"]) self.assertRaises( lib_exc.NotFound, self.shares_v2_client.get_share_group_snapshot, sg_snapshot['id'], version=constants.MIN_SHARE_GROUP_MICROVERSION) @decorators.idempotent_id('727d9c69-4c3b-4375-a91b-8b3efd349976') @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND) @testtools.skipUnless(CONF.share.run_snapshot_tests, "Snapshot tests are disabled.") def test_create_share_group_from_empty_share_group_snapshot_min(self): # Create base share group share_group = self.create_share_group( share_group_type_id=self.share_group_type_id, share_type_ids=[self.share_type_id], cleanup_in_class=False, version=constants.MIN_SHARE_GROUP_MICROVERSION) # Create share group snapshot sg_snapshot = self.create_share_group_snapshot_wait_for_active( share_group["id"], cleanup_in_class=False, version=constants.MIN_SHARE_GROUP_MICROVERSION) snapshot_members = sg_snapshot['members'] self.assertEmpty( snapshot_members, 'Expected 0 share group snapshot members, got %s' % len(snapshot_members)) new_share_group = self.create_share_group( share_group_type_id=self.share_group_type_id, cleanup_in_class=False, source_share_group_snapshot_id=sg_snapshot['id'], version=constants.MIN_SHARE_GROUP_MICROVERSION) new_shares = self.shares_v2_client.list_shares( params={'share_group_id': new_share_group['id']}, version=constants.MIN_SHARE_GROUP_MICROVERSION)['shares'] self.assertEmpty( new_shares, 'Expected 0 new shares, got %s' % len(new_shares)) msg = ('Expected source_ishare_group_snapshot_id %s ' 'as source of share group %s' % ( sg_snapshot['id'], new_share_group['source_share_group_snapshot_id'])) self.assertEqual( new_share_group['source_share_group_snapshot_id'], sg_snapshot['id'], msg) msg = ('Unexpected share_types on new share group. Expected ' '%s, got %s.' % (share_group['share_types'], new_share_group['share_types'])) self.assertEqual( sorted(share_group['share_types']), sorted(new_share_group['share_types']), msg) # Assert the share_network information is the same msg = 'Expected share_network %s as share_network of cg %s' % ( share_group['share_network_id'], new_share_group['share_network_id']) self.assertEqual( share_group['share_network_id'], new_share_group['share_network_id'], msg) # Delete the share group snapshot and wait self.shares_v2_client.delete_share_group_snapshot( sg_snapshot["id"], version=constants.MIN_SHARE_GROUP_MICROVERSION) self.shares_v2_client.wait_for_resource_deletion( share_group_snapshot_id=sg_snapshot["id"]) # Delete share group, so share network subnet deletion does not fail self.shares_v2_client.delete_share_group( share_group['id'], version=constants.MIN_SHARE_GROUP_MICROVERSION) @utils.skip_if_microversion_not_supported("2.34") @decorators.idempotent_id('14fd6d88-87ff-4af2-ad17-f95dbd8dcd61') @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND) @ddt.data( 'sg', 'sg_and_share', 'none', ) def test_create_sg_and_share_specifying_az(self, where_specify_az): # Get list of existing availability zones, at least one always # should exist azs = self.get_availability_zones_matching_share_type( self.share_type) sg_kwargs = { 'share_group_type_id': self.share_group_type_id, 'share_type_ids': [self.share_type_id], 'version': '2.34', 'cleanup_in_class': False, } if where_specify_az in ('sg', 'sg_and_share'): sg_kwargs['availability_zone'] = azs[0] # 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'], '2.34')['share_group'] self.assertIn('availability_zone', share_group) if where_specify_az in ('sg', 'sg_and_share'): self.assertEqual(azs[0], share_group['availability_zone']) else: self.assertIn(share_group['availability_zone'], azs) # 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)) s_kwargs = { 'share_type_id': self.share_type_id, 'share_group_id': share_group['id'], 'version': '2.33', 'cleanup_in_class': False, } if where_specify_az == 'sg_and_share': s_kwargs['availability_zone'] = azs[0] # Create share in share group share = self.create_share(**s_kwargs) # Get latest share info share = self.shares_v2_client.get_share(share['id'], '2.34')['share'] # 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 share group self.shares_v2_client.delete_share_group( share_group['id'], version=constants.MIN_SHARE_GROUP_MICROVERSION) self.shares_v2_client.wait_for_resource_deletion( share_group_id=share_group['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'])