Merge "Test aborting queued live migration"
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
# Copyright 2021 Red Hat, Inc.
|
||||
#
|
||||
# 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 threading
|
||||
|
||||
from lxml import etree
|
||||
from nova.tests.functional import integrated_helpers
|
||||
from nova.tests.functional.libvirt import base as libvirt_base
|
||||
|
||||
|
||||
class LiveMigrationQueuedAbortTest(
|
||||
libvirt_base.LibvirtMigrationMixin,
|
||||
libvirt_base.ServersTestBase,
|
||||
integrated_helpers.InstanceHelperMixin
|
||||
):
|
||||
"""Functional test for bug 1949808.
|
||||
|
||||
This test is used to confirm that VM's state is reverted properly
|
||||
when queued Live migration is aborted.
|
||||
"""
|
||||
|
||||
api_major_version = 'v2.1'
|
||||
microversion = '2.74'
|
||||
ADMIN_API = True
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
# We will allow only one live migration to be processed at any
|
||||
# given period of time
|
||||
self.flags(max_concurrent_live_migrations='1')
|
||||
self.src_hostname = self.start_compute(hostname='src')
|
||||
self.dest_hostname = self.start_compute(hostname='dest')
|
||||
|
||||
self.src = self.computes[self.src_hostname]
|
||||
self.dest = self.computes[self.dest_hostname]
|
||||
|
||||
# Live migration's execution could be locked if needed
|
||||
self.lock_live_migration = threading.Lock()
|
||||
|
||||
def _migrate_stub(self, domain, destination, params, flags):
|
||||
# Execute only if live migration is not locked
|
||||
with self.lock_live_migration:
|
||||
self.dest.driver._host.get_connection().createXML(
|
||||
params['destination_xml'],
|
||||
'fake-createXML-doesnt-care-about-flags')
|
||||
conn = self.src.driver._host.get_connection()
|
||||
|
||||
# Because migrateToURI3 is spawned in a background thread,
|
||||
# this method does not block the upper nova layers. Because
|
||||
# we don't want nova to think the live migration has
|
||||
# finished until this method is done, the last thing we do
|
||||
# is make fakelibvirt's Domain.jobStats() return
|
||||
# VIR_DOMAIN_JOB_COMPLETED.
|
||||
server = etree.fromstring(
|
||||
params['destination_xml']
|
||||
).find('./uuid').text
|
||||
dom = conn.lookupByUUIDString(server)
|
||||
dom.complete_job()
|
||||
|
||||
def test_queued_live_migration_abort(self):
|
||||
# Lock live migrations
|
||||
self.lock_live_migration.acquire()
|
||||
|
||||
# Start instances: first one would be used to occupy
|
||||
# executor's live migration queue, second one would be used
|
||||
# to actually confirm that queued live migrations are
|
||||
# aborted properly.
|
||||
self.server_a = self._create_server(
|
||||
host=self.src_hostname, networks='none')
|
||||
self.server_b = self._create_server(
|
||||
host=self.src_hostname, networks='none')
|
||||
# Issue live migration requests for both servers. We expect that
|
||||
# server_a live migration would be running, but locked by
|
||||
# self.lock_live_migration and server_b live migration would be
|
||||
# queued.
|
||||
self._live_migrate(
|
||||
self.server_a,
|
||||
migration_expected_state='running',
|
||||
server_expected_state='MIGRATING'
|
||||
)
|
||||
self._live_migrate(
|
||||
self.server_b,
|
||||
migration_expected_state='queued',
|
||||
server_expected_state='MIGRATING'
|
||||
)
|
||||
|
||||
# Abort live migration for server_b
|
||||
serverb_migration = self.api.api_get(
|
||||
'/os-migrations?instance_uuid=%s' % self.server_b['id']
|
||||
).body['migrations'].pop()
|
||||
|
||||
self.api.api_delete(
|
||||
'/servers/%s/migrations/%s' % (self.server_b['id'],
|
||||
serverb_migration['id']))
|
||||
self._wait_for_migration_status(self.server_b, ['cancelled'])
|
||||
# Unlock live migrations and confirm that server_a becomes
|
||||
# active again after successful live migration
|
||||
self.lock_live_migration.release()
|
||||
self._wait_for_state_change(self.server_a, 'ACTIVE')
|
||||
|
||||
# FIXME(artom) Assert the server_b never comes out of 'MIGRATING'
|
||||
self.assertRaises(
|
||||
AssertionError,
|
||||
self._wait_for_state_change, self.server_b, 'ACTIVE')
|
||||
self._wait_for_state_change(self.server_b, 'MIGRATING')
|
||||
Reference in New Issue
Block a user