Skip to content

Commit 19c4f8e

Browse files
MrStupnikovkajinamit
authored andcommitted
Test aborting queued live migration
This patch adds a regression test which asserts that if a live migration is aborted while it's 'queued', the instance's status is never reverted back to ACTIVE, and instance remains in MIGRATING state. There is simple idea behind implemented LiveMigrationQueuedAbortTest: we start two instances on the same compute and try to migrate them simultaneously when max_concurrent_live_migrations is set to 1 and nova.tests.fixtures.libvirt.Domain.migrateToURI3 is locked. As a result, we get two live migrations stuck in 'migrating' and 'queued' states and we can issue API call to abort the second one. Lock is removed and first instance is live migrated after second instance's live migration is aborted. Co-Authored-By: Alex Stupnikov <[email protected]> Partial-Bug: #1949808 Change-Id: I67d41a8e439b1ff3c5983ee17823616b80698639 (cherry picked from commit 3e7b9b6)
1 parent af3d43d commit 19c4f8e

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Copyright 2021 Red Hat, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
import threading
16+
17+
from lxml import etree
18+
from nova.tests.functional import integrated_helpers
19+
from nova.tests.functional.libvirt import base as libvirt_base
20+
21+
22+
class LiveMigrationQueuedAbortTest(
23+
libvirt_base.LibvirtMigrationMixin,
24+
libvirt_base.ServersTestBase,
25+
integrated_helpers.InstanceHelperMixin
26+
):
27+
"""Functional test for bug 1949808.
28+
29+
This test is used to confirm that VM's state is reverted properly
30+
when queued Live migration is aborted.
31+
"""
32+
33+
api_major_version = 'v2.1'
34+
microversion = '2.74'
35+
ADMIN_API = True
36+
37+
def setUp(self):
38+
super().setUp()
39+
40+
# We will allow only one live migration to be processed at any
41+
# given period of time
42+
self.flags(max_concurrent_live_migrations='1')
43+
self.src_hostname = self.start_compute(hostname='src')
44+
self.dest_hostname = self.start_compute(hostname='dest')
45+
46+
self.src = self.computes[self.src_hostname]
47+
self.dest = self.computes[self.dest_hostname]
48+
49+
# Live migration's execution could be locked if needed
50+
self.lock_live_migration = threading.Lock()
51+
52+
def _migrate_stub(self, domain, destination, params, flags):
53+
# Execute only if live migration is not locked
54+
with self.lock_live_migration:
55+
self.dest.driver._host.get_connection().createXML(
56+
params['destination_xml'],
57+
'fake-createXML-doesnt-care-about-flags')
58+
conn = self.src.driver._host.get_connection()
59+
60+
# Because migrateToURI3 is spawned in a background thread,
61+
# this method does not block the upper nova layers. Because
62+
# we don't want nova to think the live migration has
63+
# finished until this method is done, the last thing we do
64+
# is make fakelibvirt's Domain.jobStats() return
65+
# VIR_DOMAIN_JOB_COMPLETED.
66+
server = etree.fromstring(
67+
params['destination_xml']
68+
).find('./uuid').text
69+
dom = conn.lookupByUUIDString(server)
70+
dom.complete_job()
71+
72+
def test_queued_live_migration_abort(self):
73+
# Lock live migrations
74+
self.lock_live_migration.acquire()
75+
76+
# Start instances: first one would be used to occupy
77+
# executor's live migration queue, second one would be used
78+
# to actually confirm that queued live migrations are
79+
# aborted properly.
80+
self.server_a = self._create_server(
81+
host=self.src_hostname, networks='none')
82+
self.server_b = self._create_server(
83+
host=self.src_hostname, networks='none')
84+
# Issue live migration requests for both servers. We expect that
85+
# server_a live migration would be running, but locked by
86+
# self.lock_live_migration and server_b live migration would be
87+
# queued.
88+
self._live_migrate(
89+
self.server_a,
90+
migration_expected_state='running',
91+
server_expected_state='MIGRATING'
92+
)
93+
self._live_migrate(
94+
self.server_b,
95+
migration_expected_state='queued',
96+
server_expected_state='MIGRATING'
97+
)
98+
99+
# Abort live migration for server_b
100+
serverb_migration = self.api.api_get(
101+
'/os-migrations?instance_uuid=%s' % self.server_b['id']
102+
).body['migrations'].pop()
103+
104+
self.api.api_delete(
105+
'/servers/%s/migrations/%s' % (self.server_b['id'],
106+
serverb_migration['id']))
107+
self._wait_for_migration_status(self.server_b, ['cancelled'])
108+
# Unlock live migrations and confirm that server_a becomes
109+
# active again after successful live migration
110+
self.lock_live_migration.release()
111+
self._wait_for_state_change(self.server_a, 'ACTIVE')
112+
113+
# FIXME(artom) Assert the server_b never comes out of 'MIGRATING'
114+
self.assertRaises(
115+
AssertionError,
116+
self._wait_for_state_change, self.server_b, 'ACTIVE')
117+
self._wait_for_state_change(self.server_b, 'MIGRATING')

0 commit comments

Comments
 (0)