From 2cb85a6edd76ae94b428cc504c9e25f419618c9a Mon Sep 17 00:00:00 2001 From: Nir Gilboa Date: Sat, 6 Jan 2018 10:49:09 -0800 Subject: [PATCH] Scenario test: Create/extend share and write data Implements #4 from: http://specs.openstack.org/openstack/manila-specs/specs/release_independent/scenario-tests.html Change-Id: I4998bcac7a1d902c17d2add00cd42560bf6bbf75 Depends-On: https://review.openstack.org/#/c/545695/ --- .../tests/scenario/test_share_extend.py | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 manila_tempest_tests/tests/scenario/test_share_extend.py diff --git a/manila_tempest_tests/tests/scenario/test_share_extend.py b/manila_tempest_tests/tests/scenario/test_share_extend.py new file mode 100644 index 00000000..b845ff89 --- /dev/null +++ b/manila_tempest_tests/tests/scenario/test_share_extend.py @@ -0,0 +1,177 @@ +# 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 +import six + +from oslo_log import log as logging +from oslo_utils import units +from tempest import config +from tempest.lib import exceptions +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.tests.scenario import manager_share as manager + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +@ddt.ddt +class ShareExtendBase(manager.ShareScenarioTest): + + """This test case uses the following flow: + + * Launch an instance + * Create share (1GB) + * Configure RW access to the share + * Perform ssh to instance + * Mount share + * Write data in share + * Extend share (2GB) + * Write more data in share + * Unmount share + * Delete share + * Terminate the instance + """ + + @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND) + def test_create_extend_and_write(self): + default_share_size = CONF.share.share_size + + LOG.debug('Step 1 - create instance') + instance = self.boot_instance(wait_until="BUILD") + + LOG.debug('Step 2 - create share of size {} Gb' + .format(default_share_size)) + share = self.create_share(size=default_share_size) + + LOG.debug('Step 3 - wait for active instance') + instance = self.wait_for_active_instance(instance["id"]) + remote_client = self.init_remote_client(instance) + + LOG.debug('Step 4 - grant access') + self.provide_access_to_auxiliary_instance(instance, share=share) + + locations = self.get_share_export_locations(share) + + LOG.debug('Step 5 - mount') + self.mount_share(locations[0], remote_client) + + total_blocks = (units.Ki * default_share_size) / 64 + three_quarter_blocks = (total_blocks / 4) * 3 + LOG.debug('Step 6 - writing {} * 64MB blocks' + .format(three_quarter_blocks)) + self.write_data_to_mounted_share_using_dd(remote_client, + '/mnt/t1', '64M', + three_quarter_blocks, + '/dev/urandom') + ls_result = remote_client.exec_command("sudo ls -lAh /mnt/") + LOG.debug(ls_result) + + over_one_quarter_blocks = total_blocks - three_quarter_blocks + 5 + LOG.debug('Step 6b - Write more data, should fail') + self.assertRaises( + exceptions.SSHExecCommandFailed, + self.write_data_to_mounted_share_using_dd, + remote_client, '/mnt/t2', '64M', over_one_quarter_blocks, + '/dev/urandom') + ls_result = remote_client.exec_command("sudo ls -lAh /mnt/") + LOG.debug(ls_result) + + LOG.debug('Step 7 - extend and wait') + extended_share_size = default_share_size + 1 + self.shares_v2_client.extend_share(share["id"], + new_size=extended_share_size) + self.shares_v2_client.wait_for_share_status(share["id"], + constants.STATUS_AVAILABLE) + share = self.shares_v2_client.get_share(share["id"]) + self.assertEqual(extended_share_size, int(share["size"])) + + LOG.debug('Step 8 - writing more data, should succeed') + self.write_data_with_remount(locations[0], remote_client, '/mnt/t3', + '64M', over_one_quarter_blocks) + ls_result = remote_client.exec_command("sudo ls -lAh /mnt/") + LOG.debug(ls_result) + + LOG.debug('Step 9 - unmount') + self.unmount_share(remote_client) + + def write_data_with_remount(self, mount_location, + remote_client, + output_file, + block_size, + block_count): + """Writes data to mounted share using dd command + + Tries remounting once if encountering a stale file handle + + :param mount_location: Mount point for remounting if needed + :param remote_client: An SSH client connection to the Nova instance + :param block_size: The size of an individual block in bytes + :param block_count: The number of blocks to write + :param output_file: Path to the file to be written + """ + + try: + self.write_data_to_mounted_share_using_dd(remote_client, + output_file, + block_size, + block_count, + '/dev/urandom') + except exceptions.SSHExecCommandFailed as e: + if 'stale file handle' in six.text_type(e).lower(): + LOG.warning("Client was disconnected during extend process") + self.unmount_share(remote_client) + self.mount_share(mount_location, remote_client) + self.write_data_to_mounted_share_using_dd(remote_client, + output_file, + block_size, + block_count, + '/dev/urandom') + else: + raise + + +class TestShareExtendNFS(ShareExtendBase): + protocol = "nfs" + + def mount_share(self, location, remote_client, target_dir=None): + target_dir = target_dir or "/mnt" + remote_client.exec_command( + "sudo mount -vt nfs \"%s\" %s" % (location, target_dir) + ) + + +class TestShareExtendCIFS(ShareExtendBase): + protocol = "cifs" + + def mount_share(self, location, remote_client, target_dir=None): + location = location.replace("\\", "/") + target_dir = target_dir or "/mnt" + remote_client.exec_command( + "sudo mount.cifs \"%s\" %s -o guest" % (location, target_dir) + ) + + +# NOTE(u_glide): this function is required to exclude ShareExtendBase +# from executed test cases. +# See: https://docs.python.org/2/library/unittest.html#load-tests-protocol +# for details. +def load_tests(loader, tests, _): + result = [] + for test_case in tests: + if type(test_case._tests[0]) is ShareExtendBase: + continue + result.append(test_case) + return loader.suiteClass(result)