Add Share Migration tempest functional tests
This patch adds functional tests for Share Migration, running on generic driver DHSS = true mode. Implements: blueprint share-migration Change-Id: I64b0a3ee77b27278cc294f72702408a27888e0e9
This commit is contained in:
parent
f5add61a8d
commit
740fc64940
@ -542,6 +542,16 @@ function update_tempest {
|
||||
fi
|
||||
}
|
||||
|
||||
function install_libraries {
|
||||
if [ "$MANILA_MULTI_BACKEND" = "True" ]; then
|
||||
if is_ubuntu; then
|
||||
install_package nfs-common
|
||||
else
|
||||
install_package nfs-utils
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Main dispatcher
|
||||
if [[ "$1" == "stack" && "$2" == "install" ]]; then
|
||||
echo_summary "Installing Manila"
|
||||
@ -552,6 +562,8 @@ elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
|
||||
configure_manila
|
||||
echo_summary "Initializing Manila"
|
||||
init_manila
|
||||
echo_summary "Installing extra libraries"
|
||||
install_libraries
|
||||
elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
|
||||
echo_summary "Creating Manila entities for auth service"
|
||||
create_manila_accounts
|
||||
|
@ -151,4 +151,11 @@ ShareGroup = [
|
||||
cfg.StrOpt("client_vm_flavor_ref",
|
||||
default="100",
|
||||
help="Flavor used for client vm in scenario tests."),
|
||||
cfg.IntOpt("migration_timeout",
|
||||
default=1200,
|
||||
help="Time to wait for share migration before "
|
||||
"timing out (seconds)."),
|
||||
cfg.BoolOpt("migration_enabled",
|
||||
default=True,
|
||||
help="Enable or disable migration tests."),
|
||||
]
|
||||
|
@ -47,10 +47,24 @@ class SharesClient(rest_client.RestClient):
|
||||
self.build_interval = CONF.share.build_interval
|
||||
self.build_timeout = CONF.share.build_timeout
|
||||
self.API_MICROVERSIONS_HEADER = 'x-openstack-manila-api-version'
|
||||
self.API_MICROVERSIONS_EXPERIMENTAL_HEADER = (
|
||||
'x-openstack-manila-api-experimental')
|
||||
|
||||
def _get_version_dict(self, version):
|
||||
return {self.API_MICROVERSIONS_HEADER: version}
|
||||
|
||||
def migrate_share(self, share_id, host):
|
||||
post_body = {
|
||||
'os-migrate_share': {
|
||||
'host': host,
|
||||
}
|
||||
}
|
||||
body = json.dumps(post_body)
|
||||
headers = self._get_version_dict('1.6')
|
||||
headers[self.API_MICROVERSIONS_EXPERIMENTAL_HEADER] = 'true'
|
||||
return self.post('shares/%s/action' % share_id, body,
|
||||
headers=headers, extra_headers=True)
|
||||
|
||||
def send_microversion_request(self, version=None):
|
||||
"""Prepare and send the HTTP GET Request to the base URL.
|
||||
|
||||
@ -263,6 +277,30 @@ class SharesClient(rest_client.RestClient):
|
||||
self.expected_success(202, resp.status)
|
||||
return body
|
||||
|
||||
def wait_for_migration_completed(self, share_id, dest_host):
|
||||
"""Waits for a share to migrate to a certain host."""
|
||||
share = self.get_share(share_id)
|
||||
migration_timeout = CONF.share.migration_timeout
|
||||
start = int(time.time())
|
||||
while share['task_state'] != 'migration_success':
|
||||
time.sleep(self.build_interval)
|
||||
share = self.get_share(share_id)
|
||||
if share['task_state'] == 'migration_success':
|
||||
return share
|
||||
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 migrate from '
|
||||
'host %(src)s to host %(dest)s within the required '
|
||||
'time %(timeout)s.' % {
|
||||
'src': share['host'],
|
||||
'dest': dest_host,
|
||||
'share_id': share['id'],
|
||||
'timeout': self.build_timeout
|
||||
})
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
def wait_for_share_status(self, share_id, status):
|
||||
"""Waits for a share to reach a given status."""
|
||||
body = self.get_share(share_id)
|
||||
|
@ -48,5 +48,10 @@ class InvalidResource(exceptions.TempestException):
|
||||
message = "Provided invalid resource: %(message)s"
|
||||
|
||||
|
||||
class ShareMigrationException(exceptions.TempestException):
|
||||
message = ("Share %(share_id)s failed to migrate from "
|
||||
"host %(src)s to host %(dest)s.")
|
||||
|
||||
|
||||
class ResourceReleaseFailed(exceptions.TempestException):
|
||||
message = "Failed to release resource '%(res_type)s' with id '%(res_id)s'."
|
||||
|
67
manila_tempest_tests/tests/api/admin/test_migration.py
Normal file
67
manila_tempest_tests/tests/api/admin/test_migration.py
Normal file
@ -0,0 +1,67 @@
|
||||
# Copyright 2015 Hitachi Data Systems.
|
||||
# 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 # noqa
|
||||
from tempest import test # noqa
|
||||
|
||||
from manila_tempest_tests.tests.api import base
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class MigrationTest(base.BaseSharesAdminTest):
|
||||
"""Tests Share Migration.
|
||||
|
||||
Tests migration in multi-backend environment.
|
||||
"""
|
||||
|
||||
protocol = "nfs"
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
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)
|
||||
|
||||
@test.attr(type=["gate", "smoke", ])
|
||||
def test_migration_empty(self):
|
||||
|
||||
if not CONF.share.migration_enabled:
|
||||
raise self.skipException("Migration tests disabled. Skipping.")
|
||||
|
||||
pools = self.shares_client.list_pools()['pools']
|
||||
|
||||
if len(pools) < 2:
|
||||
raise self.skipException("At least two different running "
|
||||
"manila-share services are needed to "
|
||||
"run migration tests. Skipping.")
|
||||
share = self.create_share(self.protocol)
|
||||
share = self.shares_client.get_share(share['id'])
|
||||
|
||||
dest_pool = next((x for x in pools if x['name'] != share['host']),
|
||||
None)
|
||||
|
||||
self.assertIsNotNone(dest_pool)
|
||||
|
||||
dest_pool = dest_pool['name']
|
||||
|
||||
old_export_location = share['export_locations'][0]
|
||||
|
||||
share = self.migrate_share(share['id'], dest_pool)
|
||||
|
||||
self.assertEqual(dest_pool, share['host'])
|
||||
self.assertNotEqual(old_export_location, share['export_locations'][0])
|
||||
self.assertEqual('migration_success', share['task_state'])
|
@ -303,6 +303,13 @@ class BaseSharesTest(test.BaseTestCase):
|
||||
cleanup_list.insert(0, resource)
|
||||
return share
|
||||
|
||||
@classmethod
|
||||
def migrate_share(cls, share_id, dest_host, client=None):
|
||||
client = client or cls.shares_client
|
||||
client.migrate_share(share_id, dest_host)
|
||||
share = client.wait_for_migration_completed(share_id, dest_host)
|
||||
return share
|
||||
|
||||
@classmethod
|
||||
def create_share(cls, *args, **kwargs):
|
||||
"""Create one share and wait for available state. Retry if allowed."""
|
||||
|
@ -116,7 +116,7 @@ class ShareScenarioTest(manager.NetworkScenarioTest):
|
||||
return sn
|
||||
|
||||
def _allow_access(self, share_id, client=None,
|
||||
access_type="ip", access_to="0.0.0.0"):
|
||||
access_type="ip", access_to="0.0.0.0", cleanup=True):
|
||||
"""Allow share access
|
||||
|
||||
:param share_id: id of the share
|
||||
@ -128,8 +128,8 @@ class ShareScenarioTest(manager.NetworkScenarioTest):
|
||||
client = client or self.shares_client
|
||||
access = client.create_access_rule(share_id, access_type, access_to)
|
||||
client.wait_for_access_rule_status(share_id, access['id'], "active")
|
||||
self.addCleanup(client.delete_access_rule,
|
||||
share_id, access['id'])
|
||||
if cleanup:
|
||||
self.addCleanup(client.delete_access_rule, share_id, access['id'])
|
||||
return access
|
||||
|
||||
def _create_router_interface(self, subnet_id, client=None,
|
||||
@ -182,3 +182,9 @@ class ShareScenarioTest(manager.NetworkScenarioTest):
|
||||
raise
|
||||
|
||||
return linux_client
|
||||
|
||||
def _migrate_share(self, share_id, dest_host, client=None):
|
||||
client = client or self.shares_client
|
||||
client.migrate_share(share_id, dest_host)
|
||||
share = client.wait_for_migration_completed(share_id, dest_host)
|
||||
return share
|
||||
|
@ -116,6 +116,11 @@ class ShareBasicOpsBase(manager.ShareScenarioTest):
|
||||
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,
|
||||
self.shares_admin_client)
|
||||
return share
|
||||
|
||||
def create_share_network(self):
|
||||
self.net = self._create_network(namestart="manila-share")
|
||||
self.subnet = self._create_subnet(network=self.net,
|
||||
@ -132,7 +137,7 @@ class ShareBasicOpsBase(manager.ShareScenarioTest):
|
||||
self.share = self._create_share(share_protocol=self.protocol,
|
||||
share_network_id=share_net_id)
|
||||
|
||||
def allow_access_ip(self, share_id, ip=None, instance=None):
|
||||
def allow_access_ip(self, share_id, ip=None, instance=None, cleanup=True):
|
||||
if instance and not ip:
|
||||
try:
|
||||
net_addresses = instance['addresses']
|
||||
@ -144,7 +149,8 @@ class ShareBasicOpsBase(manager.ShareScenarioTest):
|
||||
"Falling back to default")
|
||||
if not ip:
|
||||
ip = '0.0.0.0/0'
|
||||
self._allow_access(share_id, access_type='ip', access_to=ip)
|
||||
self._allow_access(share_id, access_type='ip', access_to=ip,
|
||||
cleanup=cleanup)
|
||||
|
||||
@test.services('compute', 'network')
|
||||
def test_mount_share_one_vm(self):
|
||||
@ -187,6 +193,84 @@ class ShareBasicOpsBase(manager.ShareScenarioTest):
|
||||
data = self.read_data(ssh_client_inst2)
|
||||
self.assertEqual(test_data, data)
|
||||
|
||||
@test.services('compute', 'network')
|
||||
def test_migration_files(self):
|
||||
|
||||
if self.protocol == "CIFS":
|
||||
raise self.skipException("Test for CIFS protocol not supported "
|
||||
"at this moment. Skipping.")
|
||||
|
||||
if not CONF.share.migration_enabled:
|
||||
raise self.skipException("Migration tests disabled. Skipping.")
|
||||
|
||||
pools = self.shares_admin_client.list_pools()['pools']
|
||||
|
||||
if len(pools) < 2:
|
||||
raise self.skipException("At least two different running "
|
||||
"manila-share services are needed to "
|
||||
"run migration tests. Skipping.")
|
||||
|
||||
self.security_group = self._create_security_group()
|
||||
self.create_share_network()
|
||||
self.create_share(self.share_net['id'])
|
||||
share = self.shares_client.get_share(self.share['id'])
|
||||
|
||||
dest_pool = next((x for x in pools if x['name'] != share['host']),
|
||||
None)
|
||||
|
||||
self.assertIsNotNone(dest_pool)
|
||||
|
||||
dest_pool = dest_pool['name']
|
||||
|
||||
old_export_location = share['export_locations'][0]
|
||||
|
||||
instance1 = self.boot_instance(self.net)
|
||||
self.allow_access_ip(self.share['id'], instance=instance1,
|
||||
cleanup=False)
|
||||
ssh_client = self.init_ssh(instance1)
|
||||
first_location = self.share['export_locations'][0]
|
||||
self.mount_share(first_location, ssh_client)
|
||||
|
||||
ssh_client.exec_command("mkdir -p /mnt/f1")
|
||||
ssh_client.exec_command("mkdir -p /mnt/f2")
|
||||
ssh_client.exec_command("mkdir -p /mnt/f3")
|
||||
ssh_client.exec_command("mkdir -p /mnt/f4")
|
||||
ssh_client.exec_command("mkdir -p /mnt/f1/ff1")
|
||||
ssh_client.exec_command("sleep 1")
|
||||
ssh_client.exec_command("dd if=/dev/zero of=/mnt/f1/1m1.bin bs=1M"
|
||||
" count=1")
|
||||
ssh_client.exec_command("dd if=/dev/zero of=/mnt/f2/1m2.bin bs=1M"
|
||||
" count=1")
|
||||
ssh_client.exec_command("dd if=/dev/zero of=/mnt/f3/1m3.bin bs=1M"
|
||||
" count=1")
|
||||
ssh_client.exec_command("dd if=/dev/zero of=/mnt/f4/1m4.bin bs=1M"
|
||||
" count=1")
|
||||
ssh_client.exec_command("dd if=/dev/zero of=/mnt/f1/ff1/1m5.bin bs=1M"
|
||||
" count=1")
|
||||
ssh_client.exec_command("chmod -R 555 /mnt/f3")
|
||||
ssh_client.exec_command("chmod -R 777 /mnt/f4")
|
||||
|
||||
self.umount_share(ssh_client)
|
||||
|
||||
share = self.migrate_share(share['id'], dest_pool)
|
||||
|
||||
self.assertEqual(dest_pool, share['host'])
|
||||
self.assertNotEqual(old_export_location, share['export_locations'][0])
|
||||
self.assertEqual('migration_success', share['task_state'])
|
||||
|
||||
second_location = share['export_locations'][0]
|
||||
self.mount_share(second_location, ssh_client)
|
||||
|
||||
output = ssh_client.exec_command("ls -lRA --ignore=lost+found /mnt")
|
||||
|
||||
self.umount_share(ssh_client)
|
||||
|
||||
self.assertTrue('1m1.bin' in output)
|
||||
self.assertTrue('1m2.bin' in output)
|
||||
self.assertTrue('1m3.bin' in output)
|
||||
self.assertTrue('1m4.bin' in output)
|
||||
self.assertTrue('1m5.bin' in output)
|
||||
|
||||
|
||||
class TestShareBasicOpsNFS(ShareBasicOpsBase):
|
||||
protocol = "NFS"
|
||||
|
Loading…
x
Reference in New Issue
Block a user