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:
Rodrigo Barbieri 2015-09-06 22:53:16 -03:00
parent f5add61a8d
commit 740fc64940
8 changed files with 231 additions and 5 deletions

View File

@ -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

View File

@ -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."),
]

View File

@ -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)

View File

@ -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'."

View 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'])

View File

@ -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."""

View File

@ -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

View File

@ -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"