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
diff --git a/manila_tempest_tests/common/constants.py b/manila_tempest_tests/common/constants.py
index 25cf4ee..c56712e 100644
--- a/manila_tempest_tests/common/constants.py
+++ b/manila_tempest_tests/common/constants.py
@@ -83,3 +83,5 @@
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 e737cb4..0517c60 100644
--- a/manila_tempest_tests/config.py
+++ b/manila_tempest_tests/config.py
@@ -30,7 +30,7 @@
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 3f1c8a1..522d467 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 @@
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 @@
"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 @@
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 @@
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 0000000..d3f25d7
--- /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 0000000..75790ea
--- /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 0b44a5c..bb4bfd0 100644
--- a/manila_tempest_tests/tests/api/test_rules.py
+++ b/manila_tempest_tests/tests/api/test_rules.py
@@ -488,7 +488,8 @@
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 @@
"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 @@
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 @@
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 @@
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)