예제 #1
0
class ShareIpRulesForCIFSTest(ShareIpRulesForNFSTest):
    protocol = "cifs"

    @decorators.idempotent_id('8fa0a15f-c04c-4521-91e7-020943bede8a')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @testtools.skipIf("cifs"
                      not in CONF.share.enable_ro_access_level_for_protocols,
                      "RO access rule tests are disabled for CIFS protocol.")
    @ddt.data(*utils.deduplicate(
        ['1.0', '2.9', '2.27', '2.28', LATEST_MICROVERSION]))
    def test_create_delete_ro_access_rule(self, version):
        _create_delete_ro_access_rule(self, version)
예제 #2
0
class ShareUserRulesForCIFSTest(ShareUserRulesForNFSTest):
    protocol = "cifs"

    @decorators.idempotent_id('ee11084d-6c1d-4856-8044-9aa9e6c670fb')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @testtools.skipIf("cifs"
                      not in CONF.share.enable_ro_access_level_for_protocols,
                      "RO access rule tests are disabled for CIFS protocol.")
    @ddt.data(*utils.deduplicate(
        ['1.0', '2.9', '2.27', '2.28', LATEST_MICROVERSION]))
    def test_create_delete_ro_access_rule(self, version):
        _create_delete_ro_access_rule(self, version)
예제 #3
0
class ShareCephxRulesForCephFSTest(base.BaseSharesMixedTest):
    protocol = "cephfs"

    @classmethod
    def skip_checks(cls):
        super(ShareCephxRulesForCephFSTest, cls).skip_checks()
        if (cls.protocol not in CONF.share.enable_protocols or cls.protocol
                not in CONF.share.enable_cephx_rules_for_protocols):
            msg = ("Cephx rule tests for %s protocol are disabled." %
                   cls.protocol)
            raise cls.skipException(msg)

    @classmethod
    def resource_setup(cls):
        super(ShareCephxRulesForCephFSTest, cls).resource_setup()
        # create share type
        cls.share_type = cls.create_share_type()
        cls.share_type_id = cls.share_type['id']

        # create share
        cls.share = cls.create_share(cls.protocol,
                                     share_type_id=cls.share_type_id)

        cls.access_type = "cephx"
        # Provide access to a client identified by a cephx auth id.
        cls.access_to = "bob"

    @decorators.idempotent_id('4e636fd2-26ef-4b63-96eb-77860a8b6cdf')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @ddt.data(*itertools.product(
        utils.deduplicate(['2.13', '2.27', '2.28', LATEST_MICROVERSION]),
        ("alice", "alice_bob", "alice bob"), ('rw', 'ro')))
    @ddt.unpack
    def test_create_delete_cephx_rule(self, version, access_to, access_level):
        rule = self.allow_access(self.share["id"],
                                 access_type=self.access_type,
                                 access_to=access_to,
                                 version=version,
                                 access_level=access_level)

        self.assertEqual(access_level, rule['access_level'])
        for key in ('deleted', 'deleted_at', 'instance_mappings'):
            self.assertNotIn(key, rule.keys())

    @decorators.idempotent_id('ad907303-a439-4fcb-8845-fe91ecab7dc2')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    def test_different_users_in_same_tenant_can_use_same_cephx_id(self):
        # Grant access to the share
        self.allow_access(self.share['id'],
                          access_type=self.access_type,
                          access_to=self.access_to,
                          access_level='rw')

        # Create a new user in the current project
        project = self.os_admin.projects_client.show_project(
            self.shares_v2_client.tenant_id)['project']
        user_client = self.create_user_and_get_client(project)

        # Create second share by the new user
        share2 = self.create_share(client=user_client.shares_v2_client,
                                   share_protocol=self.protocol,
                                   share_type_id=self.share_type_id)

        # Grant access to the second share using the same cephx ID that was
        # used in access1
        self.allow_access(share2['id'],
                          client=user_client.shares_v2_client,
                          access_type=self.access_type,
                          access_to=self.access_to,
                          access_level='rw')
예제 #4
0
class ShareCertRulesForGLUSTERFSTest(base.BaseSharesMixedTest):
    protocol = "glusterfs"

    @classmethod
    def skip_checks(cls):
        super(ShareCertRulesForGLUSTERFSTest, cls).skip_checks()
        if (cls.protocol not in CONF.share.enable_protocols or cls.protocol
                not in CONF.share.enable_cert_rules_for_protocols):
            msg = "Cert rule tests for %s protocol are disabled" % cls.protocol
            raise cls.skipException(msg)

    @classmethod
    def resource_setup(cls):
        super(ShareCertRulesForGLUSTERFSTest, cls).resource_setup()
        # create share type
        cls.share_type = cls.create_share_type()
        cls.share_type_id = cls.share_type['id']

        # create share
        cls.share = cls.create_share(cls.protocol,
                                     share_type_id=cls.share_type_id)

        cls.access_type = "cert"
        # Provide access to a client identified by a common name (CN) of the
        # certificate that it possesses.
        cls.access_to = "client1.com"

    @decorators.idempotent_id('775ebc55-4a4d-4012-a030-2eeb7b6d2ce8')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @ddt.data(*utils.deduplicate(
        ['1.0', '2.9', '2.27', '2.28', LATEST_MICROVERSION]))
    def test_create_delete_cert_rule(self, version):
        if utils.is_microversion_le(version, '2.9'):
            client = self.shares_client
        else:
            client = self.shares_v2_client

        # create rule
        rule = self.allow_access(self.share["id"],
                                 client=client,
                                 access_type=self.access_type,
                                 access_to=self.access_to,
                                 version=version)

        self.assertEqual('rw', rule['access_level'])
        for key in ('deleted', 'deleted_at', 'instance_mappings'):
            self.assertNotIn(key, rule.keys())

        # rules must start out in 'new' until 2.28 & 'queued_to_apply' after
        if utils.is_microversion_le(version, "2.27"):
            self.assertEqual("new", rule['state'])
        else:
            self.assertEqual("queued_to_apply", rule['state'])

    @decorators.idempotent_id('cdd93d8e-7255-4ed4-8ef0-929a62bb302c')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @testtools.skipIf(
        "glusterfs" not in CONF.share.enable_ro_access_level_for_protocols,
        "RO access rule tests are disabled for GLUSTERFS protocol.")
    @ddt.data(*utils.deduplicate(
        ['1.0', '2.9', '2.27', '2.28', LATEST_MICROVERSION]))
    def test_create_delete_cert_ro_access_rule(self, version):
        if utils.is_microversion_le(version, '2.9'):
            client = self.shares_client
        else:
            client = self.shares_v2_client
        rule = self.allow_access(self.share["id"],
                                 client=client,
                                 access_type='cert',
                                 access_to='client2.com',
                                 access_level='ro',
                                 version=version)

        self.assertEqual('ro', rule['access_level'])
        for key in ('deleted', 'deleted_at', 'instance_mappings'):
            self.assertNotIn(key, rule.keys())

        # rules must start out in 'new' until 2.28 & 'queued_to_apply' after
        if utils.is_microversion_le(version, "2.27"):
            self.assertEqual("new", rule['state'])
        else:
            self.assertEqual("queued_to_apply", rule['state'])
예제 #5
0
class ShareUserRulesForNFSTest(base.BaseSharesMixedTest):
    protocol = "nfs"

    @classmethod
    def skip_checks(cls):
        super(ShareUserRulesForNFSTest, cls).skip_checks()
        if (cls.protocol not in CONF.share.enable_protocols or cls.protocol
                not in CONF.share.enable_user_rules_for_protocols):
            msg = "USER rule tests for %s protocol are disabled" % cls.protocol
            raise cls.skipException(msg)

    @classmethod
    def resource_setup(cls):
        super(ShareUserRulesForNFSTest, cls).resource_setup()

        # create share type
        cls.share_type = cls.create_share_type()
        cls.share_type_id = cls.share_type['id']

        # create share
        cls.share = cls.create_share(cls.protocol,
                                     share_type_id=cls.share_type_id)

        cls.access_type = "user"
        cls.access_to = CONF.share.username_for_user_rules

    @decorators.idempotent_id('1f87565f-c3d9-448d-b89a-387d6c2fdae6')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @ddt.data(*utils.deduplicate(
        ['1.0', '2.9', '2.27', '2.28', LATEST_MICROVERSION]))
    def test_create_delete_user_rule(self, version):
        if utils.is_microversion_le(version, '2.9'):
            client = self.shares_client
        else:
            client = self.shares_v2_client

        # create rule
        rule = self.allow_access(self.share["id"],
                                 client=client,
                                 access_type=self.access_type,
                                 access_to=self.access_to,
                                 version=version)

        self.assertEqual('rw', rule['access_level'])
        for key in ('deleted', 'deleted_at', 'instance_mappings'):
            self.assertNotIn(key, rule.keys())

        # rules must start out in 'new' until 2.28 & 'queued_to_apply' after
        if utils.is_microversion_le(version, "2.27"):
            self.assertEqual("new", rule['state'])
        else:
            self.assertEqual("queued_to_apply", rule['state'])

    @decorators.idempotent_id('ccb08342-b7ef-4dda-84ba-8de9879d8862')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @testtools.skipIf("nfs"
                      not in CONF.share.enable_ro_access_level_for_protocols,
                      "RO access rule tests are disabled for NFS protocol.")
    @ddt.data(*utils.deduplicate(
        ['1.0', '2.9', '2.27', '2.28', LATEST_MICROVERSION]))
    def test_create_delete_ro_access_rule(self, version):
        _create_delete_ro_access_rule(self, version)
class SharesQuotasTest(base.BaseSharesTest):
    @classmethod
    def skip_checks(cls):
        super(SharesQuotasTest, cls).skip_checks()
        if not CONF.share.run_quota_tests:
            msg = "Quota tests are disabled."
            raise cls.skipException(msg)

    @classmethod
    def resource_setup(cls):
        super(SharesQuotasTest, cls).resource_setup()
        cls.user_id = cls.shares_v2_client.user_id
        cls.tenant_id = cls.shares_v2_client.tenant_id

    @decorators.idempotent_id('a4649f11-41d0-4e73-ab5a-1466f2339b2f')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    @ddt.data('shares_client', 'shares_v2_client')
    def test_default_quotas(self, client_name):
        quotas = getattr(self, client_name).default_quotas(
            self.tenant_id)['quota_set']
        uses_v2_client = client_name == 'shares_v2_client'
        self.assertGreater(int(quotas["gigabytes"]), -2)
        self.assertGreater(int(quotas["snapshot_gigabytes"]), -2)
        self.assertGreater(int(quotas["shares"]), -2)
        self.assertGreater(int(quotas["snapshots"]), -2)
        self.assertGreater(int(quotas["share_networks"]), -2)
        if utils.share_replica_quotas_are_supported() and uses_v2_client:
            self.assertGreater(int(quotas["share_replicas"]), -2)
            self.assertGreater(int(quotas["replica_gigabytes"]), -2)

    @decorators.idempotent_id('19525293-2b32-48c3-a7cd-cab279ac4631')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    @ddt.data('shares_client', 'shares_v2_client')
    def test_show_quotas(self, client_name):
        quotas = getattr(self,
                         client_name).show_quotas(self.tenant_id)['quota_set']
        uses_v2_client = client_name == 'shares_v2_client'
        self.assertGreater(int(quotas["gigabytes"]), -2)
        self.assertGreater(int(quotas["snapshot_gigabytes"]), -2)
        self.assertGreater(int(quotas["shares"]), -2)
        self.assertGreater(int(quotas["snapshots"]), -2)
        self.assertGreater(int(quotas["share_networks"]), -2)
        if utils.share_replica_quotas_are_supported() and uses_v2_client:
            self.assertGreater(int(quotas["share_replicas"]), -2)
            self.assertGreater(int(quotas["replica_gigabytes"]), -2)

    @decorators.idempotent_id('1e670af7-9734-42a8-97b7-3080526f3aca')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    @ddt.data('shares_client', 'shares_v2_client')
    def test_show_quotas_for_user(self, client_name):
        quotas = getattr(self,
                         client_name).show_quotas(self.tenant_id,
                                                  self.user_id)['quota_set']
        uses_v2_client = client_name == 'shares_v2_client'
        self.assertGreater(int(quotas["gigabytes"]), -2)
        self.assertGreater(int(quotas["snapshot_gigabytes"]), -2)
        self.assertGreater(int(quotas["shares"]), -2)
        self.assertGreater(int(quotas["snapshots"]), -2)
        self.assertGreater(int(quotas["share_networks"]), -2)
        if utils.share_replica_quotas_are_supported() and uses_v2_client:
            self.assertGreater(int(quotas["share_replicas"]), -2)
            self.assertGreater(int(quotas["replica_gigabytes"]), -2)

    @ddt.data(*itertools.product(
        utils.deduplicate(["2.25", "2.53", CONF.share.max_api_microversion]),
        (True, False)))
    @ddt.unpack
    @decorators.idempotent_id('795614f6-4a18-47d5-b817-0b294e9d4c48')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    def test_show_quotas_detail(self, microversion, with_user):
        utils.check_skip_if_microversion_not_supported(microversion)
        quota_args = {
            "tenant_id": self.tenant_id,
            "version": microversion,
        }
        keys = [
            'gigabytes', 'snapshot_gigabytes', 'shares', 'snapshots',
            'share_networks'
        ]
        if utils.is_microversion_ge(microversion, SHARE_REPLICAS_MICROVERSION):
            keys.append('share_replicas')
            keys.append('replica_gigabytes')
        if with_user:
            quota_args.update({"user_id": self.user_id})
        quotas = self.shares_v2_client.detail_quotas(**quota_args)['quota_set']
        quota_keys = list(quotas.keys())
        for outer in keys:
            self.assertIn(outer, quota_keys)
            outer_keys = list(quotas[outer].keys())
            for inner in ('in_use', 'limit', 'reserved'):
                self.assertIn(inner, outer_keys)
                self.assertGreater(int(quotas[outer][inner]), -2)

    @decorators.idempotent_id('7bd5ac42-9fcb-477f-a253-02cde2bde661')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    @utils.skip_if_microversion_not_supported(PRE_SHARE_REPLICAS_MICROVERSION)
    def test_quota_detail_2_52_no_share_replica_quotas(self):
        quota_args = {
            "tenant_id": self.tenant_id,
            "version": PRE_SHARE_REPLICAS_MICROVERSION
        }
        quotas = self.shares_v2_client.detail_quotas(**quota_args)
        self.assertNotIn('share_replicas', quotas.keys())
        self.assertNotIn('replica_gigabytes', quotas.keys())
class SecurityServicesTest(base.BaseSharesMixedTest, SecurityServiceListMixin):
    @classmethod
    def resource_setup(cls):
        super(SecurityServicesTest, cls).resource_setup()
        # create share type
        cls.share_type = cls.create_share_type()
        cls.share_type_id = cls.share_type['id']

    def setUp(self):
        super(SecurityServicesTest, self).setUp()
        ss_ldap_data = {
            'name': 'ss_ldap',
            'dns_ip': '1.1.1.1',
            'server': 'fake_server_1',
            'domain': 'fake_domain_1',
            'user': '******',
            'password': '******',
        }
        if utils.is_microversion_ge(CONF.share.max_api_microversion, '2.44'):
            ss_ldap_data['ou'] = 'OU=fake_unit_1'
        ss_kerberos_data = {
            'name': 'ss_kerberos',
            'dns_ip': '2.2.2.2',
            'server': 'fake_server_2',
            'domain': 'fake_domain_2',
            'user': '******',
            'password': '******',
        }
        if utils.is_microversion_ge(CONF.share.max_api_microversion, '2.44'):
            ss_kerberos_data['ou'] = 'OU=fake_unit_2'
        self.ss_ldap = self.create_security_service('ldap', **ss_ldap_data)
        self.ss_kerberos = self.create_security_service(
            'kerberos', **ss_kerberos_data)

    @decorators.idempotent_id('70927e29-4a6a-431a-bbc1-76bc419e0579')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    def test_create_delete_security_service(self):
        data = self.generate_security_service_data()
        self.service_names = ["ldap", "kerberos", "active_directory"]
        for ss_name in self.service_names:
            ss = self.create_security_service(ss_name, **data)
            self.assertDictContainsSubset(data, ss)
            self.assertEqual(ss_name, ss["type"])
            self.shares_client.delete_security_service(ss["id"])

    @decorators.idempotent_id('bb052be4-0176-4613-b7d5-e12bef391ddb')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    @ddt.data(*utils.deduplicate(['1.0', '2.43', '2.44', LATEST_MICROVERSION]))
    def test_get_security_service(self, version):
        utils.check_skip_if_microversion_not_supported(version)
        with_ou = True if utils.is_microversion_ge(version, '2.44') else False
        data = self.generate_security_service_data(set_ou=with_ou)

        if utils.is_microversion_ge(version, '2.0'):
            ss = self.create_security_service(client=self.shares_v2_client,
                                              version=version,
                                              **data)
            get = self.shares_v2_client.get_security_service(
                ss["id"], version=version)['security_service']
        else:
            ss = self.create_security_service(**data)
            get = self.shares_client.get_security_service(
                ss["id"])['security_service']

        self.assertDictContainsSubset(data, ss)
        self.assertEqual(with_ou, 'ou' in ss)
        self.assertDictContainsSubset(data, get)
        self.assertEqual(with_ou, 'ou' in get)

    @decorators.idempotent_id('84d47747-13c8-4ab9-9fc4-a43fbb29ad18')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    def test_update_security_service(self):
        data = self.generate_security_service_data()
        ss = self.create_security_service(**data)
        self.assertDictContainsSubset(data, ss)

        upd_data = self.generate_security_service_data()
        updated = self.shares_client.update_security_service(
            ss["id"], **upd_data)['security_service']

        get = self.shares_client.get_security_service(
            ss["id"])['security_service']
        self.assertDictContainsSubset(upd_data, updated)
        self.assertDictContainsSubset(upd_data, get)

        if utils.is_microversion_ge(CONF.share.max_api_microversion, '2.44'):
            # update again with ou
            upd_data_ou = self.generate_security_service_data(set_ou=True)
            updated_ou = self.shares_v2_client.update_security_service(
                ss["id"], **upd_data_ou)['security_service']

            get_ou = self.shares_v2_client.get_security_service(
                ss["id"])['security_service']
            self.assertDictContainsSubset(upd_data_ou, updated_ou)
            self.assertDictContainsSubset(upd_data_ou, get_ou)

    @decorators.idempotent_id('c3c04992-da11-4677-9098-eff3f4231a4b')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @testtools.skipIf(not CONF.share.multitenancy_enabled,
                      "Only for multitenancy.")
    def test_try_update_valid_keys_sh_server_exists(self):
        ss_data = self.generate_security_service_data()
        ss = self.create_security_service(**ss_data)

        sn = self.shares_client.get_share_network(
            self.shares_client.share_network_id)['share_network']
        fresh_sn = self.create_share_network(
            add_security_services=False,
            neutron_net_id=sn["neutron_net_id"],
            neutron_subnet_id=sn["neutron_subnet_id"])

        self.shares_client.add_sec_service_to_share_network(
            fresh_sn["id"], ss["id"])

        # Security service with fake data is used, so if we use backend driver
        # that fails on wrong data, we expect error here.
        # We require any share that uses our share-network.
        try:
            self.create_share(share_type_id=self.share_type_id,
                              share_network_id=fresh_sn["id"],
                              cleanup_in_class=False)
        except Exception as e:
            # we do wait for either 'error' or 'available' status because
            # it is the only available statuses for proper deletion.
            LOG.warning(
                "Caught exception. It is expected in case backend "
                "fails having security-service with improper data "
                "that leads to share-server creation error. "
                "%s", str(e))

        update_data = {
            "name": "name",
            "description": "new_description",
        }
        updated = self.shares_client.update_security_service(
            ss["id"], **update_data)['security_service']
        self.assertDictContainsSubset(update_data, updated)

    @decorators.idempotent_id('8d9af272-df89-470d-9ff8-92ba774c9fff')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    def test_list_security_services_filter_by_invalid_opt(self):
        listed = self.shares_client.list_security_services(
            params={'fake_opt': 'some_value'})['security_services']
        self.assertTrue(any(self.ss_ldap['id'] == ss['id'] for ss in listed))
        self.assertTrue(
            any(self.ss_kerberos['id'] == ss['id'] for ss in listed))

    @decorators.idempotent_id('d501710e-4710-4c13-a373-75ed6ababb13')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    def test_try_list_security_services_all_tenants_ignored(self):
        alt_security_service = self.create_security_service(
            **self.generate_security_service_data(),
            client=self.alt_shares_v2_client)
        alt_security_service_id = alt_security_service['id']
        sec_service_list = self.shares_client.list_security_services(
            params={'all_tenants': 1})['security_services']
        sec_service_ids = [ss['id'] for ss in sec_service_list]
        self.assertTrue(
            any(self.ss_ldap['id'] == ss['id'] for ss in sec_service_list))
        self.assertTrue(
            any(self.ss_kerberos['id'] == ss['id'] for ss in sec_service_list))
        self.assertNotIn(alt_security_service_id, sec_service_ids)
class ReplicationExportLocationsTest(base.BaseSharesMixedTest):

    @classmethod
    def skip_checks(cls):
        super(ReplicationExportLocationsTest, cls).skip_checks()
        if not CONF.share.run_replication_tests:
            raise cls.skipException('Replication tests are disabled.')

    @classmethod
    def resource_setup(cls):
        super(ReplicationExportLocationsTest, cls).resource_setup()
        # Create share_type
        name = data_utils.rand_name(constants.TEMPEST_MANILA_PREFIX)
        cls.admin_client = cls.admin_shares_v2_client
        cls.replication_type = CONF.share.backend_replication_type
        cls.multitenancy_enabled = (
            utils.replication_with_multitenancy_support())

        if cls.replication_type not in constants.REPLICATION_TYPE_CHOICES:
            raise share_exceptions.ShareReplicationTypeException(
                replication_type=cls.replication_type
            )
        cls.extra_specs = cls.add_extra_specs_to_dict(
            {"replication_type": cls.replication_type})
        cls.share_type = cls.create_share_type(
            name,
            extra_specs=cls.extra_specs,
            client=cls.admin_client)
        cls.sn_id = None
        if cls.multitenancy_enabled:
            cls.share_network = cls.shares_v2_client.get_share_network(
                cls.shares_v2_client.share_network_id)['share_network']
            cls.sn_id = cls.share_network['id']
        cls.zones = cls.get_availability_zones_matching_share_type(
            cls.share_type)
        cls.share_zone = cls.zones[0]
        cls.replica_zone = cls.zones[-1]

    @staticmethod
    def _remove_admin_only_exports(all_exports):
        return [e for e in all_exports if not e['is_admin_only']]

    def _create_share_and_replica_get_exports(self, cleanup_replica=True):
        share = self.create_share(share_type_id=self.share_type['id'],
                                  availability_zone=self.share_zone,
                                  share_network_id=self.sn_id)
        replica = self.create_share_replica(share['id'], self.replica_zone,
                                            cleanup=cleanup_replica)
        replicas = self.shares_v2_client.list_share_replicas(
            share_id=share['id'])['share_replicas']
        primary_replica = [r for r in replicas if r['id'] != replica['id']][0]

        # Refresh share and replica
        share = self.shares_v2_client.get_share(share['id'])['share']
        replica = self.shares_v2_client.get_share_replica(
            replica['id'])['share_replica']

        # Grab export locations of the share instances using admin API
        replica_exports = self._remove_admin_only_exports(
            self.admin_client.list_share_instance_export_locations(
                replica['id'])['export_locations'])
        primary_replica_exports = self._remove_admin_only_exports(
            self.admin_client.list_share_instance_export_locations(
                primary_replica['id'])['export_locations'])

        return share, replica, primary_replica_exports, replica_exports

    def _validate_export_location_api_behavior(self, replica, replica_exports,
                                               primary_replica_exports,
                                               share_exports, version):
        share_export_paths = [e['path'] for e in share_exports]

        # Expectations
        expected_number_of_exports = len(primary_replica_exports
                                         + replica_exports)
        expected_exports = replica_exports + primary_replica_exports
        # In and beyond version 2.47, secondary "non-active" replica exports
        # are not expected to be present in the share export locations.
        # Secondary replicas can be "active" only in in "writable"
        # replication. In other types of replication, secondary replicas are
        # either "in_sync" or "out_of_sync"
        replica_is_non_active = (replica['replica_state'] !=
                                 constants.REPLICATION_STATE_ACTIVE)
        if utils.is_microversion_ge(version, '2.47') and replica_is_non_active:
            expected_number_of_exports = len(primary_replica_exports)
            expected_exports = primary_replica_exports

        # Assertions
        self.assertEqual(expected_number_of_exports, len(share_exports))
        for export in expected_exports:
            self.assertIn(export['path'], share_export_paths)

    @decorators.idempotent_id('da22cfb8-7dd8-4bf1-87fc-a1f7b51ebf8e')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @ddt.data(*utils.deduplicate(['2.46', '2.47', LATEST_MICROVERSION]))
    def test_replicated_share_export_locations(self, version):
        """Test behavior changes in the share export locations API at 2.47"""
        utils.check_skip_if_microversion_not_supported(version)
        share, replica, primary_replica_exports, replica_exports = (
            self._create_share_and_replica_get_exports()
        )

        # Share export locations list API
        share_exports = self.shares_v2_client.list_share_export_locations(
            share['id'], version=version)['export_locations']

        self._validate_export_location_api_behavior(replica, replica_exports,
                                                    primary_replica_exports,
                                                    share_exports, version)

    @decorators.idempotent_id('58430f57-c6eb-44e2-9583-eecb1dd10594')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @ddt.data(*utils.deduplicate(['2.46', '2.47', LATEST_MICROVERSION]))
    @testtools.skipUnless(
        CONF.share.backend_replication_type in
        (constants.REPLICATION_STYLE_READABLE, constants.REPLICATION_STYLE_DR),
        'Promotion of secondary not supported in writable replication style.')
    def test_replicated_share_export_locations_with_promotion(self, version):
        utils.check_skip_if_microversion_not_supported(version)
        share, replica, primary_replica_exports, replica_exports = (
            self._create_share_and_replica_get_exports(cleanup_replica=False)
        )
        primary_replica = self.shares_v2_client.get_share_replica(
            primary_replica_exports[0]['share_instance_id'])['share_replica']
        waiters.wait_for_resource_status(
            self.shares_v2_client, replica['id'],
            constants.REPLICATION_STATE_IN_SYNC, resource_name='share_replica',
            status_attr='replica_state')

        # Share export locations list API
        share_exports = self.shares_v2_client.list_share_export_locations(
            share['id'], version=version)['export_locations']

        # Validate API behavior
        self._validate_export_location_api_behavior(replica, replica_exports,
                                                    primary_replica_exports,
                                                    share_exports, version)

        # Promote share replica
        self.promote_share_replica(replica['id'])

        # Refresh for verification
        current_secondary_replica = self.shares_v2_client.get_share_replica(
            primary_replica['id'])['share_replica']
        current_primary_replica_exports = self._remove_admin_only_exports(
            self.admin_client.list_share_instance_export_locations(
                replica['id'], version=version)['export_locations'])
        current_secondary_replica_exports = self._remove_admin_only_exports(
            self.admin_client.list_share_instance_export_locations(
                primary_replica['id'], version=version)['export_locations'])
        share_exports = self.shares_v2_client.list_share_export_locations(
            share['id'], version=version)['export_locations']

        # Validate API behavior
        self._validate_export_location_api_behavior(
            current_secondary_replica, current_secondary_replica_exports,
            current_primary_replica_exports, share_exports, version)

        # Delete the secondary (the 'active' replica before promotion)
        self.delete_share_replica(primary_replica['id'])

    @decorators.idempotent_id('10ab6304-a1cd-4e60-90e3-7f9358b8808a')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @utils.skip_if_microversion_not_supported('2.47')
    def test_replica_export_locations(self):
        """Validates exports from the replica export locations APIs"""
        el_summary_keys = ['id', 'path', 'replica_state',
                           'availability_zone', 'preferred']
        el_detail_keys = el_summary_keys + ['created_at', 'updated_at']
        share, replica, expected_primary_exports, expected_replica_exports = (
            self._create_share_and_replica_get_exports()
        )
        primary_replica = self.shares_v2_client.get_share_replica(
            expected_primary_exports[0]['share_instance_id'])['share_replica']
        expected_primary_export_paths = [e['path'] for e in
                                         expected_primary_exports]
        expected_replica_export_paths = [e['path'] for e in
                                         expected_replica_exports]

        # For the primary replica
        actual_primary_exports = (
            self.shares_v2_client.list_share_replica_export_locations(
                primary_replica['id'])['export_locations']
        )

        self.assertEqual(len(expected_primary_exports),
                         len(actual_primary_exports))
        for export in actual_primary_exports:
            self.assertIn(export['path'], expected_primary_export_paths)
            self.assertEqual(constants.REPLICATION_STATE_ACTIVE,
                             export['replica_state'])
            self.assertEqual(share['availability_zone'],
                             export['availability_zone'])
            self.assertEqual(sorted(el_summary_keys), sorted(export.keys()))

            export_location_details = (
                self.shares_v2_client.get_share_replica_export_location(
                    primary_replica['id'], export['id'])['export_location']
            )
            self.assertEqual(sorted(el_detail_keys),
                             sorted(export_location_details.keys()))
            for key in el_summary_keys:
                self.assertEqual(export[key], export_location_details[key])

        # For the secondary replica
        actual_replica_exports = (
            self.shares_v2_client.list_share_replica_export_locations(
                replica['id'])['export_locations']
        )

        self.assertEqual(len(expected_replica_exports),
                         len(actual_replica_exports))
        for export in actual_replica_exports:
            self.assertIn(export['path'], expected_replica_export_paths)
            self.assertEqual(replica['replica_state'],
                             export['replica_state'])
            self.assertEqual(replica['availability_zone'],
                             export['availability_zone'])
            self.assertEqual(sorted(el_summary_keys), sorted(export.keys()))

            export_location_details = (
                self.shares_v2_client.get_share_replica_export_location(
                    replica['id'], export['id'])['export_location']
            )
            self.assertEqual(sorted(el_detail_keys),
                             sorted(export_location_details.keys()))
            for key in el_summary_keys:
                self.assertEqual(export[key], export_location_details[key])
예제 #9
0
class ShareGroupsTest(base.BaseSharesAdminTest):
    @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 2 share_types
        extra_specs = {}
        if CONF.share.capability_snapshot_support:
            extra_specs.update({'snapshot_support': True})
        cls.share_type = cls.create_share_type(extra_specs=extra_specs)
        cls.share_type_id = cls.share_type['id']
        cls.share_type2 = cls.create_share_type(extra_specs=extra_specs)
        cls.share_type_id2 = cls.share_type2['id']

        # Create a share group type
        name = data_utils.rand_name("unique_sgt_name")
        cls.sg_type = cls.create_share_group_type(
            name=name,
            share_types=[cls.share_type_id, cls.share_type_id2],
            cleanup_in_class=True,
            version=constants.MIN_SHARE_GROUP_MICROVERSION)
        cls.sg_type_id = cls.sg_type['id']

    @decorators.idempotent_id('79eaa86f-4c8f-49fd-acb2-ec051aa6bf93')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_GROUP_MICROVERSION,
        constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    def test_create_share_group_with_single_share_type_min(self, version):
        utils.check_skip_if_microversion_not_supported(version)
        share_group = self.create_share_group(
            share_group_type_id=self.sg_type_id,
            cleanup_in_class=False,
            share_type_ids=[self.share_type_id],
            version=version)

        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
            })

        actual_sg_type = share_group['share_group_type_id']
        expected_sg_type = self.sg_type_id
        self.assertEqual(
            expected_sg_type, actual_sg_type,
            'Incorrect share group type applied to share group '
            '%s. Expected %s, got %s' %
            (share_group['id'], expected_sg_type, actual_sg_type))

        actual_share_types = share_group['share_types']
        expected_share_types = [self.share_type_id]
        self.assertEqual(
            sorted(expected_share_types), sorted(actual_share_types),
            'Incorrect share types applied to share group %s. '
            'Expected %s, got %s' %
            (share_group['id'], expected_share_types, actual_share_types))

    @decorators.idempotent_id('ff6c17af-03ba-4506-923d-b6c229492d0e')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    def test_create_share_group_with_multiple_share_types_min(self):
        share_group = self.create_share_group(
            share_group_type_id=self.sg_type_id,
            cleanup_in_class=False,
            share_type_ids=[self.share_type_id, self.share_type_id2],
            version=constants.MIN_SHARE_GROUP_MICROVERSION)

        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
            })

        actual_sg_type = share_group['share_group_type_id']
        expected_sg_type = self.sg_type_id
        self.assertEqual(
            expected_sg_type, actual_sg_type,
            'Incorrect share group type applied to share group %s. '
            'Expected %s, got %s' %
            (share_group['id'], expected_sg_type, actual_sg_type))

        actual_share_types = share_group['share_types']
        expected_share_types = [self.share_type_id, self.share_type_id2]
        self.assertEqual(
            sorted(expected_share_types), sorted(actual_share_types),
            'Incorrect share types applied to share group %s. '
            'Expected %s, got %s' %
            (share_group['id'], expected_share_types, actual_share_types))

    @decorators.idempotent_id('99f0471c-e978-42ac-b50b-848b16692eab')
    @testtools.skipUnless(CONF.share.default_share_type_name,
                          "Only if defaults are defined.")
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_GROUP_MICROVERSION,
        constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    def test_default_share_group_type_applied(self, version):
        utils.check_skip_if_microversion_not_supported(version)
        try:
            default_type = self.shares_v2_client.get_default_share_group_type(
                version=version)['share_group_type']
        except exceptions.NotFound:
            msg = "There is no default share group type"
            raise self.skipException(msg)
        default_share_types = default_type['share_types']

        share_group = self.create_share_group(
            cleanup_in_class=False,
            share_type_ids=default_share_types,
            version=version)

        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
            })

        actual_sg_type = share_group['share_group_type_id']
        expected_sg_type = default_type['id']
        self.assertEqual(
            expected_sg_type, actual_sg_type,
            'Incorrect share group type applied to share group %s. '
            'Expected %s, got %s' %
            (share_group['id'], expected_sg_type, actual_sg_type))

    @decorators.idempotent_id('8ca1f0a0-2a36-4adb-af6b-6741b00307c5')
    @testtools.skipUnless(CONF.share.multitenancy_enabled,
                          "Only for multitenancy.")
    @testtools.skipUnless(CONF.share.run_snapshot_tests,
                          "Snapshot tests are disabled.")
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    def test_create_sg_from_snapshot_verify_share_server_information_min(self):
        # Create a share group
        orig_sg = self.create_share_group(
            share_group_type_id=self.sg_type_id,
            cleanup_in_class=False,
            share_type_ids=[self.share_type_id],
            version=constants.MIN_SHARE_GROUP_MICROVERSION)

        # Get latest share group information
        orig_sg = self.shares_v2_client.get_share_group(
            orig_sg['id'],
            version=constants.MIN_SHARE_GROUP_MICROVERSION)['share_group']

        # Assert share server information
        self.assertIsNotNone(orig_sg['share_network_id'])
        self.assertIsNotNone(orig_sg['share_server_id'])

        sg_snapshot = self.create_share_group_snapshot_wait_for_active(
            orig_sg['id'],
            cleanup_in_class=False,
            version=constants.MIN_SHARE_GROUP_MICROVERSION)
        new_sg = self.create_share_group(
            share_group_type_id=self.sg_type_id,
            cleanup_in_class=False,
            version=constants.MIN_SHARE_GROUP_MICROVERSION,
            source_share_group_snapshot_id=sg_snapshot['id'])

        # Assert share server information
        self.assertEqual(orig_sg['share_network_id'],
                         new_sg['share_network_id'])
        self.assertEqual(orig_sg['share_server_id'], new_sg['share_server_id'])

    @decorators.idempotent_id('93fd4a97-a25a-4a17-b5ae-c8894c1adfc5')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    def test_create_sg_with_sg_type_but_without_any_group_specs(self):
        # Create share group type not specifying any group specs
        sg_type = self.create_share_group_type(
            name=data_utils.rand_name("tempest-manila"),
            share_types=[self.share_type_id],
            group_specs={},
            cleanup_in_class=False)

        # Create share group, it should be created always, because we do not
        # restrict choice anyhow.
        self.create_share_group(share_type_ids=[self.share_type_id],
                                share_group_type_id=sg_type['id'],
                                cleanup_in_class=False)
class ReplicationAdminTest(base.BaseSharesMixedTest):
    @classmethod
    def skip_checks(cls):
        super(ReplicationAdminTest, cls).skip_checks()
        if not CONF.share.run_replication_tests:
            raise cls.skipException('Replication tests are disabled.')

        utils.check_skip_if_microversion_not_supported(
            _MIN_SUPPORTED_MICROVERSION)

    @classmethod
    def resource_setup(cls):
        super(ReplicationAdminTest, cls).resource_setup()
        cls.admin_client = cls.admin_shares_v2_client
        cls.replication_type = CONF.share.backend_replication_type
        cls.multitenancy_enabled = (
            utils.replication_with_multitenancy_support())

        if cls.replication_type not in constants.REPLICATION_TYPE_CHOICES:
            raise share_exceptions.ShareReplicationTypeException(
                replication_type=cls.replication_type)

        extra_specs = {"replication_type": cls.replication_type}
        cls.share_type = cls.create_share_type(extra_specs=extra_specs)
        cls.share_type_id = cls.share_type['id']
        cls.sn_id = None
        if cls.multitenancy_enabled:
            cls.share_network = cls.shares_v2_client.get_share_network(
                cls.shares_v2_client.share_network_id)['share_network']
            cls.sn_id = cls.share_network['id']

        cls.zones = cls.get_availability_zones_matching_share_type(
            cls.share_type, client=cls.admin_client)
        cls.share_zone = cls.zones[0]
        cls.replica_zone = cls.zones[-1]

        # Create share with above share_type
        cls.share = cls.create_share(share_type_id=cls.share_type_id,
                                     availability_zone=cls.share_zone,
                                     share_network_id=cls.sn_id,
                                     client=cls.admin_client)
        cls.replica = cls.admin_client.list_share_replicas(
            share_id=cls.share['id'])['share_replicas'][0]

    @staticmethod
    def _filter_share_replica_list(replica_list, r_state):
        # Iterate through replica list to filter based on replica_state
        return [
            replica['id'] for replica in replica_list
            if replica['replica_state'] == r_state
        ]

    @decorators.unstable_test(bug='1631314')
    @decorators.idempotent_id('0213cdfd-6a0f-4f24-a154-69796888a64a')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_REPLICATION_VERSION,
        constants.SHARE_REPLICA_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    def test_promote_out_of_sync_share_replica(self, version):
        """Test promote 'out_of_sync' share replica to active state."""
        utils.check_skip_if_microversion_not_supported(version)
        if (self.replication_type
                not in constants.REPLICATION_PROMOTION_CHOICES):
            msg = "Option backend_replication_type should be one of (%s)!"
            raise self.skipException(
                msg % ','.join(constants.REPLICATION_PROMOTION_CHOICES))
        share = self.create_share(share_type_id=self.share_type_id,
                                  client=self.admin_client,
                                  availability_zone=self.share_zone,
                                  share_network_id=self.sn_id)
        original_replica = self.admin_client.list_share_replicas(
            share_id=share['id'], version=version)['share_replicas'][0]

        # NOTE(Yogi1): Cleanup needs to be disabled for replica that is
        # being promoted since it will become the 'primary'/'active' replica.
        replica = self.create_share_replica(share["id"],
                                            self.replica_zone,
                                            cleanup=False,
                                            client=self.admin_client,
                                            version=version)
        # Wait for replica state to update after creation
        waiters.wait_for_resource_status(self.admin_client,
                                         replica['id'],
                                         constants.REPLICATION_STATE_IN_SYNC,
                                         resource_name='share_replica',
                                         status_attr='replica_state')

        # List replicas
        replica_list = self.admin_client.list_share_replicas(
            share_id=share['id'], version=version)['share_replicas']

        # Check if there is only 1 'active' replica before promotion.
        active_replicas = self._filter_share_replica_list(
            replica_list, constants.REPLICATION_STATE_ACTIVE)
        self.assertEqual(1, len(active_replicas))

        # Set replica_state to 'out_of_sync'
        self.admin_client.reset_share_replica_state(
            replica['id'],
            constants.REPLICATION_STATE_OUT_OF_SYNC,
            version=version)
        waiters.wait_for_resource_status(
            self.admin_client,
            replica['id'],
            constants.REPLICATION_STATE_OUT_OF_SYNC,
            resource_name='share_replica',
            status_attr='replica_state')

        # Promote 'out_of_sync' replica to 'active' state.
        self.promote_share_replica(replica['id'],
                                   self.admin_client,
                                   version=version)
        # Original replica will need to be cleaned up before the promoted
        # replica can be deleted.
        self.addCleanup(self.delete_share_replica,
                        original_replica['id'],
                        client=self.admin_client)

        # Check if there is still only 1 'active' replica after promotion.
        replica_list = self.admin_client.list_share_replicas(
            share_id=self.share["id"], version=version)['share_replicas']
        new_active_replicas = self._filter_share_replica_list(
            replica_list, constants.REPLICATION_STATE_ACTIVE)
        self.assertEqual(1, len(new_active_replicas))

    @decorators.idempotent_id('22a199b7-f4f6-4ede-b09f-8047a9d01cad')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_REPLICATION_VERSION,
        constants.SHARE_REPLICA_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    def test_force_delete_share_replica(self, version):
        """Test force deleting a replica that is in 'error_deleting' status."""
        utils.check_skip_if_microversion_not_supported(version)
        replica = self.create_share_replica(self.share['id'],
                                            self.replica_zone,
                                            cleanup_in_class=False,
                                            client=self.admin_client,
                                            version=version)
        self.admin_client.reset_share_replica_status(
            replica['id'], constants.STATUS_ERROR_DELETING, version=version)
        waiters.wait_for_resource_status(self.admin_client,
                                         replica['id'],
                                         constants.STATUS_ERROR_DELETING,
                                         resource_name='share_replica')
        self.admin_client.force_delete_share_replica(replica['id'],
                                                     version=version)
        self.admin_client.wait_for_resource_deletion(replica_id=replica['id'])

    @decorators.idempotent_id('16bd90f0-c478-4a99-8633-b18703ff56fa')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_REPLICATION_VERSION,
        constants.SHARE_REPLICA_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    def test_reset_share_replica_status(self, version):
        """Test resetting a replica's 'status' attribute."""
        utils.check_skip_if_microversion_not_supported(version)
        replica = self.create_share_replica(self.share['id'],
                                            self.replica_zone,
                                            cleanup_in_class=False,
                                            client=self.admin_client,
                                            version=version)
        self.admin_client.reset_share_replica_status(replica['id'],
                                                     constants.STATUS_ERROR,
                                                     version=version)
        waiters.wait_for_resource_status(self.admin_client,
                                         replica['id'],
                                         constants.STATUS_ERROR,
                                         resource_name='share_replica')

    @decorators.idempotent_id('258844da-a853-42b6-87db-b16e616018c6')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_REPLICATION_VERSION,
        constants.SHARE_REPLICA_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    def test_reset_share_replica_state(self, version):
        """Test resetting a replica's 'replica_state' attribute."""
        utils.check_skip_if_microversion_not_supported(version)
        replica = self.create_share_replica(self.share['id'],
                                            self.replica_zone,
                                            cleanup_in_class=False,
                                            client=self.admin_client,
                                            version=version)
        self.admin_client.reset_share_replica_state(replica['id'],
                                                    constants.STATUS_ERROR,
                                                    version=version)
        waiters.wait_for_resource_status(self.admin_client,
                                         replica['id'],
                                         constants.STATUS_ERROR,
                                         resource_name='share_replica',
                                         status_attr='replica_state')

    @decorators.unstable_test(bug='1631314')
    @decorators.idempotent_id('2969565a-85e8-4c61-9dfb-cc7f7ca9f6dd')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_REPLICATION_VERSION,
        constants.SHARE_REPLICA_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    def test_resync_share_replica(self, version):
        """Test resyncing a replica."""
        utils.check_skip_if_microversion_not_supported(version)
        replica = self.create_share_replica(self.share['id'],
                                            self.replica_zone,
                                            cleanup_in_class=False,
                                            client=self.admin_client,
                                            version=version)
        waiters.wait_for_resource_status(self.admin_client,
                                         replica['id'],
                                         constants.REPLICATION_STATE_IN_SYNC,
                                         resource_name='share_replica',
                                         status_attr='replica_state')

        # Set replica_state to 'out_of_sync'.
        self.admin_client.reset_share_replica_state(
            replica['id'],
            constants.REPLICATION_STATE_OUT_OF_SYNC,
            version=version)
        waiters.wait_for_resource_status(
            self.admin_client,
            replica['id'],
            constants.REPLICATION_STATE_OUT_OF_SYNC,
            resource_name='share_replica',
            status_attr='replica_state')

        # Attempt resync
        self.admin_client.resync_share_replica(replica['id'], version=version)
        waiters.wait_for_resource_status(self.admin_client,
                                         replica['id'],
                                         constants.REPLICATION_STATE_IN_SYNC,
                                         resource_name='share_replica',
                                         status_attr='replica_state')
class ShareGroupRenameTest(base.BaseSharesMixedTest):
    @classmethod
    def skip_checks(cls):
        super(ShareGroupRenameTest, 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(ShareGroupRenameTest, cls).resource_setup()

        # Create a share type
        cls.share_type = cls.create_share_type()
        cls.share_type_id = cls.share_type['id']

        # Create a share group type
        cls.share_group_type = cls._create_share_group_type()
        cls.share_group_type_id = cls.share_group_type['id']

        # Create share group
        cls.share_group_name = data_utils.rand_name("tempest-sg-name")
        cls.share_group_desc = data_utils.rand_name("tempest-sg-description")
        cls.share_group = cls.create_share_group(
            name=cls.share_group_name,
            description=cls.share_group_desc,
            share_group_type_id=cls.share_group_type_id,
            share_type_ids=[cls.share_type_id])

    def _rollback_share_group_update(self, version):
        self.shares_v2_client.update_share_group(
            self.share_group["id"],
            name=self.share_group_name,
            description=self.share_group_desc,
            version=version,
        )

    @decorators.idempotent_id('7f0a07ce-afdd-4c51-a29c-d8fe6cb5f6a5')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_GROUP_MICROVERSION,
        constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    def test_update_share_group(self, version):
        utils.check_skip_if_microversion_not_supported(version)

        # Get share_group
        share_group = self.shares_v2_client.get_share_group(
            self.share_group['id'], version=version)['share_group']
        self.assertEqual(self.share_group_name, share_group["name"])
        self.assertEqual(self.share_group_desc, share_group["description"])

        # Update share_group
        new_name = data_utils.rand_name("tempest-new-name")
        new_desc = data_utils.rand_name("tempest-new-description")
        updated = self.shares_v2_client.update_share_group(
            share_group["id"],
            name=new_name,
            description=new_desc,
            version=version,
        )['share_group']
        self.assertEqual(new_name, updated["name"])
        self.assertEqual(new_desc, updated["description"])

        # Get share_group
        share_group = self.shares_v2_client.get_share_group(
            self.share_group['id'],
            version=version,
        )['share_group']
        self.assertEqual(new_name, share_group["name"])
        self.assertEqual(new_desc, share_group["description"])

        # Rollback the update since this is a ddt and the class resources are
        # going to be reused
        self._rollback_share_group_update(version)

    @decorators.idempotent_id('611b1555-df09-499b-8aef-e8261e3f6863')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_GROUP_MICROVERSION,
        constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    def test_create_update_read_share_group_with_unicode(self, version):
        utils.check_skip_if_microversion_not_supported(version)

        value1 = u'ಠ_ಠ'
        value2 = u'ಠ_ರೃ'

        # Create share_group
        share_group = self.create_share_group(
            cleanup_in_class=False,
            name=value1,
            description=value1,
            version=version,
            share_group_type_id=self.share_group_type_id,
            share_type_ids=[self.share_type_id])
        self.assertEqual(value1, share_group["name"])
        self.assertEqual(value1, share_group["description"])

        # Update share group
        updated = self.shares_v2_client.update_share_group(
            share_group["id"],
            name=value2,
            description=value2,
            version=version,
        )['share_group']
        self.assertEqual(value2, updated["name"])
        self.assertEqual(value2, updated["description"])

        # Get share group
        share_group = self.shares_v2_client.get_share_group(
            share_group['id'], version=version)['share_group']
        self.assertEqual(value2, share_group["name"])
        self.assertEqual(value2, share_group["description"])

        # Rollback the update since this is a ddt and the class resources are
        # going to be reused
        self._rollback_share_group_update(version)
class ShareGroupActionsTest(base.BaseSharesMixedTest):
    """Covers share group functionality."""
    @classmethod
    def skip_checks(cls):
        super(ShareGroupActionsTest, 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(ShareGroupActionsTest, cls).resource_setup()

        # Create a 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']

        cls.share_group_type = cls._create_share_group_type()
        cls.share_group_type_id = cls.share_group_type['id']

        # Create first share group
        cls.share_group_name = data_utils.rand_name("tempest-sg-name")
        cls.share_group_desc = data_utils.rand_name("tempest-sg-description")
        cls.share_group = cls.create_share_group(
            name=cls.share_group_name,
            description=cls.share_group_desc,
            share_group_type_id=cls.share_group_type_id,
            share_type_ids=[cls.share_type_id],
        )

        # Create second share group for purposes of sorting and snapshot
        # filtering
        cls.share_group2 = cls.create_share_group(
            name=cls.share_group_name,
            description=cls.share_group_desc,
            share_group_type_id=cls.share_group_type_id,
            share_type_ids=[cls.share_type_id],
        )

        # Create 2 shares - inside first and second share groups
        cls.share_name = data_utils.rand_name("tempest-share-name")
        cls.share_desc = data_utils.rand_name("tempest-share-description")
        cls.share_size = CONF.share.share_size
        cls.share_size2 = CONF.share.share_size + 2
        cls.shares = cls.create_shares([{
            'kwargs': {
                'name': cls.share_name,
                'description': cls.share_desc,
                'size': size,
                'share_type_id': cls.share_type_id,
                'share_group_id': sg_id,
            }
        } for size, sg_id in ((cls.share_size, cls.share_group['id']),
                              (cls.share_size2, cls.share_group['id']),
                              (cls.share_size, cls.share_group2['id']))])

        # Create share group snapshots
        if CONF.share.capability_snapshot_support:
            cls.sg_snap_name = data_utils.rand_name("tempest-sg-snap-name")
            cls.sg_snap_desc = data_utils.rand_name("tempest-sg-snap-desc")

            cls.sg_snapshot = cls.create_share_group_snapshot_wait_for_active(
                cls.share_group["id"],
                name=cls.sg_snap_name,
                description=cls.sg_snap_desc,
            )

            cls.sg_snapshot2 = cls.create_share_group_snapshot_wait_for_active(
                cls.share_group2['id'],
                name=cls.sg_snap_name,
                description=cls.sg_snap_desc,
            )

    @decorators.idempotent_id('1e359389-09a7-4235-84c9-7b5c83632fff')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_GROUP_MICROVERSION,
        constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    def test_get_share_group(self, version):
        utils.check_skip_if_microversion_not_supported(version)

        # Get share group
        share_group = self.shares_v2_client.get_share_group(
            self.share_group['id'], version=version)['share_group']

        # Verify keys
        actual_keys = set(share_group.keys())
        self.assertTrue(
            constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS.issubset(actual_keys),
            'Not all required keys returned for share group %s. '
            'Expected at least: %s, found %s' %
            (share_group['id'], constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS,
             actual_keys))

        # Verify values
        self.assertEqual(self.share_group_name, share_group["name"])
        self.assertEqual(self.share_group_desc, share_group["description"])

    @decorators.idempotent_id('45b77673-b1bb-43a1-b4b7-41351930adbd')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    def test_get_share_min_supported_sg_microversion(self):

        # Get share
        share = self.shares_v2_client.get_share(
            self.shares[0]['id'],
            version=constants.MIN_SHARE_GROUP_MICROVERSION)['share']

        # Verify keys
        expected_keys = {
            "status",
            "description",
            "links",
            "availability_zone",
            "created_at",
            "share_proto",
            "name",
            "snapshot_id",
            "id",
            "size",
            "share_group_id",
        }
        actual_keys = set(share.keys())
        self.assertTrue(
            expected_keys.issubset(actual_keys),
            'Not all required keys returned for share %s.  '
            'Expected at least: %s, found %s' %
            (share['id'], expected_keys, actual_keys))

        # Verify values
        self.assertEqual(self.share_name, share["name"])
        self.assertEqual(self.share_desc, share["description"])
        self.assertEqual(self.share_size, int(share["size"]))
        self.assertEqual(self.share_group["id"], share["share_group_id"])

    @decorators.idempotent_id('04fcd695-c5f8-4de7-ab09-131424e6bdfb')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_GROUP_MICROVERSION,
        constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    def test_list_share_groups(self, version):
        utils.check_skip_if_microversion_not_supported(version)

        # List share groups
        share_groups = self.shares_v2_client.list_share_groups(
            version=version)['share_groups']

        # Verify keys
        self.assertGreater(len(share_groups), 0)
        for sg in share_groups:
            keys = set(sg.keys())
            self.assertEqual(
                constants.SHARE_GROUP_SIMPLE_KEYS, keys,
                'Incorrect keys returned for share group %s. '
                'Expected: %s, found %s' %
                (sg['id'], constants.SHARE_GROUP_SIMPLE_KEYS, ','.join(keys)))

        # Share group ids are in list exactly once
        for sg_id in (self.share_group["id"], self.share_group2["id"]):
            gen = [sg["id"] for sg in share_groups if sg["id"] == sg_id]
            msg = ("Expected id %s exactly once in share group list" % sg_id)
            self.assertEqual(1, len(gen), msg)

    @decorators.idempotent_id('16986c21-ecbc-429e-ab3d-8d1596a3eac4')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_GROUP_MICROVERSION, '2.36',
        constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    def test_list_share_groups_with_detail_min(self, version):
        utils.check_skip_if_microversion_not_supported(version)
        params = None
        if utils.is_microversion_ge(version, '2.36'):
            params = {'name~': 'tempest', 'description~': 'tempest'}
        # List share groups
        share_groups = self.shares_v2_client.list_share_groups(
            detailed=True, params=params, version=version)['share_groups']

        # Verify keys
        for sg in share_groups:
            keys = set(sg.keys())
            self.assertTrue(
                constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS.issubset(keys),
                'Not all required keys returned for share group %s.  '
                'Expected at least: %s, found %s' % (
                    sg['id'],
                    constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS,
                    ','.join(keys),
                ))

        # Share group ids are in list exactly once
        for group_id in (self.share_group["id"], self.share_group2["id"]):
            gen = [
                share_group["id"] for share_group in share_groups
                if share_group["id"] == group_id
            ]
            msg = ("Expected id %s exactly once in share group list" %
                   group_id)
            self.assertEqual(1, len(gen), msg)

    @decorators.idempotent_id('e72be2f9-56db-467f-89d7-0dddbf7e37e9')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    def test_filter_shares_by_share_group_id_min(self):
        shares = self.shares_v2_client.list_shares(
            detailed=True,
            params={'share_group_id': self.share_group['id']},
            version=constants.MIN_SHARE_GROUP_MICROVERSION,
        )['shares']

        share_ids = [share['id'] for share in shares]

        self.assertEqual(
            2, len(shares), 'Incorrect number of shares returned. '
            'Expected 2, got %s' % len(shares))
        self.assertIn(
            self.shares[0]['id'], share_ids,
            'Share %s expected in returned list, but got %s' %
            (self.shares[0]['id'], share_ids))
        self.assertIn(
            self.shares[1]['id'], share_ids,
            'Share %s expected in returned list, but got %s' %
            (self.shares[0]['id'], share_ids))

    @decorators.idempotent_id('5d2ca4f5-04da-4528-af47-ec980b95e884')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_GROUP_MICROVERSION,
        constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    @testtools.skipUnless(CONF.share.run_snapshot_tests,
                          "Snapshot tests are disabled.")
    def test_get_share_group_snapshot(self, version):
        utils.check_skip_if_microversion_not_supported(version)

        # Get share group snapshot
        sg_snapshot = self.shares_v2_client.get_share_group_snapshot(
            self.sg_snapshot['id'],
            version=version,
        )['share_group_snapshot']

        # Verify keys
        actual_keys = set(sg_snapshot.keys())
        self.assertTrue(
            constants.SHARE_GROUP_SNAPSHOT_DETAIL_REQUIRED_KEYS.issubset(
                actual_keys),
            'Not all required keys returned for share group %s.  '
            'Expected at least: %s, found %s' % (
                sg_snapshot['id'],
                constants.SHARE_GROUP_DETAIL_REQUIRED_KEYS,
                actual_keys,
            ))

        # Verify values
        self.assertEqual(self.sg_snap_name, sg_snapshot["name"])
        self.assertEqual(self.sg_snap_desc, sg_snapshot["description"])

    @decorators.idempotent_id('67e8c099-f1c1-4972-9c51-bb7bfe1d7994')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @testtools.skipUnless(CONF.share.run_snapshot_tests,
                          "Snapshot tests are disabled.")
    def test_get_share_group_snapshot_members_min(self):
        sg_snapshot = self.shares_v2_client.get_share_group_snapshot(
            self.sg_snapshot['id'],
            version=constants.MIN_SHARE_GROUP_MICROVERSION,
        )['share_group_snapshot']
        sg_snapshot_members = sg_snapshot['members']
        member_share_ids = [m['share_id'] for m in sg_snapshot_members]
        self.assertEqual(
            2, len(sg_snapshot_members),
            'Unexpected number of share group snapshot members. '
            'Expected 2, got %s.' % len(sg_snapshot_members))
        # Verify each share is represented in the share group snapshot
        # appropriately
        for share_id in (self.shares[0]['id'], self.shares[1]['id']):
            self.assertIn(
                share_id, member_share_ids,
                'Share missing %s missing from share group '
                'snapshot. Found %s.' % (share_id, member_share_ids))
        for share in (self.shares[0], self.shares[1]):
            for member in sg_snapshot_members:
                if share['id'] == member['share_id']:
                    self.assertEqual(share['size'], member['size'])

    @decorators.idempotent_id('650c5fa7-11f2-48bd-b012-fc2e32b6f446')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @ddt.data(*utils.deduplicate([
        constants.MIN_SHARE_GROUP_MICROVERSION,
        constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION
    ]))
    @testtools.skipUnless(CONF.share.run_snapshot_tests,
                          "Snapshot tests are disabled.")
    @testtools.skipUnless(
        CONF.share.capability_create_share_from_snapshot_support,
        "Tests creating shares from snapshots are disabled.")
    def test_create_share_group_from_populated_share_group_snapshot(
            self, version):
        utils.check_skip_if_microversion_not_supported(version)

        sg_snapshot = self.shares_v2_client.get_share_group_snapshot(
            self.sg_snapshot['id'],
            version=version,
        )['share_group_snapshot']
        snapshot_members = sg_snapshot['members']

        new_share_group = self.create_share_group(
            cleanup_in_class=False,
            source_share_group_snapshot_id=self.sg_snapshot['id'],
            version=version,
            share_group_type_id=self.share_group_type_id,
        )

        new_share_group = self.shares_v2_client.get_share_group(
            new_share_group['id'],
            version=version,
        )['share_group']

        # Verify that share_network information matches source share group
        self.assertEqual(self.share_group['share_network_id'],
                         new_share_group['share_network_id'])

        new_shares = self.shares_v2_client.list_shares(
            params={'share_group_id': new_share_group['id']},
            detailed=True,
            version=version,
        )['shares']

        # Verify each new share is available
        for share in new_shares:
            self.assertEqual(
                'available', share['status'],
                'Share %s is not in available status.' % share['id'])

        # Verify each sgsnapshot member is represented in the new sg
        # appropriately
        share_source_member_ids = [
            share['source_share_group_snapshot_member_id']
            for share in new_shares
        ]
        for member in snapshot_members:
            self.assertIn(
                member['id'], share_source_member_ids,
                'Share group snapshot member %s not represented by '
                'share group %s.' % (member['id'], new_share_group['id']))
            for share in new_shares:
                if (share['source_share_group_snapshot_member_id'] == (
                        member['id'])):
                    self.assertEqual(member['size'], share['size'])
                    self.assertEqual(self.share_group['share_network_id'],
                                     share['share_network_id'])
예제 #13
0
class ShareTypesAdminTest(base.BaseSharesAdminTest):
    @decorators.idempotent_id('34000fb9-b595-4a10-8306-7465f9ebc45d')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    def test_share_type_create_delete(self):
        name = data_utils.rand_name("tempest-manila")
        extra_specs = self.add_extra_specs_to_dict()

        # Create share type
        st_create = self.shares_v2_client.create_share_type(
            name, extra_specs=extra_specs)
        self.assertEqual(name, st_create['share_type']['name'])
        st_id = st_create['share_type']['id']

        # Delete share type
        self.shares_v2_client.delete_share_type(st_id)

        # Verify deletion of share type
        self.shares_v2_client.wait_for_resource_deletion(st_id=st_id)
        self.assertRaises(lib_exc.NotFound,
                          self.shares_v2_client.get_share_type, st_id)

    def _verify_is_public_key_name(self, share_type, version):
        old_key_name = 'os-share-type-access:is_public'
        new_key_name = 'share_type_access:is_public'
        if utils.is_microversion_gt(version, "2.6"):
            self.assertIn(new_key_name, share_type)
            self.assertNotIn(old_key_name, share_type)
        else:
            self.assertIn(old_key_name, share_type)
            self.assertNotIn(new_key_name, share_type)

    def _verify_description(self, expect_des, share_type, version):
        if utils.is_microversion_ge(version, "2.41"):
            self.assertEqual(expect_des, share_type['description'])
        else:
            self.assertNotIn('description', share_type)

    @decorators.idempotent_id('228d8bab-0b31-433e-956f-9db9877e6573')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    @ddt.data('2.0', '2.6', '2.7', '2.40', '2.41')
    def test_share_type_create_get(self, version):
        utils.check_skip_if_microversion_not_supported(version)

        name = data_utils.rand_name("tempest-manila")
        description = None
        if utils.is_microversion_ge(version, "2.41"):
            description = "Description for share type"
        extra_specs = self.add_extra_specs_to_dict({
            "key": "value",
        })

        # Create share type
        st_create = self.create_share_type(name,
                                           extra_specs=extra_specs,
                                           version=version,
                                           description=description)
        self.assertEqual(name, st_create['name'])
        self._verify_description(description, st_create, version)
        self._verify_is_public_key_name(st_create, version)
        st_id = st_create["id"]

        # Get share type
        get = self.shares_v2_client.get_share_type(st_id, version=version)

        self.assertEqual(name, get["share_type"]["name"])
        self.assertEqual(st_id, get["share_type"]["id"])
        self._verify_description(description, get['share_type'], version)

        if utils.is_microversion_lt(version, "2.24"):
            # snapshot_support is an implied/required extra-spec until
            # version 2.24, and the service assumes it to be True since we
            # don't provide it during share type creation.
            extra_specs.update({"snapshot_support": 'True'})

        self.assertEqual(extra_specs, get["share_type"]["extra_specs"])
        self._verify_is_public_key_name(get['share_type'], version)

        # Check that backwards compatibility didn't break
        self.assertDictMatch(get["volume_type"], get["share_type"])

    @utils.skip_if_microversion_not_supported("2.50")
    @decorators.idempotent_id('a9af19e1-e789-4c4f-a39b-dd8df6ed00b1')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    @ddt.data(
        ('2.50', data_utils.rand_name("type_updated"), 'description_updated',
         True),
        ('2.50', data_utils.rand_name("type_updated"), None, None),
        ('2.50', None, 'description_updated', None),
        ('2.50', None, None, True),
        ('2.50', None, None, False),
        (LATEST_MICROVERSION, data_utils.rand_name("type_updated"),
         'description_updated', True),
        (LATEST_MICROVERSION, data_utils.rand_name("type_updated"), None,
         None),
        (LATEST_MICROVERSION, None, 'description_updated', None),
        (LATEST_MICROVERSION, None, None, True),
        (LATEST_MICROVERSION, None, None, False),
    )
    @ddt.unpack
    def test_share_type_create_update(self, version, st_name, st_description,
                                      st_is_public):
        name = data_utils.rand_name("tempest-manila")
        description = "Description for share type"
        extra_specs = self.add_extra_specs_to_dict({
            "key": "value",
        })

        # Create share type
        st_create = self.create_share_type(name,
                                           extra_specs=extra_specs,
                                           version=version,
                                           description=description)
        self.assertEqual(name, st_create['name'])
        self._verify_description(description, st_create, version)
        self._verify_is_public_key_name(st_create, version)
        st_id = st_create["id"]

        # Update share type
        updated_st = self.shares_v2_client.update_share_type(
            st_id,
            name=st_name,
            is_public=st_is_public,
            description=st_description,
            version=version)
        if st_name is not None:
            self.assertEqual(st_name, updated_st["share_type"]["name"])
        if st_description is not None:
            self._verify_description(st_description, updated_st['share_type'],
                                     version)
        if st_is_public is not None:
            self.assertEqual(
                st_is_public,
                updated_st["share_type"]["share_type_access:is_public"])

    @utils.skip_if_microversion_not_supported("2.50")
    @decorators.idempotent_id('9019dc61-b2b1-472d-9b15-a3986439d4c3')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    @ddt.data(
        ('2.50', None, '', None),
        (LATEST_MICROVERSION, None, '', None),
    )
    @ddt.unpack
    def test_share_type_unset_description(self, version, st_name,
                                          st_description, st_is_public):
        name = data_utils.rand_name("tempest-manila")
        description = "Description for share type"
        extra_specs = self.add_extra_specs_to_dict({
            "key": "value",
        })

        # Create share type
        st_create = self.create_share_type(name,
                                           extra_specs=extra_specs,
                                           version=version,
                                           description=description)
        self.assertEqual(name, st_create['name'])
        self._verify_description(description, st_create, version)
        self._verify_is_public_key_name(st_create, version)
        st_id = st_create["id"]

        # Update share type
        updated_st = self.shares_v2_client.update_share_type(
            st_id,
            name=st_name,
            is_public=st_is_public,
            description=st_description,
            version=version)

        self._verify_description(None, updated_st['share_type'], version)

    @decorators.idempotent_id('5cc4c2e5-d2a4-4bfc-9208-3455ac551f20')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    @ddt.data('2.0', '2.6', '2.7', '2.40', '2.41')
    def test_share_type_create_list(self, version):
        utils.check_skip_if_microversion_not_supported(version)

        name = data_utils.rand_name("tempest-manila")
        description = None
        if utils.is_microversion_ge(version, "2.41"):
            description = "Description for share type"
        extra_specs = self.add_extra_specs_to_dict()

        # Create share type
        st_create = self.create_share_type(name,
                                           extra_specs=extra_specs,
                                           version=version,
                                           description=description)
        self._verify_is_public_key_name(st_create, version)
        st_id = st_create["id"]

        # list share types
        st_list = self.shares_v2_client.list_share_types(version=version)
        sts = st_list["share_types"]
        self.assertGreaterEqual(len(sts), 1)
        self.assertTrue(any(st_id in st["id"] for st in sts))
        for st in sts:
            self._verify_is_public_key_name(st, version)

        # Check that backwards compatibility didn't break
        vts = st_list["volume_types"]
        self.assertEqual(len(sts), len(vts))
        for i in range(len(sts)):
            self.assertDictMatch(sts[i], vts[i])

    @decorators.idempotent_id('4e2ad320-9f3d-4797-a3ad-bf800bcd1831')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    def test_get_share_with_share_type(self):

        # Data
        share_name = data_utils.rand_name("share")
        shr_type_name = data_utils.rand_name("share-type")
        extra_specs = self.add_extra_specs_to_dict({
            "storage_protocol":
            CONF.share.capability_storage_protocol,
        })

        # Create share type
        st_create = self.create_share_type(shr_type_name,
                                           extra_specs=extra_specs)

        # Create share with share type
        share = self.create_share(name=share_name,
                                  share_type_id=st_create["id"])
        self.assertEqual(share["name"], share_name)
        waiters.wait_for_resource_status(self.shares_client, share["id"],
                                         "available")

        # Verify share info
        get = self.shares_v2_client.get_share(share["id"],
                                              version="2.5")['share']
        self.assertEqual(share_name, get["name"])
        self.assertEqual(share["id"], get["id"])
        self.assertEqual(shr_type_name, get["share_type"])

        get = self.shares_v2_client.get_share(share["id"],
                                              version="2.6")['share']
        self.assertEqual(st_create["id"], get["share_type"])
        self.assertEqual(shr_type_name, get["share_type_name"])

    @decorators.idempotent_id('d2261a27-d4a4-4237-9fad-f6fd8f27783a')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    def test_private_share_type_access(self):
        name = data_utils.rand_name("tempest-manila")
        extra_specs = self.add_extra_specs_to_dict({
            "key": "value",
        })
        project_id = self.shares_client.tenant_id

        # Create private share type
        st_create = self.create_share_type(name,
                                           False,
                                           extra_specs=extra_specs)
        self.assertEqual(name, st_create['name'])
        st_id = st_create["id"]

        # It should not be listed without access
        st_list = self.shares_v2_client.list_share_types()
        sts = st_list["share_types"]
        self.assertFalse(any(st_id in st["id"] for st in sts))

        # List projects that have access for share type - none expected
        access = self.shares_v2_client.list_access_to_share_type(
            st_id)['share_type_access']
        self.assertEmpty(access)

        # Add project access to share type
        access = self.shares_v2_client.add_access_to_share_type(
            st_id, project_id)

        # Now it should be listed
        st_list = self.shares_client.list_share_types()
        sts = st_list["share_types"]
        self.assertTrue(any(st_id in st["id"] for st in sts))

        # List projects that have access for share type - one expected
        access = self.shares_v2_client.list_access_to_share_type(
            st_id)['share_type_access']
        expected = [
            {
                'share_type_id': st_id,
                'project_id': project_id
            },
        ]
        self.assertEqual(expected, access)

        # Remove project access from share type
        access = self.shares_v2_client.remove_access_from_share_type(
            st_id, project_id)

        # It should not be listed without access
        st_list = self.shares_client.list_share_types()
        sts = st_list["share_types"]
        self.assertFalse(any(st_id in st["id"] for st in sts))

        # List projects that have access for share type - none expected
        access = self.shares_v2_client.list_access_to_share_type(
            st_id)['share_type_access']
        self.assertEmpty(access)

    @decorators.idempotent_id('90dca5c5-f28e-4f16-90ed-78f5d725664e')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    @ddt.data(*utils.deduplicate(('2.45', '2.46', LATEST_MICROVERSION)))
    def test_share_type_create_show_list_with_is_default_key(self, version):
        utils.check_skip_if_microversion_not_supported(version)
        name = data_utils.rand_name("tempest-manila")
        extra_specs = self.add_extra_specs_to_dict()

        # Create share type
        st_create = self.create_share_type(name,
                                           extra_specs=extra_specs,
                                           version=version)

        if utils.is_microversion_ge(version, '2.46'):
            self.assertIn('is_default', st_create)
            self.assertIs(False, st_create['is_default'])
        else:
            self.assertNotIn('is_default', st_create)

        # list share types
        st_list = self.shares_v2_client.list_share_types(version=version)
        for st_get in st_list['share_types']:
            if utils.is_microversion_ge(version, '2.46'):
                self.assertIn('is_default', st_get)
                if st_create['id'] == st_get['id']:
                    self.assertIs(False, st_get['is_default'])
                else:
                    self.assertTrue(st_get['is_default'] in (True, False))
            else:
                self.assertNotIn('is_default', st_get)

        # show share types
        st_id = st_create['id']
        st_show = self.shares_v2_client.get_share_type(
            st_id, version=version)['share_type']

        if utils.is_microversion_ge(version, '2.46'):
            self.assertIn('is_default', st_show)
            self.assertIs(False, st_show['is_default'])
        else:
            self.assertNotIn('is_default', st_show)
예제 #14
0
class ShareRulesTest(base.BaseSharesMixedTest):
    """A Test class to test access rules generically.

    Tests in this class don't care about the type of access rule or the
    protocol of the share created. They are meant to test the API semantics
    of the access rules APIs.
    """
    @classmethod
    def skip_checks(cls):
        super(ShareRulesTest, cls).skip_checks()
        if not (any(p in CONF.share.enable_ip_rules_for_protocols
                    for p in cls.protocols)
                or any(p in CONF.share.enable_user_rules_for_protocols
                       for p in cls.protocols)
                or any(p in CONF.share.enable_cert_rules_for_protocols
                       for p in cls.protocols)
                or any(p in CONF.share.enable_cephx_rules_for_protocols
                       for p in cls.protocols)):
            cls.message = "Rule tests are disabled"
            raise cls.skipException(cls.message)

    @classmethod
    def resource_setup(cls):
        super(ShareRulesTest, cls).resource_setup()
        cls.protocol = cls.shares_v2_client.share_protocol
        cls.access_type, cls.access_to = (
            cls._get_access_rule_data_from_config())
        cls.share_type = cls.create_share_type()
        cls.share_type_id = cls.share_type['id']
        cls.share = cls.create_share(share_type_id=cls.share_type_id)

    @decorators.idempotent_id('c52e95cc-d6ea-4d02-9b52-cd7c1913dfff')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @ddt.data(*utils.deduplicate(
        ['1.0', '2.9', '2.27', '2.28', '2.45', LATEST_MICROVERSION]))
    def test_list_access_rules(self, version):
        utils.check_skip_if_microversion_not_supported(version)
        if (utils.is_microversion_lt(version, '2.13')
                and CONF.share.enable_cephx_rules_for_protocols):
            msg = ("API version %s does not support cephx access type, need "
                   "version >= 2.13." % version)
            raise self.skipException(msg)

        metadata = None
        if utils.is_microversion_ge(version, '2.45'):
            metadata = {'key1': 'v1', 'key2': 'v2'}
        if utils.is_microversion_le(version, '2.9'):
            client = self.shares_client
        else:
            client = self.shares_v2_client
        # create rule
        rule = self.allow_access(self.share["id"],
                                 client=client,
                                 access_type=self.access_type,
                                 access_to=self.access_to,
                                 metadata=metadata,
                                 version=version)

        # verify added rule keys since 2.33 when create rule
        if utils.is_microversion_ge(version, '2.33'):
            self.assertIn('created_at', list(rule.keys()))
            self.assertIn('updated_at', list(rule.keys()))
        else:
            self.assertNotIn('created_at', list(rule.keys()))
            self.assertNotIn('updated_at', list(rule.keys()))

        # rules must start out in 'new' until 2.28 & 'queued_to_apply' after
        if utils.is_microversion_le(version, "2.27"):
            self.assertEqual("new", rule['state'])
        else:
            self.assertEqual("queued_to_apply", rule['state'])

        # list rules
        if utils.is_microversion_eq(version, '1.0'):
            rules = self.shares_client.list_access_rules(
                self.share["id"])['access_list']
        else:
            rules = self.shares_v2_client.list_access_rules(
                self.share["id"], version=version)['access_list']

        # verify keys
        keys = ("id", "access_type", "access_to", "access_level")
        if utils.is_microversion_ge(version, '2.21'):
            keys += ("access_key", )
        if utils.is_microversion_ge(version, '2.33'):
            keys += (
                "created_at",
                "updated_at",
            )
        if utils.is_microversion_ge(version, '2.45'):
            keys += ("metadata", )
        for key in keys:
            [self.assertIn(key, r.keys()) for r in rules]
        for key in ('deleted', 'deleted_at', 'instance_mappings'):
            [self.assertNotIn(key, r.keys()) for r in rules]

        # verify values
        self.assertEqual(self.access_type, rules[0]["access_type"])
        self.assertEqual(self.access_to, rules[0]["access_to"])
        self.assertEqual('rw', rules[0]["access_level"])
        if utils.is_microversion_ge(version, '2.21'):
            if self.access_type == 'cephx':
                self.assertIsNotNone(rules[0]['access_key'])
            else:
                self.assertIsNone(rules[0]['access_key'])

        # our share id in list and have no duplicates
        gen = [r["id"] for r in rules if r["id"] in rule["id"]]
        msg = "expected id lists %s times in rule list" % (len(gen))
        self.assertEqual(1, len(gen), msg)

    @decorators.idempotent_id('b77bcbda-9754-48f0-9be6-79341ad1af64')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
    @ddt.data(*utils.deduplicate(
        ['1.0', '2.9', '2.27', '2.28', LATEST_MICROVERSION]))
    def test_access_rules_deleted_if_share_deleted(self, version):
        if (utils.is_microversion_lt(version, '2.13')
                and CONF.share.enable_cephx_rules_for_protocols):
            msg = ("API version %s does not support cephx access type, need "
                   "version >= 2.13." % version)
            raise self.skipException(msg)
        if utils.is_microversion_le(version, '2.9'):
            client = self.shares_client
        else:
            client = self.shares_v2_client

        # create share
        share = self.create_share(share_type_id=self.share_type_id)

        # create rule
        rule = self.allow_access(share["id"],
                                 client=client,
                                 access_type=self.access_type,
                                 access_to=self.access_to,
                                 version=version,
                                 cleanup=False)

        # rules must start out in 'new' until 2.28 & 'queued_to_apply' after
        if utils.is_microversion_le(version, "2.27"):
            self.assertEqual("new", rule['state'])
        else:
            self.assertEqual("queued_to_apply", rule['state'])

        # delete share
        if utils.is_microversion_eq(version, '1.0'):
            self.shares_client.delete_share(share['id'])
            self.shares_client.wait_for_resource_deletion(share_id=share['id'])
        else:
            self.shares_v2_client.delete_share(share['id'], version=version)
            self.shares_v2_client.wait_for_resource_deletion(
                share_id=share['id'], version=version)

        # verify absence of rules for nonexistent share id
        if utils.is_microversion_eq(version, '1.0'):
            self.assertRaises(lib_exc.NotFound,
                              self.shares_client.list_access_rules,
                              share['id'])
        elif utils.is_microversion_lt(version, '2.45'):
            self.assertRaises(lib_exc.NotFound,
                              self.shares_v2_client.list_access_rules,
                              share['id'], version)
        else:
            self.assertRaises(lib_exc.BadRequest,
                              self.shares_v2_client.list_access_rules,
                              share['id'], version)
class SecurityServiceListMixin(object):
    @decorators.idempotent_id('f6f5657c-a93c-49ed-86e3-b351a92734d5')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    def test_list_security_services(self):
        listed = self.shares_client.list_security_services(
        )['security_services']
        self.assertTrue(any(self.ss_ldap['id'] == ss['id'] for ss in listed))
        self.assertTrue(
            any(self.ss_kerberos['id'] == ss['id'] for ss in listed))

        # verify keys
        keys = [
            "name",
            "id",
            "status",
            "type",
        ]
        [self.assertIn(key, s_s.keys()) for s_s in listed for key in keys]

    @decorators.idempotent_id('22b22937-7436-458c-ac22-8ff19feab253')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    @ddt.data(*utils.deduplicate(['1.0', '2.42', '2.44', LATEST_MICROVERSION]))
    def test_list_security_services_with_detail(self, version):
        utils.check_skip_if_microversion_not_supported(version)
        with_ou = True if utils.is_microversion_ge(version, '2.44') else False
        if utils.is_microversion_ge(version, '2.0'):
            listed = self.shares_v2_client.list_security_services(
                detailed=True, version=version)['security_services']
        else:
            listed = self.shares_client.list_security_services(
                detailed=True)['security_services']

        self.assertTrue(any(self.ss_ldap['id'] == ss['id'] for ss in listed))
        self.assertTrue(
            any(self.ss_kerberos['id'] == ss['id'] for ss in listed))

        # verify keys
        keys = [
            "name",
            "id",
            "status",
            "description",
            "domain",
            "server",
            "dns_ip",
            "user",
            "password",
            "type",
            "created_at",
            "updated_at",
            "project_id",
        ]
        [self.assertIn(key, s_s.keys()) for s_s in listed for key in keys]

        for ss in listed:
            self.assertEqual(with_ou, 'ou' in ss.keys())

    @decorators.idempotent_id('88f62835-0aee-4bed-a37f-ffd99430da8a')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    @testtools.skipIf(not CONF.share.multitenancy_enabled,
                      "Only for multitenancy.")
    def test_list_security_services_filter_by_share_network(self):
        sn = self.shares_client.get_share_network(
            self.shares_client.share_network_id)['share_network']
        fresh_sn = []
        for i in range(2):
            sn = self.create_share_network(
                add_security_services=False,
                neutron_net_id=sn["neutron_net_id"],
                neutron_subnet_id=sn["neutron_subnet_id"])
            fresh_sn.append(sn)

        self.shares_client.add_sec_service_to_share_network(
            fresh_sn[0]["id"], self.ss_ldap["id"])
        self.shares_client.add_sec_service_to_share_network(
            fresh_sn[1]["id"], self.ss_kerberos["id"])

        listed = self.shares_client.list_security_services(
            params={'share_network_id': fresh_sn[0]['id']
                    })['security_services']
        self.assertEqual(1, len(listed))
        self.assertEqual(self.ss_ldap['id'], listed[0]['id'])

        keys = [
            "name",
            "id",
            "status",
            "type",
        ]
        [self.assertIn(key, s_s.keys()) for s_s in listed for key in keys]

    @decorators.idempotent_id('f055faad-dd36-4eed-9b50-61280931dea2')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    def test_list_security_services_detailed_filter_by_ss_attributes(self):
        search_opts = {
            'name': 'ss_ldap',
            'type': 'ldap',
            'user': '******',
            'server': 'fake_server_1',
            'dns_ip': '1.1.1.1',
            'domain': 'fake_domain_1',
        }
        listed = self.shares_client.list_security_services(
            detailed=True, params=search_opts)['security_services']
        self.assertTrue(any(self.ss_ldap['id'] == ss['id'] for ss in listed))
        for ss in listed:
            self.assertTrue(
                all(ss[key] == value for key, value in search_opts.items()))
예제 #16
0
class ShareIpRulesForNFSTest(base.BaseSharesMixedTest):
    protocol = "nfs"

    @classmethod
    def skip_checks(cls):
        super(ShareIpRulesForNFSTest, cls).skip_checks()
        if (cls.protocol not in CONF.share.enable_protocols or cls.protocol
                not in CONF.share.enable_ip_rules_for_protocols):
            msg = "IP rule tests for %s protocol are disabled" % cls.protocol
            raise cls.skipException(msg)

    @classmethod
    def resource_setup(cls):
        super(ShareIpRulesForNFSTest, cls).resource_setup()
        # create share type
        cls.share_type = cls.create_share_type()
        cls.share_type_id = cls.share_type['id']

        # create share
        cls.share = cls.create_share(cls.protocol,
                                     share_type_id=cls.share_type_id)
        cls.access_type = "ip"
        cls.access_to = "2.2.2.2"

    @decorators.idempotent_id('3390df2d-f6f8-4634-a562-87c1be994f6a')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @ddt.data(*itertools.chain(
        itertools.product(
            utils.deduplicate(['1.0', '2.9', '2.37', LATEST_MICROVERSION]),
            [4]),
        itertools.product(utils.deduplicate(['2.38', LATEST_MICROVERSION]),
                          [6])))
    @ddt.unpack
    def test_create_delete_access_rules_with_one_ip(self, version, ip_version):

        if ip_version == 4:
            access_to = utils.rand_ip()
        else:
            access_to = utils.rand_ipv6_ip()

        if utils.is_microversion_le(version, '2.9'):
            client = self.shares_client
        else:
            client = self.shares_v2_client

        # create rule
        rule = self.allow_access(self.share["id"],
                                 client=client,
                                 access_type=self.access_type,
                                 access_to=access_to,
                                 version=version)

        self.assertEqual('rw', rule['access_level'])
        for key in ('deleted', 'deleted_at', 'instance_mappings'):
            self.assertNotIn(key, rule.keys())

        # rules must start out in 'new' until 2.28 & 'queued_to_apply' after
        if utils.is_microversion_le(version, "2.27"):
            self.assertEqual("new", rule['state'])
        else:
            self.assertEqual("queued_to_apply", rule['state'])

    @decorators.idempotent_id('5d25168a-d646-443e-8cf1-3151eb7887f5')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @ddt.data(*itertools.chain(
        itertools.product(
            utils.deduplicate(['1.0', '2.9', '2.37', LATEST_MICROVERSION]),
            [4]),
        itertools.product(utils.deduplicate(['2.38', LATEST_MICROVERSION]),
                          [6])))
    @ddt.unpack
    def test_create_delete_access_rule_with_cidr(self, version, ip_version):
        if ip_version == 4:
            access_to = utils.rand_ip(network=True)
        else:
            access_to = utils.rand_ipv6_ip(network=True)
        if utils.is_microversion_le(version, '2.9'):
            client = self.shares_client
        else:
            client = self.shares_v2_client
        # create rule
        rule = self.allow_access(self.share["id"],
                                 client=client,
                                 access_type=self.access_type,
                                 access_to=access_to,
                                 version=version)

        for key in ('deleted', 'deleted_at', 'instance_mappings'):
            self.assertNotIn(key, rule.keys())
        self.assertEqual('rw', rule['access_level'])

    @decorators.idempotent_id('187a4fb0-ba1d-45b9-83c9-f0272e7e6f3e')
    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
    @testtools.skipIf("nfs"
                      not in CONF.share.enable_ro_access_level_for_protocols,
                      "RO access rule tests are disabled for NFS protocol.")
    @ddt.data(*utils.deduplicate(
        ['1.0', '2.9', '2.27', '2.28', LATEST_MICROVERSION]))
    def test_create_delete_ro_access_rule(self, client_name):
        _create_delete_ro_access_rule(self, client_name)
class ExtraSpecsWriteAdminTest(base.BaseSharesAdminTest):

    def setUp(self):
        super(ExtraSpecsWriteAdminTest, self).setUp()
        self.required_extra_specs = self.add_extra_specs_to_dict()
        self.custom_extra_specs = {"key1": "value1", "key2": "value2"}
        self.share_type_name = data_utils.rand_name("share-type")

        # Create share type
        self.share_type = self.create_share_type(
            self.share_type_name, extra_specs=self.required_extra_specs)

        self.st_id = self.share_type['id']

        # Create extra specs for share type
        self.shares_client.create_share_type_extra_specs(
            self.st_id, self.custom_extra_specs)

    @decorators.idempotent_id('baba776b-1b05-42ca-a002-bff1f8f14e13')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    def test_update_one_share_type_extra_spec(self):
        self.custom_extra_specs["key1"] = "fake_value1_updated"

        # Update extra specs of share type
        update_one = self.shares_client.update_share_type_extra_spec(
            self.st_id, "key1", self.custom_extra_specs["key1"])
        self.assertEqual({"key1": self.custom_extra_specs["key1"]}, update_one)

        get = self.shares_client.get_share_type_extra_specs(
            self.st_id)['extra_specs']
        expected_extra_specs = self.custom_extra_specs
        expected_extra_specs.update(self.required_extra_specs)
        self.assertEqual(self.custom_extra_specs, get)

    @decorators.idempotent_id('374e7ff5-0d26-4778-bbb5-4ffd28521ad9')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    def test_update_all_share_type_extra_specs(self):
        self.custom_extra_specs["key2"] = "value2_updated"

        # Update extra specs of share type
        update_all = self.shares_client.update_share_type_extra_specs(
            self.st_id, self.custom_extra_specs)['extra_specs']
        self.assertEqual(self.custom_extra_specs, update_all)

        get = self.shares_client.get_share_type_extra_specs(
            self.st_id)['extra_specs']
        expected_extra_specs = self.custom_extra_specs
        expected_extra_specs.update(self.required_extra_specs)
        self.assertEqual(self.custom_extra_specs, get)

    @decorators.idempotent_id('129db0ce-9a4f-4a40-994f-e492174e0661')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    def test_delete_one_share_type_extra_spec(self):
        # Delete one extra spec for share type
        self.shares_client.delete_share_type_extra_spec(self.st_id, "key1")

        # Get metadata
        get = self.shares_client.get_share_type_extra_specs(
            self.st_id)['extra_specs']

        self.assertNotIn('key1', get)

    @decorators.idempotent_id('1b9f501d-8f34-46d0-b318-83bdfed571ec')
    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
    @ddt.data(*utils.deduplicate(['2.24', LATEST_MICROVERSION]))
    def test_delete_snapshot_support_extra_spec(self, version):
        """Is snapshot_support really an optional extra-spec if API > v2.24?"""
        utils.check_skip_if_microversion_not_supported(version)

        # set snapshot_support extra-spec
        self.shares_v2_client.update_share_type_extra_specs(
            self.st_id, {'snapshot_support': 'True'})

        # Get extra specs
        share_type_extra_specs = self.shares_client.get_share_type_extra_specs(
            self.st_id)['extra_specs']

        self.assertIn('snapshot_support', share_type_extra_specs)
        self.assertEqual('True', share_type_extra_specs['snapshot_support'])

        # Delete the 'snapshot_support' extra spec from the share type
        self.shares_v2_client.delete_share_type_extra_spec(
            self.st_id, 'snapshot_support', version=version)

        # Get extra specs
        share_type_extra_specs = self.shares_client.get_share_type_extra_specs(
            self.st_id)['extra_specs']

        self.assertNotIn('snapshot_support', share_type_extra_specs)