Merge "share-size not set to 1 with 'manage_error' state"
diff --git a/manila_tempest_tests/common/constants.py b/manila_tempest_tests/common/constants.py
index bef35a5..abef181 100644
--- a/manila_tempest_tests/common/constants.py
+++ b/manila_tempest_tests/common/constants.py
@@ -34,3 +34,19 @@
RULE_STATE_ACTIVE = 'active'
RULE_STATE_OUT_OF_SYNC = 'out_of_sync'
RULE_STATE_ERROR = 'error'
+
+TASK_STATE_MIGRATION_STARTING = 'migration_starting'
+TASK_STATE_MIGRATION_IN_PROGRESS = 'migration_in_progress'
+TASK_STATE_MIGRATION_COMPLETING = 'migration_completing'
+TASK_STATE_MIGRATION_SUCCESS = 'migration_success'
+TASK_STATE_MIGRATION_ERROR = 'migration_error'
+TASK_STATE_MIGRATION_CANCELLED = 'migration_cancelled'
+TASK_STATE_MIGRATION_DRIVER_STARTING = 'migration_driver_starting'
+TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS = 'migration_driver_in_progress'
+TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE = 'migration_driver_phase1_done'
+TASK_STATE_DATA_COPYING_STARTING = 'data_copying_starting'
+TASK_STATE_DATA_COPYING_IN_PROGRESS = 'data_copying_in_progress'
+TASK_STATE_DATA_COPYING_COMPLETING = 'data_copying_completing'
+TASK_STATE_DATA_COPYING_COMPLETED = 'data_copying_completed'
+TASK_STATE_DATA_COPYING_CANCELLED = 'data_copying_cancelled'
+TASK_STATE_DATA_COPYING_ERROR = 'data_copying_error'
diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py
index 4b394c6..f0c8dc7 100644
--- a/manila_tempest_tests/config.py
+++ b/manila_tempest_tests/config.py
@@ -34,7 +34,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.18",
+ default="2.22",
help="The maximum api microversion is configured to be the "
"value of the latest microversion supported by Manila."),
cfg.StrOpt("region",
@@ -168,9 +168,19 @@
help="Defines whether to run replication tests or not. "
"Enable this feature if the driver is configured "
"for replication."),
- cfg.BoolOpt("run_migration_tests",
+ cfg.BoolOpt("run_multiple_share_replicas_tests",
+ default=True,
+ help="Defines whether to run multiple replicas creation test "
+ "or not. Enable this if the driver can create more than "
+ "one replica for a share."),
+ cfg.BoolOpt("run_host_assisted_migration_tests",
+ deprecated_name="run_migration_tests",
default=False,
- help="Enable or disable migration tests."),
+ help="Enable or disable host-assisted migration tests."),
+ cfg.BoolOpt("run_driver_assisted_migration_tests",
+ deprecated_name="run_migration_tests",
+ default=False,
+ help="Enable or disable driver-assisted migration tests."),
cfg.BoolOpt("run_manage_unmanage_tests",
default=False,
help="Defines whether to run manage/unmanage tests or not. "
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 3995161..0ccc18b 100755
--- a/manila_tempest_tests/services/share/v2/json/shares_client.py
+++ b/manila_tempest_tests/services/share/v2/json/shares_client.py
@@ -14,6 +14,7 @@
# under the License.
import json
+import six
import time
from six.moves.urllib import parse as urlparse
@@ -552,6 +553,68 @@
###############
+ def get_snapshot_instance(self, instance_id, version=LATEST_MICROVERSION):
+ resp, body = self.get("snapshot-instances/%s" % instance_id,
+ version=version)
+ self.expected_success(200, resp.status)
+ return self._parse_resp(body)
+
+ def list_snapshot_instances(self, detail=False, snapshot_id=None,
+ version=LATEST_MICROVERSION):
+ """Get list of share snapshot instances."""
+ uri = "snapshot-instances%s" % ('/detail' if detail else '')
+ if snapshot_id is not None:
+ uri += '?snapshot_id=%s' % snapshot_id
+ resp, body = self.get(uri, version=version)
+ self.expected_success(200, resp.status)
+ return self._parse_resp(body)
+
+ def reset_snapshot_instance_status(self, instance_id,
+ status=constants.STATUS_AVAILABLE,
+ version=LATEST_MICROVERSION):
+ """Reset the status."""
+ uri = 'snapshot-instances/%s/action' % instance_id
+ post_body = {
+ 'reset_status': {
+ 'status': status
+ }
+ }
+ body = json.dumps(post_body)
+ resp, body = self.post(uri, body, extra_headers=True, version=version)
+ self.expected_success(202, resp.status)
+ return self._parse_resp(body)
+
+ def wait_for_snapshot_instance_status(self, instance_id, expected_status):
+ """Waits for a snapshot instance status to reach a given status."""
+ body = self.get_snapshot_instance(instance_id)
+ instance_status = body['status']
+ start = int(time.time())
+
+ while instance_status != expected_status:
+ time.sleep(self.build_interval)
+ body = self.get_snapshot_instance(instance_id)
+ instance_status = body['status']
+ if instance_status == expected_status:
+ return
+ if 'error' in instance_status:
+ raise share_exceptions.SnapshotInstanceBuildErrorException(
+ id=instance_id)
+
+ if int(time.time()) - start >= self.build_timeout:
+ message = ('The status of snapshot instance %(id)s failed to '
+ 'reach %(expected_status)s status within the '
+ 'required time (%(time)ss). Current '
+ 'status: %(current_status)s.' %
+ {
+ 'expected_status': expected_status,
+ 'time': self.build_timeout,
+ 'id': instance_id,
+ 'current_status': instance_status,
+ })
+ raise exceptions.TimeoutException(message)
+
+###############
+
def _get_access_action_name(self, version, action):
if utils.is_microversion_gt(version, "2.6"):
return action.split('os-')[-1]
@@ -626,8 +689,11 @@
###############
- def list_share_types(self, params=None, version=LATEST_MICROVERSION):
+ def list_share_types(self, params=None, default=False,
+ version=LATEST_MICROVERSION):
uri = 'types'
+ if default:
+ uri += '/default'
if params is not None:
uri += '?%s' % urlparse.urlencode(params)
resp, body = self.get(uri, version=version)
@@ -951,22 +1017,25 @@
###############
- def migrate_share(self, share_id, host, notify,
- version=LATEST_MICROVERSION, action_name=None):
- if action_name is None:
- if utils.is_microversion_lt(version, "2.7"):
- action_name = 'os-migrate_share'
- elif utils.is_microversion_lt(version, "2.15"):
- action_name = 'migrate_share'
- else:
- action_name = 'migration_start'
- post_body = {
- action_name: {
+ def migrate_share(self, share_id, host,
+ force_host_assisted_migration=False,
+ new_share_network_id=None, writable=False,
+ preserve_metadata=False, nondisruptive=False,
+ new_share_type_id=None, version=LATEST_MICROVERSION):
+
+ body = {
+ 'migration_start': {
'host': host,
- 'notify': notify,
+ 'force_host_assisted_migration': force_host_assisted_migration,
+ 'new_share_network_id': new_share_network_id,
+ 'new_share_type_id': new_share_type_id,
+ 'writable': writable,
+ 'preserve_metadata': preserve_metadata,
+ 'nondisruptive': nondisruptive,
}
}
- body = json.dumps(post_body)
+
+ body = json.dumps(body)
return self.post('shares/%s/action' % share_id, body,
headers=EXPERIMENTAL, extra_headers=True,
version=version)
@@ -997,9 +1066,10 @@
action_name: None,
}
body = json.dumps(post_body)
- return self.post('shares/%s/action' % share_id, body,
- headers=EXPERIMENTAL, extra_headers=True,
- version=version)
+ result = self.post('shares/%s/action' % share_id, body,
+ headers=EXPERIMENTAL, extra_headers=True,
+ version=version)
+ return json.loads(result[1])
def reset_task_state(
self, share_id, task_state, version=LATEST_MICROVERSION,
@@ -1014,22 +1084,25 @@
headers=EXPERIMENTAL, extra_headers=True,
version=version)
- def wait_for_migration_status(self, share_id, dest_host, status,
+ def wait_for_migration_status(self, share_id, dest_host, status_to_wait,
version=LATEST_MICROVERSION):
"""Waits for a share to migrate to a certain host."""
+ statuses = ((status_to_wait,)
+ if not isinstance(status_to_wait, (tuple, list, set))
+ else status_to_wait)
share = self.get_share(share_id, version=version)
migration_timeout = CONF.share.migration_timeout
start = int(time.time())
- while share['task_state'] != status:
+ while share['task_state'] not in statuses:
time.sleep(self.build_interval)
share = self.get_share(share_id, version=version)
- if share['task_state'] == status:
- return share
+ if share['task_state'] in statuses:
+ break
elif share['task_state'] == 'migration_error':
raise share_exceptions.ShareMigrationException(
share_id=share['id'], src=share['host'], dest=dest_host)
elif int(time.time()) - start >= migration_timeout:
- message = ('Share %(share_id)s failed to reach status '
+ message = ('Share %(share_id)s failed to reach a status in'
'%(status)s when migrating from host %(src)s to '
'host %(dest)s within the required time '
'%(timeout)s.' % {
@@ -1037,9 +1110,10 @@
'dest': dest_host,
'share_id': share['id'],
'timeout': self.build_timeout,
- 'status': status,
+ 'status': six.text_type(statuses),
})
raise exceptions.TimeoutException(message)
+ return share
################
diff --git a/manila_tempest_tests/share_exceptions.py b/manila_tempest_tests/share_exceptions.py
index 3a11531..9b84d02 100644
--- a/manila_tempest_tests/share_exceptions.py
+++ b/manila_tempest_tests/share_exceptions.py
@@ -37,6 +37,11 @@
message = "Snapshot %(snapshot_id)s failed to build and is in ERROR status"
+class SnapshotInstanceBuildErrorException(exceptions.TempestException):
+ message = ("Snapshot instance %(id)s failed to build and is in "
+ "ERROR status.")
+
+
class CGSnapshotBuildErrorException(exceptions.TempestException):
message = ("CGSnapshot %(cgsnapshot_id)s failed to build and is in ERROR "
"status")
diff --git a/manila_tempest_tests/tests/api/admin/test_admin_actions.py b/manila_tempest_tests/tests/api/admin/test_admin_actions.py
index 40b2460..ac862d4 100644
--- a/manila_tempest_tests/tests/api/admin/test_admin_actions.py
+++ b/manila_tempest_tests/tests/api/admin/test_admin_actions.py
@@ -30,7 +30,7 @@
super(AdminActionsTest, cls).resource_setup()
cls.states = ["error", "available"]
cls.task_states = ["migration_starting", "data_copying_in_progress",
- "migration_success"]
+ "migration_success", None]
cls.bad_status = "error_deleting"
cls.sh = cls.create_share()
cls.sh_instance = (
@@ -120,7 +120,7 @@
self.shares_v2_client.wait_for_resource_deletion(snapshot_id=sn["id"])
@test.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
- @base.skip_if_microversion_lt("2.15")
+ @base.skip_if_microversion_lt("2.22")
def test_reset_share_task_state(self):
for task_state in self.task_states:
self.shares_v2_client.reset_task_state(self.sh["id"], task_state)
diff --git a/manila_tempest_tests/tests/api/admin/test_admin_actions_negative.py b/manila_tempest_tests/tests/api/admin/test_admin_actions_negative.py
index 67444de..735c65d 100644
--- a/manila_tempest_tests/tests/api/admin/test_admin_actions_negative.py
+++ b/manila_tempest_tests/tests/api/admin/test_admin_actions_negative.py
@@ -13,6 +13,7 @@
# 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 tempest import test
@@ -124,20 +125,14 @@
self.sh['id'])
@test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
- @base.skip_if_microversion_lt("2.15")
- def test_reset_task_state_empty(self):
- self.assertRaises(
- lib_exc.BadRequest, self.admin_client.reset_task_state,
- self.sh['id'], None)
-
- @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
- @base.skip_if_microversion_lt("2.15")
+ @base.skip_if_microversion_lt("2.22")
def test_reset_task_state_invalid_state(self):
self.assertRaises(
lib_exc.BadRequest, self.admin_client.reset_task_state,
self.sh['id'], 'fake_state')
+@ddt.ddt
class AdminActionsAPIOnlyNegativeTest(base.BaseSharesMixedTest):
@classmethod
@@ -153,7 +148,7 @@
self.member_client.list_share_instances)
@test.attr(type=[base.TAG_NEGATIVE, base.TAG_API])
- @base.skip_if_microversion_lt("2.15")
+ @base.skip_if_microversion_lt("2.22")
def test_reset_task_state_share_not_found(self):
self.assertRaises(
lib_exc.NotFound, self.admin_client.reset_task_state,
@@ -196,3 +191,20 @@
def test_reset_nonexistent_snapshot_state(self):
self.assertRaises(lib_exc.NotFound, self.admin_client.reset_state,
"fake", s_type="snapshots")
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API])
+ @ddt.data('migrate_share', 'migration_complete', 'reset_task_state',
+ 'migration_get_progress', 'migration_cancel')
+ def test_migration_API_invalid_microversion(self, method_name):
+ if method_name == 'migrate_share':
+ self.assertRaises(
+ lib_exc.NotFound, getattr(self.shares_v2_client, method_name),
+ 'fake_share', 'fake_host', version='2.21')
+ elif method_name == 'reset_task_state':
+ self.assertRaises(
+ lib_exc.NotFound, getattr(self.shares_v2_client, method_name),
+ 'fake_share', 'fake_task_state', version='2.21')
+ else:
+ self.assertRaises(
+ lib_exc.NotFound, getattr(self.shares_v2_client, method_name),
+ 'fake_share', version='2.21')
diff --git a/manila_tempest_tests/tests/api/admin/test_export_locations.py b/manila_tempest_tests/tests/api/admin/test_export_locations.py
index d985d90..97db819 100644
--- a/manila_tempest_tests/tests/api/admin/test_export_locations.py
+++ b/manila_tempest_tests/tests/api/admin/test_export_locations.py
@@ -80,8 +80,8 @@
# Check the format of ever-present summary keys
self.assertTrue(uuidutils.is_uuid_like(export_location['id']))
- self.assertTrue(isinstance(export_location['path'],
- six.string_types))
+ self.assertIsInstance(export_location['path'],
+ six.string_types)
if utils.is_microversion_ge(version, '2.14'):
self.assertIn(export_location['preferred'], (True, False))
diff --git a/manila_tempest_tests/tests/api/admin/test_migration.py b/manila_tempest_tests/tests/api/admin/test_migration.py
index a5ceaa4..0f55598 100644
--- a/manila_tempest_tests/tests/api/admin/test_migration.py
+++ b/manila_tempest_tests/tests/api/admin/test_migration.py
@@ -13,19 +13,41 @@
# License for the specific language governing permissions and limitations
# under the License.
+import six
+
+import ddt
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest import test
+from manila_tempest_tests.common import constants
from manila_tempest_tests.tests.api import base
from manila_tempest_tests import utils
CONF = config.CONF
+@ddt.ddt
class MigrationNFSTest(base.BaseSharesAdminTest):
- """Tests Share Migration.
+ """Tests Share Migration for NFS shares.
- Tests migration in multi-backend environment.
+ Tests share migration in multi-backend environment.
+
+ This class covers:
+ 1) Driver-assisted migration: force_host_assisted_migration, nondisruptive,
+ writable and preserve-metadata are False.
+ 2) Host-assisted migration: force_host_assisted_migration is True,
+ nondisruptive, writable and preserve-metadata are False.
+ 3) 2-phase migration of both Host-assisted and Driver-assisted.
+
+ No need to test with writable, preserve-metadata and non-disruptive as
+ True, values are supplied to the driver which decides what to do. Test
+ should be positive, so not being writable, not preserving metadata and
+ being disruptive is less restrictive for drivers, which would abort if they
+ cannot handle them.
+
+ Drivers that implement driver-assisted migration should enable the
+ configuration flag to be tested.
"""
protocol = "nfs"
@@ -34,60 +56,116 @@
def resource_setup(cls):
super(MigrationNFSTest, cls).resource_setup()
if cls.protocol not in CONF.share.enable_protocols:
- message = "%s tests are disabled" % cls.protocol
+ message = "%s tests are disabled." % cls.protocol
raise cls.skipException(message)
- if not CONF.share.run_migration_tests:
- raise cls.skipException("Migration tests disabled. Skipping.")
+ if not (CONF.share.run_host_assisted_migration_tests or
+ CONF.share.run_driver_assisted_migration_tests):
+ raise cls.skipException("Share migration tests are disabled.")
+
+ extra_specs = {
+ 'storage_protocol': CONF.share.capability_storage_protocol,
+ 'driver_handles_share_servers': (
+ CONF.share.multitenancy_enabled),
+ 'snapshot_support': six.text_type(
+ CONF.share.capability_snapshot_support),
+ }
+ cls.new_type = cls.create_share_type(
+ name=data_utils.rand_name('new_share_type_for_migration'),
+ cleanup_in_class=True,
+ extra_specs=extra_specs)
@test.attr(type=[base.TAG_POSITIVE, base.TAG_BACKEND])
- @base.skip_if_microversion_lt("2.5")
- def test_migration_empty_v2_5(self):
+ @base.skip_if_microversion_lt("2.22")
+ @ddt.data(True, False)
+ def test_migration_cancel(self, force_host_assisted):
- share, dest_pool = self._setup_migration()
-
- old_exports = share['export_locations']
-
- share = self.migrate_share(share['id'], dest_pool, version='2.5')
-
- self._validate_migration_successful(dest_pool, share, old_exports,
- version='2.5')
-
- @test.attr(type=[base.TAG_POSITIVE, base.TAG_BACKEND])
- @base.skip_if_microversion_lt("2.15")
- def test_migration_completion_empty_v2_15(self):
+ self._check_migration_enabled(force_host_assisted)
share, dest_pool = self._setup_migration()
old_exports = self.shares_v2_client.list_share_export_locations(
- share['id'], version='2.15')
+ share['id'])
self.assertNotEmpty(old_exports)
old_exports = [x['path'] for x in old_exports
if x['is_admin_only'] is False]
self.assertNotEmpty(old_exports)
+ task_state = (constants.TASK_STATE_DATA_COPYING_COMPLETED
+ if force_host_assisted
+ else constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE)
+
share = self.migrate_share(
- share['id'], dest_pool, version='2.15', notify=False,
- wait_for_status='data_copying_completed')
+ share['id'], dest_pool, wait_for_status=task_state,
+ force_host_assisted_migration=force_host_assisted)
- self._validate_migration_successful(dest_pool, share,
- old_exports, '2.15', notify=False)
+ self._validate_migration_successful(
+ dest_pool, share, task_state, complete=False)
- share = self.migration_complete(share['id'], dest_pool, version='2.15')
+ share = self.migration_cancel(share['id'], dest_pool)
- self._validate_migration_successful(dest_pool, share, old_exports,
- version='2.15')
+ self._validate_migration_successful(
+ dest_pool, share, constants.TASK_STATE_MIGRATION_CANCELLED,
+ complete=False)
+
+ @test.attr(type=[base.TAG_POSITIVE, base.TAG_BACKEND])
+ @base.skip_if_microversion_lt("2.22")
+ @ddt.data(True, False)
+ def test_migration_2phase(self, force_host_assisted):
+
+ self._check_migration_enabled(force_host_assisted)
+
+ share, dest_pool = self._setup_migration()
+
+ old_exports = self.shares_v2_client.list_share_export_locations(
+ share['id'])
+ self.assertNotEmpty(old_exports)
+ old_exports = [x['path'] for x in old_exports
+ if x['is_admin_only'] is False]
+ self.assertNotEmpty(old_exports)
+
+ task_state = (constants.TASK_STATE_DATA_COPYING_COMPLETED
+ if force_host_assisted
+ else constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE)
+
+ old_share_network_id = share['share_network_id']
+ new_share_network_id = self._create_secondary_share_network(
+ old_share_network_id)
+ old_share_type_id = share['share_type']
+ new_share_type_id = self.new_type['share_type']['id']
+
+ share = self.migrate_share(
+ share['id'], dest_pool,
+ force_host_assisted_migration=force_host_assisted,
+ wait_for_status=task_state, new_share_type_id=new_share_type_id,
+ new_share_network_id=new_share_network_id)
+
+ self._validate_migration_successful(
+ dest_pool, share, task_state, complete=False,
+ share_network_id=old_share_network_id,
+ share_type_id=old_share_type_id)
+
+ progress = self.shares_v2_client.migration_get_progress(share['id'])
+
+ self.assertEqual(task_state, progress['task_state'])
+ self.assertEqual(100, progress['total_progress'])
+
+ share = self.migration_complete(share['id'], dest_pool)
+
+ self._validate_migration_successful(
+ dest_pool, share, constants.TASK_STATE_MIGRATION_SUCCESS,
+ complete=True, share_network_id=new_share_network_id,
+ share_type_id=new_share_type_id)
def _setup_migration(self):
- pools = self.shares_client.list_pools()['pools']
+ pools = self.shares_v2_client.list_pools(detail=True)['pools']
if len(pools) < 2:
- raise self.skipException("At least two different pool entries "
- "are needed to run migration tests. "
- "Skipping.")
+ raise self.skipException("At least two different pool entries are "
+ "needed to run share migration tests.")
share = self.create_share(self.protocol)
- share = self.shares_client.get_share(share['id'])
+ share = self.shares_v2_client.get_share(share['id'])
self.shares_v2_client.create_access_rule(
share['id'], access_to="50.50.50.50", access_level="rw")
@@ -101,8 +179,10 @@
self.shares_v2_client.wait_for_share_status(
share['id'], 'active', status_attr='access_rules_status')
- dest_pool = next((x for x in pools if x['name'] != share['host']),
- None)
+ default_type = self.shares_v2_client.list_share_types(
+ default=True)['share_type']
+
+ dest_pool = utils.choose_matching_backend(share, pools, default_type)
self.assertIsNotNone(dest_pool)
self.assertIsNotNone(dest_pool.get('name'))
@@ -111,28 +191,62 @@
return share, dest_pool
- def _validate_migration_successful(self, dest_pool, share,
- old_exports, version, notify=True):
- if utils.is_microversion_lt(version, '2.9'):
- new_exports = share['export_locations']
- self.assertNotEmpty(new_exports)
- else:
- new_exports = self.shares_v2_client.list_share_export_locations(
- share['id'], version='2.9')
- self.assertNotEmpty(new_exports)
- new_exports = [x['path'] for x in new_exports if
- x['is_admin_only'] is False]
- self.assertNotEmpty(new_exports)
+ def _validate_migration_successful(self, dest_pool, share, status_to_wait,
+ version=CONF.share.max_api_microversion,
+ complete=True, share_network_id=None,
+ share_type_id=None):
+
+ statuses = ((status_to_wait,)
+ if not isinstance(status_to_wait, (tuple, list, set))
+ else status_to_wait)
+
+ new_exports = self.shares_v2_client.list_share_export_locations(
+ share['id'], version=version)
+ self.assertNotEmpty(new_exports)
+ new_exports = [x['path'] for x in new_exports if
+ x['is_admin_only'] is False]
+ self.assertNotEmpty(new_exports)
+
+ self.assertIn(share['task_state'], statuses)
+ if share_network_id:
+ self.assertEqual(share_network_id, share['share_network_id'])
+ if share_type_id:
+ self.assertEqual(share_type_id, share['share_type'])
# Share migrated
- if notify:
+ if complete:
self.assertEqual(dest_pool, share['host'])
- for export in old_exports:
- self.assertFalse(export in new_exports)
- self.assertEqual('migration_success', share['task_state'])
+ self.shares_v2_client.delete_share(share['id'])
+ self.shares_v2_client.wait_for_resource_deletion(
+ share_id=share['id'])
# Share not migrated yet
else:
self.assertNotEqual(dest_pool, share['host'])
- for export in old_exports:
- self.assertTrue(export in new_exports)
- self.assertEqual('data_copying_completed', share['task_state'])
+
+ def _check_migration_enabled(self, force_host_assisted):
+
+ if force_host_assisted:
+ if not CONF.share.run_host_assisted_migration_tests:
+ raise self.skipException(
+ "Host-assisted migration tests are disabled.")
+ else:
+ if not CONF.share.run_driver_assisted_migration_tests:
+ raise self.skipException(
+ "Driver-assisted migration tests are disabled.")
+
+ def _create_secondary_share_network(self, old_share_network_id):
+ if (utils.is_microversion_ge(
+ CONF.share.max_api_microversion, "2.22") and
+ CONF.share.multitenancy_enabled):
+
+ old_share_network = self.shares_v2_client.get_share_network(
+ old_share_network_id)
+
+ new_share_network = self.create_share_network(
+ cleanup_in_class=True,
+ neutron_net_id=old_share_network['neutron_net_id'],
+ neutron_subnet_id=old_share_network['neutron_subnet_id'])
+
+ return new_share_network['id']
+ else:
+ return None
diff --git a/manila_tempest_tests/tests/api/admin/test_migration_negative.py b/manila_tempest_tests/tests/api/admin/test_migration_negative.py
index 127be01..5d7a578 100644
--- a/manila_tempest_tests/tests/api/admin/test_migration_negative.py
+++ b/manila_tempest_tests/tests/api/admin/test_migration_negative.py
@@ -13,85 +13,199 @@
# License for the specific language governing permissions and limitations
# under the License.
+import six
+
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
+import testtools
+from manila_tempest_tests.common import constants
+from manila_tempest_tests import share_exceptions
from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
CONF = config.CONF
-class MigrationNFSTest(base.BaseSharesAdminTest):
+class MigrationTest(base.BaseSharesAdminTest):
"""Tests Share Migration.
- Tests migration in multi-backend environment.
+ Tests share migration in multi-backend environment.
"""
protocol = "nfs"
@classmethod
def resource_setup(cls):
- super(MigrationNFSTest, cls).resource_setup()
- if not CONF.share.run_migration_tests:
- raise cls.skipException("Migration tests disabled. Skipping.")
+ super(MigrationTest, cls).resource_setup()
+ if cls.protocol not in CONF.share.enable_protocols:
+ message = "%s tests are disabled." % cls.protocol
+ raise cls.skipException(message)
+ if not (CONF.share.run_host_assisted_migration_tests or
+ CONF.share.run_driver_assisted_migration_tests):
+ raise cls.skipException("Share migration tests are disabled.")
- cls.share = cls.create_share(cls.protocol)
- cls.share = cls.shares_client.get_share(cls.share['id'])
- pools = cls.shares_client.list_pools()['pools']
+ pools = cls.shares_client.list_pools(detail=True)['pools']
if len(pools) < 2:
raise cls.skipException("At least two different pool entries "
- "are needed to run migration tests. "
- "Skipping.")
- cls.dest_pool = next((x for x in pools
- if x['name'] != cls.share['host']), None)
+ "are needed to run share migration tests.")
+
+ cls.share = cls.create_share(cls.protocol)
+ cls.share = cls.shares_client.get_share(cls.share['id'])
+
+ default_type = cls.shares_v2_client.list_share_types(
+ default=True)['share_type']
+
+ dest_pool = utils.choose_matching_backend(
+ cls.share, pools, default_type)
+
+ if not dest_pool or dest_pool.get('name') is None:
+ raise share_exceptions.ShareMigrationException(
+ "No valid pool entries to run share migration tests.")
+
+ cls.dest_pool = dest_pool['name']
+
+ extra_specs = {
+ 'storage_protocol': CONF.share.capability_storage_protocol,
+ 'driver_handles_share_servers': CONF.share.multitenancy_enabled,
+ 'snapshot_support': six.text_type(
+ not CONF.share.capability_snapshot_support),
+ }
+ cls.new_type = cls.create_share_type(
+ name=data_utils.rand_name(
+ 'new_invalid_share_type_for_migration'),
+ cleanup_in_class=True,
+ extra_specs=extra_specs)
@test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
- @base.skip_if_microversion_lt("2.15")
+ @base.skip_if_microversion_lt("2.22")
def test_migration_cancel_invalid(self):
self.assertRaises(
lib_exc.BadRequest, self.shares_v2_client.migration_cancel,
self.share['id'])
@test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
- @base.skip_if_microversion_lt("2.15")
- def test_migration_get_progress_invalid(self):
+ @base.skip_if_microversion_lt("2.22")
+ def test_migration_get_progress_None(self):
+ self.shares_v2_client.reset_task_state(self.share["id"], None)
+ self.shares_v2_client.wait_for_share_status(
+ self.share["id"], None, 'task_state')
self.assertRaises(
lib_exc.BadRequest, self.shares_v2_client.migration_get_progress,
self.share['id'])
@test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
- @base.skip_if_microversion_lt("2.15")
+ @base.skip_if_microversion_lt("2.22")
def test_migration_complete_invalid(self):
self.assertRaises(
lib_exc.BadRequest, self.shares_v2_client.migration_complete,
self.share['id'])
@test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
- @base.skip_if_microversion_lt("2.5")
- def test_migrate_share_with_snapshot_v2_5(self):
+ @base.skip_if_microversion_lt("2.22")
+ def test_migration_cancel_not_found(self):
+ self.assertRaises(
+ lib_exc.NotFound, self.shares_v2_client.migration_cancel,
+ 'invalid_share_id')
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @base.skip_if_microversion_lt("2.22")
+ def test_migration_get_progress_not_found(self):
+ self.assertRaises(
+ lib_exc.NotFound, self.shares_v2_client.migration_get_progress,
+ 'invalid_share_id')
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @base.skip_if_microversion_lt("2.22")
+ def test_migration_complete_not_found(self):
+ self.assertRaises(
+ lib_exc.NotFound, self.shares_v2_client.migration_complete,
+ 'invalid_share_id')
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @base.skip_if_microversion_lt("2.22")
+ @testtools.skipUnless(CONF.share.run_snapshot_tests,
+ "Snapshot tests are disabled.")
+ def test_migrate_share_with_snapshot(self):
snap = self.create_snapshot_wait_for_active(self.share['id'])
self.assertRaises(
lib_exc.BadRequest, self.shares_v2_client.migrate_share,
- self.share['id'], self.dest_pool, True, version='2.5')
+ self.share['id'], self.dest_pool)
self.shares_client.delete_snapshot(snap['id'])
self.shares_client.wait_for_resource_deletion(snapshot_id=snap["id"])
@test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
- @base.skip_if_microversion_lt("2.5")
- def test_migrate_share_same_host_v2_5(self):
+ @base.skip_if_microversion_lt("2.22")
+ def test_migrate_share_same_host(self):
self.assertRaises(
lib_exc.BadRequest, self.shares_v2_client.migrate_share,
- self.share['id'], self.share['host'], True, version='2.5')
+ self.share['id'], self.share['host'])
@test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
- @base.skip_if_microversion_lt("2.5")
- def test_migrate_share_not_available_v2_5(self):
- self.shares_client.reset_state(self.share['id'], 'error')
- self.shares_client.wait_for_share_status(self.share['id'], 'error')
+ @base.skip_if_microversion_lt("2.22")
+ def test_migrate_share_host_invalid(self):
+ self.assertRaises(
+ lib_exc.NotFound, self.shares_v2_client.migrate_share,
+ self.share['id'], 'invalid_host')
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @base.skip_if_microversion_lt("2.22")
+ def test_migrate_share_host_assisted_not_allowed(self):
+ self.shares_v2_client.migrate_share(
+ self.share['id'], self.dest_pool,
+ force_host_assisted_migration=True, writable=True,
+ preserve_metadata=True)
+ self.shares_v2_client.wait_for_migration_status(
+ self.share['id'], self.dest_pool,
+ constants.TASK_STATE_MIGRATION_ERROR)
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @base.skip_if_microversion_lt("2.22")
+ def test_migrate_share_change_type_no_valid_host(self):
+ self.shares_v2_client.migrate_share(
+ self.share['id'], self.dest_pool,
+ new_share_type_id=self.new_type['share_type']['id'])
+ self.shares_v2_client.wait_for_migration_status(
+ self.share['id'], self.dest_pool,
+ constants.TASK_STATE_MIGRATION_ERROR)
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @base.skip_if_microversion_lt("2.22")
+ def test_migrate_share_not_found(self):
+ self.assertRaises(
+ lib_exc.NotFound, self.shares_v2_client.migrate_share,
+ 'invalid_share_id', self.dest_pool)
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @base.skip_if_microversion_lt("2.22")
+ def test_migrate_share_not_available(self):
+ self.shares_client.reset_state(self.share['id'],
+ constants.STATUS_ERROR)
+ self.shares_client.wait_for_share_status(self.share['id'],
+ constants.STATUS_ERROR)
self.assertRaises(
lib_exc.BadRequest, self.shares_v2_client.migrate_share,
- self.share['id'], self.dest_pool, True, version='2.5')
- self.shares_client.reset_state(self.share['id'], 'available')
- self.shares_client.wait_for_share_status(self.share['id'], 'available')
+ self.share['id'], self.dest_pool)
+ self.shares_client.reset_state(self.share['id'],
+ constants.STATUS_AVAILABLE)
+ self.shares_client.wait_for_share_status(self.share['id'],
+ constants.STATUS_AVAILABLE)
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @base.skip_if_microversion_lt("2.22")
+ def test_migrate_share_invalid_share_network(self):
+ self.assertRaises(
+ lib_exc.BadRequest, self.shares_v2_client.migrate_share,
+ self.share['id'], self.dest_pool,
+ new_share_network_id='invalid_net_id')
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ @base.skip_if_microversion_lt("2.22")
+ def test_migrate_share_invalid_share_type(self):
+ self.assertRaises(
+ lib_exc.BadRequest, self.shares_v2_client.migrate_share,
+ self.share['id'], self.dest_pool, True,
+ new_share_type_id='invalid_type_id')
diff --git a/manila_tempest_tests/tests/api/admin/test_replication.py b/manila_tempest_tests/tests/api/admin/test_replication.py
index f8b1741..ff2f3c6 100644
--- a/manila_tempest_tests/tests/api/admin/test_replication.py
+++ b/manila_tempest_tests/tests/api/admin/test_replication.py
@@ -86,6 +86,10 @@
replica = self.create_share_replica(
share["id"], self.replica_zone, cleanup=False,
client=self.admin_client)
+ # Wait for replica state to update after creation
+ self.admin_client.wait_for_share_replica_status(
+ replica['id'], constants.REPLICATION_STATE_IN_SYNC,
+ status_attr='replica_state')
# List replicas
replica_list = self.admin_client.list_share_replicas(
diff --git a/manila_tempest_tests/tests/api/admin/test_replication_actions.py b/manila_tempest_tests/tests/api/admin/test_replication_actions.py
index a8f5eb7..41bfa86 100644
--- a/manila_tempest_tests/tests/api/admin/test_replication_actions.py
+++ b/manila_tempest_tests/tests/api/admin/test_replication_actions.py
@@ -58,7 +58,7 @@
client=cls.admin_client)
cls.share_type = share_type["share_type"]
# Create share with above share_type
- cls.share = cls.create_share(size=2,
+ cls.share = cls.create_share(size=CONF.share.share_size+1,
share_type_id=cls.share_type["id"],
availability_zone=cls.share_zone,
client=cls.admin_client)
diff --git a/manila_tempest_tests/tests/api/admin/test_share_servers.py b/manila_tempest_tests/tests/api/admin/test_share_servers.py
index e08a7a4..2b3bd6f 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_servers.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_servers.py
@@ -165,8 +165,8 @@
@test.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
def test_show_share_server(self):
- servers = self.shares_client.list_share_servers()
- server = self.shares_client.show_share_server(servers[0]["id"])
+ share = self.shares_client.get_share(self.share["id"])
+ server = self.shares_client.show_share_server(share["share_server_id"])
keys = [
"id",
"host",
diff --git a/manila_tempest_tests/tests/api/admin/test_share_snapshot_instances.py b/manila_tempest_tests/tests/api/admin/test_share_snapshot_instances.py
new file mode 100644
index 0000000..68f5661
--- /dev/null
+++ b/manila_tempest_tests/tests/api/admin/test_share_snapshot_instances.py
@@ -0,0 +1,121 @@
+# Copyright 2016 Huawei
+# 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 import test
+import testtools
+
+from manila_tempest_tests.tests.api import base
+
+CONF = config.CONF
+
+
+@testtools.skipUnless(CONF.share.run_snapshot_tests,
+ 'Snapshot tests are disabled.')
+@base.skip_if_microversion_lt("2.19")
+@ddt.ddt
+class ShareSnapshotInstancesTest(base.BaseSharesAdminTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(ShareSnapshotInstancesTest, cls).resource_setup()
+ cls.share = cls.create_share()
+ snap = cls.create_snapshot_wait_for_active(cls.share["id"])
+ cls.snapshot = cls.shares_v2_client.get_snapshot(snap['id'])
+
+ @ddt.data(True, False)
+ @test.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
+ def test_list_snapshot_instances_by_snapshot(self, detail):
+ """Test that we get only the 1 snapshot instance from snapshot."""
+ snapshot_instances = self.shares_v2_client.list_snapshot_instances(
+ detail=detail, snapshot_id=self.snapshot['id'])
+
+ expected_keys = ['id', 'snapshot_id', 'status']
+
+ if detail:
+ extra_detail_keys = ['provider_location', 'share_id',
+ 'share_instance_id', 'created_at',
+ 'updated_at', 'progress']
+ expected_keys.extend(extra_detail_keys)
+
+ si_num = len(snapshot_instances)
+ self.assertEqual(1, si_num,
+ 'Incorrect amount of snapshot instances found; '
+ 'expected 1, found %s.' % si_num)
+
+ si = snapshot_instances[0]
+ self.assertEqual(self.snapshot['id'], si['snapshot_id'],
+ 'Snapshot instance %s has incorrect snapshot id;'
+ ' expected %s, got %s.' % (si['id'],
+ self.snapshot['id'],
+ si['snapshot_id']))
+ if detail:
+ self.assertEqual(self.snapshot['share_id'], si['share_id'])
+
+ for key in si:
+ self.assertIn(key, expected_keys)
+ self.assertEqual(len(expected_keys), len(si))
+
+ @test.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
+ def test_list_snapshot_instances(self):
+ """Test that we get at least the snapshot instance."""
+ snapshot_instances = self.shares_v2_client.list_snapshot_instances()
+
+ snapshot_ids = [si['snapshot_id'] for si in snapshot_instances]
+
+ msg = ('Snapshot instance for snapshot %s was not found.' %
+ self.snapshot['id'])
+ self.assertIn(self.snapshot['id'], snapshot_ids, msg)
+
+ @test.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
+ def test_get_snapshot_instance(self):
+ instances = self.shares_v2_client.list_snapshot_instances(
+ snapshot_id=self.snapshot['id'])
+ instance_detail = self.shares_v2_client.get_snapshot_instance(
+ instance_id=instances[0]['id'])
+
+ expected_keys = (
+ 'id', 'created_at', 'updated_at', 'progress', 'provider_location',
+ 'share_id', 'share_instance_id', 'snapshot_id', 'status',
+ )
+
+ for key in instance_detail:
+ self.assertIn(key, expected_keys)
+ self.assertEqual(len(expected_keys), len(instance_detail))
+ self.assertEqual(self.snapshot['id'], instance_detail['snapshot_id'])
+ self.assertEqual(self.snapshot['share_id'],
+ instance_detail['share_id'])
+ self.assertEqual(self.snapshot['provider_location'],
+ instance_detail['provider_location'])
+
+ @test.attr(type=[base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND])
+ def test_reset_snapshot_instance_status_and_delete(self):
+ """Test resetting a snapshot instance's status attribute."""
+ snapshot = self.create_snapshot_wait_for_active(self.share["id"])
+
+ snapshot_instances = self.shares_v2_client.list_snapshot_instances(
+ snapshot_id=snapshot['id'])
+
+ sii = snapshot_instances[0]['id']
+
+ for status in ("error", "available"):
+ self.shares_v2_client.reset_snapshot_instance_status(
+ sii, status=status)
+ self.shares_v2_client.wait_for_snapshot_instance_status(
+ sii, expected_status=status)
+ self.shares_v2_client.delete_snapshot(snapshot['id'])
+ self.shares_v2_client.wait_for_resource_deletion(
+ snapshot_id=snapshot['id'])
diff --git a/manila_tempest_tests/tests/api/admin/test_share_snapshot_instances_negative.py b/manila_tempest_tests/tests/api/admin/test_share_snapshot_instances_negative.py
new file mode 100644
index 0000000..b76481c
--- /dev/null
+++ b/manila_tempest_tests/tests/api/admin/test_share_snapshot_instances_negative.py
@@ -0,0 +1,88 @@
+# Copyright 2016 Huawei
+# 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.
+
+from tempest import config
+from tempest.lib import exceptions as lib_exc
+from tempest import test
+import testtools
+
+from manila_tempest_tests.tests.api import base
+
+CONF = config.CONF
+
+
+@testtools.skipUnless(CONF.share.run_snapshot_tests,
+ 'Snapshot tests are disabled.')
+@base.skip_if_microversion_lt("2.19")
+class SnapshotInstancesNegativeTest(base.BaseSharesMixedTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(SnapshotInstancesNegativeTest, cls).resource_setup()
+ cls.admin_client = cls.admin_shares_v2_client
+ cls.member_client = cls.shares_v2_client
+ cls.share = cls.create_share(client=cls.admin_client)
+ cls.snapshot = cls.create_snapshot_wait_for_active(
+ cls.share["id"], client=cls.admin_client)
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ def test_list_snapshot_instances_with_snapshot_by_non_admin(self):
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.member_client.list_snapshot_instances,
+ snapshot_id=self.snapshot['id'])
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ def test_get_snapshot_instance_by_non_admin(self):
+ instances = self.admin_client.list_snapshot_instances(
+ snapshot_id=self.snapshot['id'])
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.member_client.get_snapshot_instance,
+ instance_id=instances[0]['id'])
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
+ def test_reset_snapshot_instance_status_by_non_admin(self):
+ instances = self.admin_client.list_snapshot_instances(
+ snapshot_id=self.snapshot['id'])
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.member_client.reset_snapshot_instance_status,
+ instances[0]['id'],
+ 'error')
+
+
+@testtools.skipUnless(CONF.share.run_snapshot_tests,
+ 'Snapshot tests are disabled.')
+@base.skip_if_microversion_lt("2.19")
+class SnapshotInstancesNegativeNoResourceTest(base.BaseSharesMixedTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(SnapshotInstancesNegativeNoResourceTest, cls).resource_setup()
+ cls.admin_client = cls.admin_shares_v2_client
+ cls.member_client = cls.shares_v2_client
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API])
+ def test_get_snapshot_instance_with_non_existent_instance(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.admin_client.get_snapshot_instance,
+ instance_id="nonexistent_instance")
+
+ @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API])
+ def test_list_snapshot_instances_by_non_admin(self):
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.member_client.list_snapshot_instances)
diff --git a/manila_tempest_tests/tests/api/base.py b/manila_tempest_tests/tests/api/base.py
index 79c8673..a36f391 100644
--- a/manila_tempest_tests/tests/api/base.py
+++ b/manila_tempest_tests/tests/api/base.py
@@ -261,9 +261,9 @@
@classmethod
def resource_cleanup(cls):
- super(BaseSharesTest, cls).resource_cleanup()
cls.clear_resources(cls.class_resources)
cls.clear_isolated_creds(cls.class_isolated_creds)
+ super(BaseSharesTest, cls).resource_cleanup()
@classmethod
@network_synchronized
@@ -401,13 +401,19 @@
return share
@classmethod
- def migrate_share(cls, share_id, dest_host, client=None, notify=True,
- wait_for_status='migration_success', **kwargs):
+ def migrate_share(
+ cls, share_id, dest_host, wait_for_status, client=None,
+ force_host_assisted_migration=False, new_share_network_id=None,
+ new_share_type_id=None, **kwargs):
client = client or cls.shares_v2_client
- client.migrate_share(share_id, dest_host, notify, **kwargs)
+ client.migrate_share(
+ share_id, dest_host,
+ force_host_assisted_migration=force_host_assisted_migration,
+ new_share_network_id=new_share_network_id,
+ writable=False, preserve_metadata=False, nondisruptive=False,
+ new_share_type_id=new_share_type_id, **kwargs)
share = client.wait_for_migration_status(
- share_id, dest_host, wait_for_status,
- version=kwargs.get('version'))
+ share_id, dest_host, wait_for_status, **kwargs)
return share
@classmethod
@@ -415,8 +421,15 @@
client = client or cls.shares_v2_client
client.migration_complete(share_id, **kwargs)
share = client.wait_for_migration_status(
- share_id, dest_host, 'migration_success',
- version=kwargs.get('version'))
+ share_id, dest_host, 'migration_success', **kwargs)
+ return share
+
+ @classmethod
+ def migration_cancel(cls, share_id, dest_host, client=None, **kwargs):
+ client = client or cls.shares_v2_client
+ client.migration_cancel(share_id, **kwargs)
+ share = client.wait_for_migration_status(
+ share_id, dest_host, 'migration_cancelled', **kwargs)
return share
@classmethod
@@ -498,7 +511,7 @@
d["share"] = cls._create_share(
*d["args"], **d["kwargs"])
else:
- raise e
+ raise
return [d["share"] for d in data]
diff --git a/manila_tempest_tests/tests/api/test_replication.py b/manila_tempest_tests/tests/api/test_replication.py
index 27db099..d5ec21a 100644
--- a/manila_tempest_tests/tests/api/test_replication.py
+++ b/manila_tempest_tests/tests/api/test_replication.py
@@ -175,6 +175,8 @@
self.delete_share_replica(share_replica["id"])
@test.attr(type=[base.TAG_POSITIVE, base.TAG_BACKEND])
+ @testtools.skipUnless(CONF.share.run_multiple_share_replicas_tests,
+ 'Multiple share replicas tests are disabled.')
def test_add_multiple_share_replicas(self):
rep_domain, pools = self.get_pools_for_replication_domain()
if len(pools) < 3:
diff --git a/manila_tempest_tests/tests/api/test_rules.py b/manila_tempest_tests/tests/api/test_rules.py
index 09ad7a4..8e70a5f 100644
--- a/manila_tempest_tests/tests/api/test_rules.py
+++ b/manila_tempest_tests/tests/api/test_rules.py
@@ -424,7 +424,7 @@
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 = "alice"
+ cls.access_to = "eve"
cls.shares_v2_client.share_protocol = cls.protocol
cls.share = cls.create_share()
@@ -433,8 +433,8 @@
def test_list_access_rules(self, version):
if (utils.is_microversion_lt(version, '2.13') and
CONF.share.enable_cephx_rules_for_protocols):
- msg = ("API version %s does not support cephx access type, "
- "need version greater than 2.13." % version)
+ msg = ("API version %s does not support cephx access type, need "
+ "version >= 2.13." % version)
raise self.skipException(msg)
# create rule
@@ -465,7 +465,10 @@
version=version)
# verify keys
- for key in ("id", "access_type", "access_to", "access_level"):
+ keys = ("id", "access_type", "access_to", "access_level")
+ if utils.is_microversion_ge(version, '2.21'):
+ keys += ("access_key", )
+ for key in keys:
[self.assertIn(key, r.keys()) for r in rules]
for key in ('deleted', 'deleted_at', 'instance_mappings'):
[self.assertNotIn(key, r.keys()) for r in rules]
@@ -474,6 +477,11 @@
self.assertEqual(self.access_type, rules[0]["access_type"])
self.assertEqual(self.access_to, rules[0]["access_to"])
self.assertEqual('rw', rules[0]["access_level"])
+ if utils.is_microversion_ge(version, '2.21'):
+ if self.access_type == 'cephx':
+ self.assertIsNotNone(rules[0]['access_key'])
+ else:
+ self.assertIsNone(rules[0]['access_key'])
# our share id in list and have no duplicates
gen = [r["id"] for r in rules if r["id"] in rule["id"]]
@@ -495,8 +503,8 @@
def test_access_rules_deleted_if_share_deleted(self, version):
if (utils.is_microversion_lt(version, '2.13') and
CONF.share.enable_cephx_rules_for_protocols):
- msg = ("API version %s does not support cephx access type, "
- "need version greater than 2.13." % version)
+ msg = ("API version %s does not support cephx access type, need "
+ "version >= 2.13." % version)
raise self.skipException(msg)
# create share
diff --git a/manila_tempest_tests/tests/api/test_rules_negative.py b/manila_tempest_tests/tests/api/test_rules_negative.py
index 376ba85..7fc9fc7 100644
--- a/manila_tempest_tests/tests/api/test_rules_negative.py
+++ b/manila_tempest_tests/tests/api/test_rules_negative.py
@@ -19,7 +19,6 @@
from tempest import test
import testtools
-from manila_tempest_tests import share_exceptions
from manila_tempest_tests.tests.api import base
from manila_tempest_tests import utils
@@ -348,16 +347,6 @@
self.share["id"], self.access_type, self.access_to,
access_level="su")
- @test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
- def test_create_access_rule_cephx_with_unsupported_access_level_ro(self):
- rule = self.shares_v2_client.create_access_rule(
- self.share["id"], self.access_type, self.access_to,
- access_level="ro")
- self.assertRaises(
- share_exceptions.AccessRuleBuildErrorException,
- self.shares_client.wait_for_access_rule_status,
- self.share['id'], rule['id'], "active")
-
def skip_if_cephx_access_type_not_supported_by_client(self, client):
if client == 'shares_client':
@@ -366,8 +355,8 @@
version = LATEST_MICROVERSION
if (CONF.share.enable_cephx_rules_for_protocols and
utils.is_microversion_lt(version, '2.13')):
- msg = ("API version %s does not support cephx access type, "
- "need version greater than 2.13." % version)
+ msg = ("API version %s does not support cephx access type, need "
+ "version >= 2.13." % version)
raise self.skipException(msg)
diff --git a/manila_tempest_tests/tests/api/test_share_networks.py b/manila_tempest_tests/tests/api/test_share_networks.py
index b9d2d8c..84ac838 100755
--- a/manila_tempest_tests/tests/api/test_share_networks.py
+++ b/manila_tempest_tests/tests/api/test_share_networks.py
@@ -53,6 +53,10 @@
if utils.is_microversion_supported('2.18'):
keys.append('gateway')
+ # In v2.20 and beyond, we expect mtu.
+ if utils.is_microversion_supported('2.20'):
+ keys.append('mtu')
+
[self.assertIn(key, sn.keys()) for sn in listed for key in keys]
@test.attr(type=[base.TAG_POSITIVE, base.TAG_API])
diff --git a/manila_tempest_tests/tests/api/test_shares.py b/manila_tempest_tests/tests/api/test_shares.py
index f7ff194..7926b33 100644
--- a/manila_tempest_tests/tests/api/test_shares.py
+++ b/manila_tempest_tests/tests/api/test_shares.py
@@ -57,8 +57,10 @@
self.assertFalse(share['is_public'])
# The 'status' of the share returned by the create API must be
- # the default value - 'creating'.
- self.assertEqual('creating', share['status'])
+ # set and have value either 'creating' or
+ # 'available' (if share creation is really fast as in
+ # case of Dummy driver).
+ self.assertIn(share['status'], ('creating', 'available'))
# Get share using v 2.1 - we expect key 'snapshot_support' to be absent
share_get = self.shares_v2_client.get_share(share['id'], version='2.1')
@@ -178,8 +180,10 @@
self.protocol, snapshot_id=snap["id"], cleanup_in_class=False)
# The 'status' of the share returned by the create API must be
- # the default value - 'creating'.
- self.assertEqual('creating', child['status'])
+ # set and have value either 'creating' or
+ # 'available' (if share creation is really fast as in
+ # case of Dummy driver).
+ self.assertIn(child['status'], ('creating', 'available'))
# verify share, created from snapshot
get = self.shares_client.get_share(child["id"])
diff --git a/manila_tempest_tests/tests/scenario/manager_share.py b/manila_tempest_tests/tests/scenario/manager_share.py
index 972654b..14fb34d 100644
--- a/manila_tempest_tests/tests/scenario/manager_share.py
+++ b/manila_tempest_tests/tests/scenario/manager_share.py
@@ -21,6 +21,7 @@
from tempest.lib.common.utils import data_utils
from tempest.scenario import manager
+from manila_tempest_tests.common import constants
from manila_tempest_tests.services.share.json import shares_client
from manila_tempest_tests.services.share.v2.json import (
shares_client as shares_v2_client)
@@ -196,11 +197,19 @@
return linux_client
- def _migrate_share(self, share_id, dest_host, client=None):
+ def _migrate_share(self, share_id, dest_host, status, client=None):
client = client or self.shares_admin_v2_client
- client.migrate_share(share_id, dest_host, True)
- share = client.wait_for_migration_status(share_id, dest_host,
- 'migration_success')
+ client.migrate_share(share_id, dest_host, writable=False,
+ preserve_metadata=False, nondisruptive=False)
+ share = client.wait_for_migration_status(share_id, dest_host, status)
+ return share
+
+ def _migration_complete(self, share_id, dest_host, client=None, **kwargs):
+ client = client or self.shares_admin_v2_client
+ client.migration_complete(share_id, **kwargs)
+ share = client.wait_for_migration_status(
+ share_id, dest_host, constants.TASK_STATE_MIGRATION_SUCCESS,
+ **kwargs)
return share
def _create_share_type(self, name, is_public=True, **kwargs):
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 dbe5599..532ddd5 100644
--- a/manila_tempest_tests/tests/scenario/test_share_basic_ops.py
+++ b/manila_tempest_tests/tests/scenario/test_share_basic_ops.py
@@ -20,7 +20,9 @@
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions
from tempest import test
+import testtools
+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
from manila_tempest_tests import utils
@@ -123,9 +125,10 @@
data = ssh_client.exec_command("sudo cat /mnt/t1")
return data.rstrip()
- def migrate_share(self, share_id, dest_host):
- share = self._migrate_share(share_id, dest_host,
+ def migrate_share(self, share_id, dest_host, status):
+ share = self._migrate_share(share_id, dest_host, status,
self.shares_admin_v2_client)
+ share = self._migration_complete(share['id'], dest_host)
return share
def create_share_network(self):
@@ -244,47 +247,51 @@
@test.services('compute', 'network')
@test.attr(type=[base.TAG_POSITIVE, base.TAG_BACKEND])
+ @testtools.skipUnless(CONF.share.run_host_assisted_migration_tests or
+ CONF.share.run_driver_assisted_migration_tests,
+ "Share migration tests are disabled.")
def test_migration_files(self):
- if self.protocol == "CIFS":
- raise self.skipException("Test for CIFS protocol not supported "
- "at this moment. Skipping.")
+ if self.protocol != "NFS":
+ raise self.skipException("Only NFS protocol supported "
+ "at this moment.")
- if not CONF.share.run_migration_tests:
- raise self.skipException("Migration tests disabled. Skipping.")
-
- pools = self.shares_admin_client.list_pools()['pools']
+ pools = self.shares_admin_v2_client.list_pools(detail=True)['pools']
if len(pools) < 2:
- raise self.skipException("At least two different pool entries "
- "are needed to run migration tests. "
- "Skipping.")
+ raise self.skipException("At least two different pool entries are "
+ "needed to run share migration tests.")
instance = self.boot_instance(wait_until="BUILD")
self.create_share()
instance = self.wait_for_active_instance(instance["id"])
- share = self.shares_client.get_share(self.share['id'])
+ self.share = self.shares_client.get_share(self.share['id'])
- dest_pool = next((x for x in pools if x['name'] != share['host']),
- None)
+ default_type = self.shares_v2_client.list_share_types(
+ default=True)['share_type']
+
+ dest_pool = utils.choose_matching_backend(
+ self.share, pools, default_type)
self.assertIsNotNone(dest_pool)
self.assertIsNotNone(dest_pool.get('name'))
dest_pool = dest_pool['name']
- self.allow_access_ip(self.share['id'], instance=instance,
- cleanup=False)
+ self.allow_access_ip(
+ self.share['id'], instance=instance, cleanup=False)
ssh_client = self.init_ssh(instance)
if utils.is_microversion_lt(CONF.share.max_api_microversion, "2.9"):
- locations = self.share['export_locations']
+ exports = self.share['export_locations']
else:
exports = self.shares_v2_client.list_share_export_locations(
self.share['id'])
- locations = [x['path'] for x in exports]
+ self.assertNotEmpty(exports)
+ exports = [x['path'] for x in exports]
+ self.assertNotEmpty(exports)
- self.mount_share(locations[0], ssh_client)
+ self.mount_share(exports[0], ssh_client)
ssh_client.exec_command("mkdir -p /mnt/f1")
ssh_client.exec_command("mkdir -p /mnt/f2")
@@ -307,21 +314,27 @@
self.umount_share(ssh_client)
- share = self.migrate_share(share['id'], dest_pool)
+ task_state = (constants.TASK_STATE_DATA_COPYING_COMPLETED,
+ constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE)
+
+ self.share = self.migrate_share(
+ self.share['id'], dest_pool, task_state)
+
if utils.is_microversion_lt(CONF.share.max_api_microversion, "2.9"):
- new_locations = self.share['export_locations']
+ new_exports = self.share['export_locations']
+ self.assertNotEmpty(new_exports)
else:
new_exports = self.shares_v2_client.list_share_export_locations(
self.share['id'])
- new_locations = [x['path'] for x in new_exports]
+ self.assertNotEmpty(new_exports)
+ new_exports = [x['path'] for x in new_exports]
+ self.assertNotEmpty(new_exports)
- self.assertEqual(dest_pool, share['host'])
- locations.sort()
- new_locations.sort()
- self.assertNotEqual(locations, new_locations)
- self.assertEqual('migration_success', share['task_state'])
+ self.assertEqual(dest_pool, self.share['host'])
+ self.assertEqual(constants.TASK_STATE_MIGRATION_SUCCESS,
+ self.share['task_state'])
- self.mount_share(new_locations[0], ssh_client)
+ self.mount_share(new_exports[0], ssh_client)
output = ssh_client.exec_command("ls -lRA --ignore=lost+found /mnt")
diff --git a/manila_tempest_tests/utils.py b/manila_tempest_tests/utils.py
index dea51ab..277130e 100644
--- a/manila_tempest_tests/utils.py
+++ b/manila_tempest_tests/utils.py
@@ -100,3 +100,18 @@
TEST_NET_3 = '203.0.113.'
final_octet = six.text_type(random.randint(0, 255))
return TEST_NET_3 + final_octet
+
+
+def choose_matching_backend(share, pools, share_type):
+ extra_specs = {}
+ # fix extra specs with string values instead of boolean
+ for k, v in share_type['extra_specs'].items():
+ extra_specs[k] = (True if six.text_type(v).lower() == 'true'
+ else False if six.text_type(v).lower() == 'false'
+ else v)
+ selected_pool = next(
+ (x for x in pools if (x['name'] != share['host'] and all(
+ y in x['capabilities'].items() for y in extra_specs.items()))),
+ None)
+
+ return selected_pool