# Copyright 2019 Red Hat, Inc. # Copyright 2012 OpenStack Foundation # 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 oslo_log import log as logging import testtools import xml.etree.ElementTree as ET from tempest.common import utils from tempest.common import waiters from tempest import config from tempest.lib import decorators from whitebox_tempest_plugin.api.compute import base from whitebox_tempest_plugin.common import utils as whitebox_utils from whitebox_tempest_plugin.services import clients CONF = config.CONF LOG = logging.getLogger(__name__) # NOTE(mdbooth): This test was originally based on # tempest.api.compute.admin.test_live_migration class LiveMigrationTest(base.BaseTest): # First support for block_migration='auto': since Mitaka (OSP9) min_microversion = '2.25' @classmethod def skip_checks(cls): super(LiveMigrationTest, cls).skip_checks() if not CONF.compute_feature_enabled.live_migration: skip_msg = ("%s skipped as live-migration is " "not available" % cls.__name__) raise cls.skipException(skip_msg) if CONF.compute.min_compute_nodes < 2: raise cls.skipException( "Less than 2 compute nodes, skipping migration test.") @classmethod def setup_credentials(cls): # These tests don't attempt any SSH validation nor do they use # floating IPs on the instance, so all we need is a network and # a subnet so the instance being migrated has a single port, but # we need that to make sure we are properly updating the port # host bindings during the live migration. # TODO(mriedem): SSH validation before and after the instance is # live migrated would be a nice test wrinkle addition. cls.set_network_resources(network=True, subnet=True) super(LiveMigrationTest, cls).setup_credentials() @classmethod def setup_clients(cls): super(LiveMigrationTest, cls).setup_clients() cls.admin_migration_client = cls.os_admin.migrations_client def _live_migrate(self, server_id, target_host, state): self.admin_servers_client.live_migrate_server( server_id, host=target_host, block_migration='auto') waiters.wait_for_server_status(self.servers_client, server_id, state) migration_list = (self.admin_migration_client.list_migrations() ['migrations']) msg = ("Live Migration failed. Migrations list for Instance " "%s: [" % server_id) for live_migration in migration_list: if (live_migration['instance_uuid'] == server_id): msg += "\n%s" % live_migration msg += "]" self.assertEqual(target_host, self.get_host_for_server(server_id), msg) def _get_server_libvirt_domain(self, server_id): compute_node_address = whitebox_utils.get_hypervisor_ip( self.servers_client, server_id) LOG.debug("Connecting to hypervisor %s for server %s", compute_node_address, server_id) virshxml = clients.VirshXMLClient(compute_node_address) xml = virshxml.dumpxml(server_id) return ET.fromstring(xml) @testtools.skipUnless(CONF.compute_feature_enabled. volume_backed_live_migration, 'Volume-backed live migration not available') @decorators.idempotent_id('41e92884-ed04-42da-89fc-ef8922646542') @utils.services('volume') def test_volume_backed_live_migration(self): # Live migrate an instance to another host server_id = self.create_test_server(wait_until="ACTIVE", volume_backed=True)['id'] def root_disk_cache(): domain = self._get_server_libvirt_domain(server_id) return domain.find( "devices/disk/target[@dev='vda']/../driver").attrib['cache'] # The initial value of disk cache depends on config and the storage in # use. We can't guess it, so fetch it before we start. cache_type = root_disk_cache() source_host = self.get_host_for_server(server_id) destination_host = self.get_host_other_than(server_id) LOG.info("Live migrate from source %s to destination %s", source_host, destination_host) self._live_migrate(server_id, destination_host, 'ACTIVE') # Assert cache-mode has not changed during live migration self.assertEqual(cache_type, root_disk_cache())