From ac9fbb994c7e5dd0bf6ac303bd76bc64cc598a4c Mon Sep 17 00:00:00 2001 From: zhongjun2 Date: Mon, 2 Jul 2018 12:30:25 +0000 Subject: [PATCH] Add tempest test for share access metadata Depends-On: https://review.openstack.org/#/c/570708/ Change-Id: Ia794e1b13fec092b139c4859af48159869d6869e Partially-implements bp: metadata-for-access-rule --- manila_tempest_tests/common/constants.py | 2 + manila_tempest_tests/config.py | 2 +- .../services/share/v2/json/shares_client.py | 59 +++++++- .../tests/api/test_access_rules_metadata.py | 133 ++++++++++++++++++ .../test_access_rules_metadata_negative.py | 80 +++++++++++ manila_tempest_tests/tests/api/test_rules.py | 16 ++- 6 files changed, 283 insertions(+), 9 deletions(-) create mode 100644 manila_tempest_tests/tests/api/test_access_rules_metadata.py create mode 100644 manila_tempest_tests/tests/api/test_access_rules_metadata_negative.py diff --git a/manila_tempest_tests/common/constants.py b/manila_tempest_tests/common/constants.py index 25cf4ee7..c56712ed 100644 --- a/manila_tempest_tests/common/constants.py +++ b/manila_tempest_tests/common/constants.py @@ -83,3 +83,5 @@ SHARE_GROUP_SNAPSHOT_DETAIL_REQUIRED_KEYS = { SHARE_GROUP_TYPE_REQUIRED_KEYS = { 'id', 'name', 'share_types', 'is_public', 'group_specs', } + +MIN_SHARE_ACCESS_METADATA_MICROVERSION = '2.45' diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py index e737cb41..0517c601 100644 --- a/manila_tempest_tests/config.py +++ b/manila_tempest_tests/config.py @@ -30,7 +30,7 @@ ShareGroup = [ help="The minimum api microversion is configured to be the " "value of the minimum microversion supported by Manila."), cfg.StrOpt("max_api_microversion", - default="2.44", + default="2.45", help="The maximum api microversion is configured to be the " "value of the latest microversion supported by Manila."), cfg.StrOpt("region", diff --git a/manila_tempest_tests/services/share/v2/json/shares_client.py b/manila_tempest_tests/services/share/v2/json/shares_client.py index 3f1c8a1c..522d467d 100644 --- a/manila_tempest_tests/services/share/v2/json/shares_client.py +++ b/manila_tempest_tests/services/share/v2/json/shares_client.py @@ -731,7 +731,8 @@ class SharesV2Client(shares_client.SharesClient): def create_access_rule(self, share_id, access_type="ip", access_to="0.0.0.0", access_level=None, - version=LATEST_MICROVERSION, action_name=None): + version=LATEST_MICROVERSION, metadata=None, + action_name=None): post_body = { self._get_access_action_name(version, 'os-allow_access'): { "access_type": access_type, @@ -739,6 +740,8 @@ class SharesV2Client(shares_client.SharesClient): "access_level": access_level, } } + if metadata is not None: + post_body['allow_access']['metadata'] = metadata body = json.dumps(post_body) resp, body = self.post( "shares/%s/action" % share_id, body, version=version, @@ -747,10 +750,34 @@ class SharesV2Client(shares_client.SharesClient): return self._parse_resp(body) def list_access_rules(self, share_id, version=LATEST_MICROVERSION, - action_name=None): - body = {self._get_access_action_name(version, 'os-access_list'): None} - resp, body = self.post( - "shares/%s/action" % share_id, json.dumps(body), version=version) + metadata=None, action_name=None): + if utils.is_microversion_lt(version, "2.45"): + body = { + self._get_access_action_name(version, 'os-access_list'): None + } + resp, body = self.post( + "shares/%s/action" % share_id, json.dumps(body), + version=version) + self.expected_success(200, resp.status) + else: + return self.list_access_rules_with_new_API( + share_id, metadata=metadata, version=version, + action_name=action_name) + return self._parse_resp(body) + + def list_access_rules_with_new_API(self, share_id, metadata=None, + version=LATEST_MICROVERSION, + action_name=None): + metadata = metadata or {} + query_string = '' + + params = sorted( + [(k, v) for (k, v) in list(metadata.items()) if v]) + if params: + query_string = "&%s" % urlparse.urlencode(params) + + url = 'share-access-rules?share_id=%s' % share_id + query_string + resp, body = self.get(url, version=version) self.expected_success(200, resp.status) return self._parse_resp(body) @@ -767,6 +794,28 @@ class SharesV2Client(shares_client.SharesClient): self.expected_success(202, resp.status) return body + def get_access(self, access_id, version=LATEST_MICROVERSION): + resp, body = self.get("share-access-rules/%s" % access_id, + version=version) + self.expected_success(200, resp.status) + return self._parse_resp(body) + + def update_access_metadata(self, access_id, metadata, + version=LATEST_MICROVERSION): + url = 'share-access-rules/%s/metadata' % access_id + body = {"metadata": metadata} + resp, body = self.put(url, json.dumps(body), version=version) + self.expected_success(200, resp.status) + return self._parse_resp(body) + + def delete_access_metadata(self, access_id, key, + version=LATEST_MICROVERSION): + url = "share-access-rules/%s/metadata/%s" % (access_id, key) + resp, body = self.delete(url, version=version) + self.expected_success(200, resp.status) + return body + + ############### def list_availability_zones(self, url='availability-zones', diff --git a/manila_tempest_tests/tests/api/test_access_rules_metadata.py b/manila_tempest_tests/tests/api/test_access_rules_metadata.py new file mode 100644 index 00000000..d3f25d74 --- /dev/null +++ b/manila_tempest_tests/tests/api/test_access_rules_metadata.py @@ -0,0 +1,133 @@ +# Copyright 2018 Huawei Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import ddt +from tempest import config +from testtools import testcase as tc + +from manila_tempest_tests.common import constants +from manila_tempest_tests.tests.api import base +from manila_tempest_tests import utils + +CONF = config.CONF + + +@base.skip_if_microversion_lt( + constants.MIN_SHARE_ACCESS_METADATA_MICROVERSION) +@ddt.ddt +class AccessRulesMetadataTest(base.BaseSharesTest): + + @classmethod + def resource_setup(cls): + super(AccessRulesMetadataTest, cls).resource_setup() + # The share access rule metadata doesn't care about the value of + # access type, access protocol, access_to, so we only get one of + # the value that the driver support. + 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) + if CONF.share.enable_ip_rules_for_protocols: + cls.protocol = CONF.share.enable_ip_rules_for_protocols[0] + cls.access_type = "ip" + elif CONF.share.enable_user_rules_for_protocols: + cls.protocol = CONF.share.enable_user_rules_for_protocols[0] + cls.access_type = "user" + elif CONF.share.enable_cert_rules_for_protocols: + cls.protocol = CONF.share.enable_cert_rules_for_protocols[0] + cls.access_type = "cert" + elif CONF.share.enable_cephx_rules_for_protocols: + cls.protocol = CONF.share.enable_cephx_rules_for_protocols[0] + cls.access_type = "cephx" + cls.shares_v2_client.share_protocol = cls.protocol + int_range = range(20, 50) + cls.access_to = { + # list of unique values is required for ability to create lots + # of access rules for one share using different API microversions. + 'ip': set([utils.rand_ipv6_ip() for i in int_range]), + # following users are fakes and access rules that use it are + # expected to fail, but they are used only for API testing. + 'user': ['foo_user_%d' % i for i in int_range], + 'cert': ['tenant_%d.example.com' % i for i in int_range], + 'cephx': ['eve%d' % i for i in int_range], + } + cls.share = cls.create_share() + cls.md1 = {"key1": "value1", "key2": "value2"} + cls.access = cls.shares_v2_client.create_access_rule( + cls.share["id"], cls.access_type, + cls.access_to[cls.access_type].pop(), 'rw', metadata=cls.md1) + + @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND) + def test_set_get_delete_access_metadata(self): + data = {"key1": "v" * 255, "k" * 255: "value2"} + # set metadata + access = self.shares_v2_client.create_access_rule( + self.share["id"], self.access_type, + self.access_to[self.access_type].pop(), 'rw', metadata=data) + + # read metadata + get_access = self.shares_v2_client.get_access(access["id"]) + + # verify metadata + self.assertEqual(data, get_access['metadata']) + + # delete metadata + for key in data.keys(): + self.shares_v2_client.delete_access_metadata(access["id"], key) + + # verify deletion of metadata + access_without_md = self.shares_v2_client.get_access(access["id"]) + self.assertEqual({}, access_without_md['metadata']) + self.shares_v2_client.delete_access_rule(self.share["id"], + access["id"]) + self.shares_v2_client.wait_for_resource_deletion( + rule_id=access["id"], share_id=self.share["id"]) + + @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND) + def test_update_metadata_by_key(self): + md2 = {"key7": "value7", "key2": "value6_new"} + + # update metadata + self.shares_v2_client.update_access_metadata( + access_id=self.access['id'], metadata=md2) + # get metadata + get_access = self.shares_v2_client.get_access(self.access['id']) + + # verify metadata + self.md1.update(md2) + self.assertEqual(self.md1, get_access['metadata']) + + @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND) + def test_list_access_filter_by_metadata(self): + data = {"key3": "v3", "key4": "value4"} + # set metadata + access = self.shares_v2_client.create_access_rule( + self.share["id"], self.access_type, + self.access_to[self.access_type].pop(), 'rw', metadata=data) + + # list metadata with metadata filter + list_access = self.shares_v2_client.list_access_rules( + share_id=self.share["id"], metadata={'metadata': data}) + + # verify metadata + self.assertEqual(1, len(list_access)) + self.assertEqual(access['metadata'], list_access[0]['metadata']) + self.assertEqual(access['id'], list_access[0]['id']) diff --git a/manila_tempest_tests/tests/api/test_access_rules_metadata_negative.py b/manila_tempest_tests/tests/api/test_access_rules_metadata_negative.py new file mode 100644 index 00000000..75790ea6 --- /dev/null +++ b/manila_tempest_tests/tests/api/test_access_rules_metadata_negative.py @@ -0,0 +1,80 @@ +# Copyright 2018 Huawei Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import ddt +from tempest import config +from tempest.lib import exceptions as lib_exc +from testtools import testcase as tc + +from manila_tempest_tests.common import constants +from manila_tempest_tests.tests.api import base +from manila_tempest_tests import utils + +CONF = config.CONF + + +@base.skip_if_microversion_lt(constants.MIN_SHARE_GROUP_MICROVERSION) +@ddt.ddt +class AccessesMetadataNegativeTest(base.BaseSharesTest): + + @classmethod + def resource_setup(cls): + super(AccessesMetadataNegativeTest, cls).resource_setup() + 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) + if CONF.share.enable_ip_rules_for_protocols: + cls.protocol = CONF.share.enable_ip_rules_for_protocols[0] + cls.access_type = "ip" + cls.access_to = utils.rand_ip() + elif CONF.share.enable_user_rules_for_protocols: + cls.protocol = CONF.share.enable_user_rules_for_protocols[0] + cls.access_type = "user" + cls.access_to = CONF.share.username_for_user_rules + elif CONF.share.enable_cert_rules_for_protocols: + cls.protocol = CONF.share.enable_cert_rules_for_protocols[0] + cls.access_type = "cert" + cls.access_to = "client3.com" + elif CONF.share.enable_cephx_rules_for_protocols: + cls.protocol = CONF.share.enable_cephx_rules_for_protocols[0] + cls.access_type = "cephx" + cls.access_to = "eve" + cls.shares_v2_client.share_protocol = cls.protocol + cls.share = cls.create_share() + cls.access = cls.shares_v2_client.create_access_rule( + cls.share["id"], cls.access_type, cls.access_to, + 'rw', metadata={u"key1": u"value1"}) + + @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND) + @ddt.data({'data': {"": "value"}}, {'data': {"k" * 256: "value"}}, + {'data': {"key": "x" * 1024}}) + @ddt.unpack + def test_try_upd_access_metadata_error(self, data): + self.assertRaises(lib_exc.BadRequest, + self.shares_v2_client.update_access_metadata, + self.access["id"], data) + + @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND) + def test_try_delete_unexisting_access_metadata(self): + self.assertRaises(lib_exc.NotFound, + self.shares_v2_client.delete_access_metadata, + self.access["id"], "wrong_key") diff --git a/manila_tempest_tests/tests/api/test_rules.py b/manila_tempest_tests/tests/api/test_rules.py index 0b44a5c8..bb4bfd09 100644 --- a/manila_tempest_tests/tests/api/test_rules.py +++ b/manila_tempest_tests/tests/api/test_rules.py @@ -488,7 +488,8 @@ class ShareRulesTest(base.BaseSharesTest): cls.share = cls.create_share() @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND) - @ddt.data(*set(['1.0', '2.9', '2.27', '2.28', LATEST_MICROVERSION])) + @ddt.data(*set( + ['1.0', '2.9', '2.27', '2.28', '2.45', LATEST_MICROVERSION])) def test_list_access_rules(self, version): if (utils.is_microversion_lt(version, '2.13') and CONF.share.enable_cephx_rules_for_protocols): @@ -496,6 +497,9 @@ class ShareRulesTest(base.BaseSharesTest): "version >= 2.13." % version) raise self.skipException(msg) + metadata = None + if utils.is_microversion_ge(version, '2.45'): + metadata = {'key1': 'v1', 'key2': 'v2'} # create rule if utils.is_microversion_eq(version, '1.0'): rule = self.shares_client.create_access_rule( @@ -503,7 +507,7 @@ class ShareRulesTest(base.BaseSharesTest): else: rule = self.shares_v2_client.create_access_rule( self.share["id"], self.access_type, self.access_to, - version=version) + metadata=metadata, version=version) # verify added rule keys since 2.33 when create rule if utils.is_microversion_ge(version, '2.33'): @@ -543,6 +547,8 @@ class ShareRulesTest(base.BaseSharesTest): 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'): @@ -625,7 +631,11 @@ class ShareRulesTest(base.BaseSharesTest): self.assertRaises(lib_exc.NotFound, self.shares_client.list_access_rules, share['id']) - else: + 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)