From 134f07af6d87942327234d74336a92385a90be78 Mon Sep 17 00:00:00 2001 From: Maari Tamm Date: Thu, 13 Feb 2020 07:52:08 +0000 Subject: [PATCH] Add functional tests for OSC In this patch we create the base for osc functional testing and add tests for 'openstack share' commands that are currently implemented. Change-Id: I3732229e4a61182a80f6f35ad7050075db84f6ce Partially-implements: bp openstack-client-support --- manilaclient/tests/functional/osc/__init__.py | 0 manilaclient/tests/functional/osc/base.py | 154 ++++++++++++++++++ .../tests/functional/osc/test_shares.py | 69 ++++++++ 3 files changed, 223 insertions(+) create mode 100644 manilaclient/tests/functional/osc/__init__.py create mode 100644 manilaclient/tests/functional/osc/base.py create mode 100644 manilaclient/tests/functional/osc/test_shares.py diff --git a/manilaclient/tests/functional/osc/__init__.py b/manilaclient/tests/functional/osc/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/manilaclient/tests/functional/osc/base.py b/manilaclient/tests/functional/osc/base.py new file mode 100644 index 000000000..fa9287fb5 --- /dev/null +++ b/manilaclient/tests/functional/osc/base.py @@ -0,0 +1,154 @@ +# 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 six +import time + +from tempest.lib.cli import base +from tempest.lib.common.utils import data_utils + +from manilaclient import config + +CONF = config.CONF + + +class OSCClientTestBase(base.ClientTestBase): + """Base class for OSC manila functional tests""" + + def _get_clients(self): + + return base.CLIClient( + username=CONF.admin_username or CONF.username, + password=CONF.admin_password or CONF.password, + tenant_name=CONF.admin_tenant_name or CONF.tenant_name, + uri=CONF.admin_auth_url or CONF.auth_url, + cli_dir=CONF.manila_exec_dir, + insecure=CONF.insecure, + project_domain_name=(CONF.admin_project_domain_name or + CONF.project_domain_name or None), + project_domain_id=(CONF.admin_project_domain_id or + CONF.project_domain_id or None), + user_domain_name=(CONF.admin_user_domain_name or + CONF.user_domain_name or None), + user_domain_id=(CONF.admin_user_domain_id or + CONF.user_domain_id or None) + ) + + def _get_property_from_output(self, output): + """Creates a dictionary from the given output""" + obj = {} + items = self.parser.listing(output) + for item in items: + obj[item['Field']] = six.text_type(item['Value']) + return obj + + def _wait_for_object_status(self, object_name, object_id, status, + timeout=CONF.build_timeout, + interval=CONF.build_interval): + """Waits for a object to reach a given status.""" + + start_time = time.time() + while time.time() - start_time < timeout: + if status == self.openstack( + '%(obj)s show -c status -f value %(id)s' + % {'obj': object_name, + 'id': object_id}).rstrip(): + break + time.sleep(interval) + else: + self.fail("%s %s did not reach status %s after %d seconds." + % (object_name, object_id, status, timeout)) + + def check_object_deleted(self, object_name, object_id, + timeout=CONF.build_timeout, + interval=CONF.build_interval): + """Check that object deleted successfully""" + + cmd = '%s list -c ID -f value' % object_name + start_time = time.time() + while time.time() - start_time < timeout: + if object_id not in self.openstack(cmd): + break + time.sleep(interval) + else: + self.fail("%s %s not deleted after %d seconds." + % (object_name, object_id, timeout)) + + def openstack(self, action, flags='', params='', fail_ok=False, + merge_stderr=False): + """Executes openstack command for given action""" + + if '--os-share-api-version' not in flags: + flags = ( + flags + '--os-share-api-version %s' + % CONF.max_api_microversion) + + return self.clients.openstack(action, flags=flags, params=params, + fail_ok=fail_ok, + merge_stderr=merge_stderr) + + def listing_result(self, object_name, command): + """Returns output for the given command as list of dictionaries""" + + output = self.openstack(object_name, params=command) + result = self.parser.listing(output) + return result + + def dict_result(self, object_name, command): + """Returns output for the given command as dictionary""" + + output = self.openstack(object_name, params=command) + result_dict = self._get_property_from_output(output) + return result_dict + + def create_share(self, share_protocol=None, size=None, name=None, + snapshot_id=None, properties=None, share_network=None, + description=None, public=False, share_type=None, + availability_zone=None, share_group=None, + add_cleanup=True): + + name = name or data_utils.rand_name('autotest_share_name') + # share_type = dhss_false until we have implemented + # share network commands for osc + share_type = share_type or 'dhss_false' + + cmd = ('create ' + '%(protocol)s %(size)s %(name)s %(desc)s %(public)s %(stype)s' + % {'protocol': share_protocol or 'NFS', + 'size': size or '1', + 'name': '--name %s' % name, + 'desc': '--description %s' % description, + 'public': '--public %s' % public, + 'stype': '--share-type %s' % share_type}) + + if snapshot_id: + cmd = cmd + ' --snapshot-id %s' % snapshot_id + if properties: + for key, value in properties.items(): + cmd = (cmd + ' --property %(key)s=%(value)s' + % {'key': key, 'value': value}) + if share_network: + cmd = cmd + ' --share-network %s' % share_network + if availability_zone: + cmd = cmd + ' --availability-zone %s' % availability_zone + if share_group: + cmd = cmd + ' --share-group %s' % share_group + + share_object = self.dict_result('share', cmd) + self._wait_for_object_status( + 'share', share_object['id'], 'available') + + if add_cleanup: + self.addCleanup( + self.openstack, 'share delete %s' % share_object['id'] + ) + return share_object diff --git a/manilaclient/tests/functional/osc/test_shares.py b/manilaclient/tests/functional/osc/test_shares.py new file mode 100644 index 000000000..78f25e5d6 --- /dev/null +++ b/manilaclient/tests/functional/osc/test_shares.py @@ -0,0 +1,69 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +from manilaclient.tests.functional.osc import base + + +class SharesCLITest(base.OSCClientTestBase): + + def test_openstack_share_create(self): + share_name = 'test_create_share' + share = self.create_share(name=share_name) + + self.assertEqual(share['share_proto'], 'NFS') + self.assertEqual(share['size'], '1') + self.assertEqual(share['name'], share_name) + + shares_list = self.listing_result('share', 'list') + self.assertIn(share['id'], [item['ID'] for item in shares_list]) + + def test_openstack_share_list(self): + share = self.create_share() + shares_list = self.listing_result('share', 'list') + self.assertTableStruct(shares_list, [ + 'ID', + 'Name', + 'Size', + 'Share Proto', + 'Status', + 'Is Public', + 'Share Type Name', + 'Host', + 'Availability Zone' + ]) + self.assertIn(share['id'], [item['ID'] for item in shares_list]) + + def test_openstack_share_show(self): + share = self.create_share() + + result = self.dict_result('share', 'show %s' % share['id']) + self.assertEqual(share['id'], result['id']) + + listing_result = self.listing_result('share', 'show %s' % share['id']) + self.assertTableStruct(listing_result, [ + 'Field', + 'Value' + ]) + + def test_openstack_share_delete(self): + share = self.create_share(add_cleanup=False) + shares_list = self.listing_result('share', 'list') + + self.assertIn(share['id'], [item['ID'] for item in shares_list]) + + self.openstack('share delete %s' % share['id']) + self.check_object_deleted('share', share['id']) + shares_list_after_delete = self.listing_result('share', 'list') + + self.assertNotIn( + share['id'], [item['ID'] for item in shares_list_after_delete])