Merge "Clarify help text of network options"
diff --git a/README.rst b/README.rst
index 0a2d95b..f84f23f 100644
--- a/README.rst
+++ b/README.rst
@@ -1,6 +1,6 @@
-===============================
-openstack
-===============================
+=====================
+manila-tempest-plugin
+=====================
 
 Tempest plugin manila-tempest-plugin
 
@@ -9,7 +9,7 @@
 * Free software: Apache license
 * Documentation: https://docs.openstack.org/manila/latest/
 * Release notes: https://docs.openstack.org/releasenotes/manila/
-* Source: https://git.openstack.org/cgit/openstack/manila-tempest-plugin
+* Source: https://opendev.org/openstack/manila-tempest-plugin
 * Bugs: https://bugs.launchpad.net/manila-tempest-plugin
 
 Features
diff --git a/devstack/README.rst b/devstack/README.rst
index a78b7f7..efacb7e 100644
--- a/devstack/README.rst
+++ b/devstack/README.rst
@@ -8,14 +8,14 @@
 
 1. Download DevStack::
 
-    git clone https://git.openstack.org/openstack-dev/devstack.git
+    git clone https://opendev.org/openstack/devstack.git
     cd devstack
 
 2. Add this repo as an external repository::
 
      > cat local.conf
      [[local|localrc]]
-     enable_plugin manila-tempest-plugin https://git.openstack.org/openstack/manila-tempest-plugin
+     enable_plugin manila-tempest-plugin https://opendev.org/openstack/manila-tempest-plugin
 
 3. run ``stack.sh``
 
diff --git a/manila_tempest_tests/tests/scenario/manager_share.py b/manila_tempest_tests/tests/scenario/manager_share.py
index 5a38079..6eb6840 100644
--- a/manila_tempest_tests/tests/scenario/manager_share.py
+++ b/manila_tempest_tests/tests/scenario/manager_share.py
@@ -311,11 +311,7 @@
                                              client=None):
         share = share or self.share
         client = client or self.shares_v2_client
-        if self.protocol.lower() == 'cifs':
-            self.allow_access_ip(
-                share['id'], instance=instance, cleanup=False,
-                snapshot=snapshot, access_level=access_level, client=client)
-        elif not CONF.share.multitenancy_enabled:
+        if not CONF.share.multitenancy_enabled:
             if self.ipv6_enabled:
                 server_ip = self._get_ipv6_server_ip(instance)
             else:
@@ -326,8 +322,7 @@
                 share['id'], ip=server_ip,
                 instance=instance, cleanup=False, snapshot=snapshot,
                 access_level=access_level, client=client)
-        elif (CONF.share.multitenancy_enabled and
-              self.protocol.lower() == 'nfs'):
+        else:
             return self.allow_access_ip(
                 share['id'], instance=instance, cleanup=False,
                 snapshot=snapshot, access_level=access_level, client=client)
diff --git a/manila_tempest_tests/tests/scenario/test_share_basic_ops.py b/manila_tempest_tests/tests/scenario/test_share_basic_ops.py
index 1e23b82..6b54bdb 100644
--- a/manila_tempest_tests/tests/scenario/test_share_basic_ops.py
+++ b/manila_tempest_tests/tests/scenario/test_share_basic_ops.py
@@ -116,6 +116,11 @@
     @tc.attr(base.TAG_NEGATIVE, base.TAG_BACKEND)
     def test_write_with_ro_access(self):
         '''Test if an instance with ro access can write on the share.'''
+        if self.protocol.upper() == 'CIFS':
+            msg = ("Skipped for CIFS protocol because RO access is not "
+                   "supported for shares by IP.")
+            raise self.skipException(msg)
+
         test_data = "Some test data to write"
 
         instance = self.boot_instance(wait_until="BUILD")
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 0000000..b845ff8
--- /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)
diff --git a/manila_tempest_tests/tests/scenario/test_share_shrink.py b/manila_tempest_tests/tests/scenario/test_share_shrink.py
index 8bbb921..03427af 100644
--- a/manila_tempest_tests/tests/scenario/test_share_shrink.py
+++ b/manila_tempest_tests/tests/scenario/test_share_shrink.py
@@ -71,7 +71,7 @@
         LOG.debug('Step 5 - mount')
         self.mount_share(locations[0], remote_client)
 
-        total_blocks = (1024 * default_share_size) / 64
+        total_blocks = int((1024 * default_share_size) / 64)
         blocks = total_blocks + 4
         LOG.debug('Step 6 - writing {} * 64MB blocks'.format(blocks))
         self.write_data_to_mounted_share_using_dd(remote_client,
diff --git a/tox.ini b/tox.ini
index 8dc2a65..f32dd68 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,7 +5,7 @@
 
 [testenv]
 usedevelop = True
-install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
+install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt} {opts} {packages}
 setenv =
    VIRTUAL_ENV={envdir}
    PYTHONWARNINGS=default::DeprecationWarning