Skip to content

Commit a84083c

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Add a regression test for bug 1939545" into stable/wallaby
2 parents c1da58d + 9f7b81c commit a84083c

File tree

2 files changed

+151
-15
lines changed

2 files changed

+151
-15
lines changed

nova/tests/fixtures.py

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2435,6 +2435,26 @@ class CinderFixture(fixtures.Fixture):
24352435
# as part of volume image metadata
24362436
IMAGE_WITH_TRAITS_BACKED_VOL = '6194fc02-c60e-4a01-a8e5-600798208b5f'
24372437

2438+
# This represents a bootable volume backed by iSCSI storage.
2439+
ISCSI_BACKED_VOL = uuidsentinel.iscsi_backed_volume
2440+
2441+
# Dict of connection_info for the above volumes key'd by the volume id
2442+
# TODO(lyarwood): Make this unique and tracked per attachment somehow.
2443+
VOLUME_CONNECTION_INFO = {
2444+
uuidsentinel.iscsi_backed_volume: {
2445+
'driver_volume_type': 'iscsi',
2446+
'data': {
2447+
'target_lun': '1'
2448+
}
2449+
},
2450+
'fake': {
2451+
'driver_volume_type': 'fake',
2452+
'data': {
2453+
'foo': 'bar',
2454+
}
2455+
}
2456+
}
2457+
24382458
def __init__(self, test, az='nova'):
24392459
"""Initialize this instance of the CinderFixture.
24402460
@@ -2593,7 +2613,14 @@ def fake_attachment_create(_self, context, volume_id, instance_uuid,
25932613
attachment_id = uuidutils.generate_uuid()
25942614
if self.attachment_error_id is not None:
25952615
attachment_id = self.attachment_error_id
2596-
attachment = {'id': attachment_id, 'connection_info': {'data': {}}}
2616+
2617+
attachment = {'id': attachment_id}
2618+
2619+
if connector:
2620+
connection_info = self.VOLUME_CONNECTION_INFO.get(
2621+
volume_id, self.VOLUME_CONNECTION_INFO.get('fake'))
2622+
attachment['connection_info'] = copy.deepcopy(connection_info)
2623+
25972624
self.volume_to_attachment[volume_id][attachment_id] = {
25982625
'id': attachment_id,
25992626
'instance_uuid': instance_uuid,
@@ -2631,13 +2658,9 @@ def fake_attachment_update(_self, context, attachment_id, connector,
26312658
LOG.info('Updating volume attachment: %s', attachment_id)
26322659
attachment_ref = {
26332660
'id': attachment_id,
2634-
'connection_info': {
2635-
'driver_volume_type': 'fake',
2636-
'data': {
2637-
'foo': 'bar',
2638-
'target_lun': '1'
2639-
}
2640-
}
2661+
'connection_info': copy.deepcopy(
2662+
self.VOLUME_CONNECTION_INFO.get(
2663+
volume_id, self.VOLUME_CONNECTION_INFO.get('fake')))
26412664
}
26422665
if attachment_id == self.SWAP_ERR_ATTACH_ID:
26432666
# This intentionally triggers a TypeError for the
@@ -2646,13 +2669,15 @@ def fake_attachment_update(_self, context, attachment_id, connector,
26462669
return attachment_ref
26472670

26482671
def fake_attachment_get(_self, context, attachment_id):
2649-
# Ensure the attachment exists
2650-
_find_attachment(attachment_id)
2651-
attachment_ref = {'driver_volume_type': 'fake_type',
2652-
'id': attachment_id,
2653-
'connection_info': {'data':
2654-
{'foo': 'bar',
2655-
'target_lun': '1'}}}
2672+
# Ensure the attachment exists and grab the volume_id
2673+
volume_id, _, _ = _find_attachment(attachment_id)
2674+
2675+
attachment_ref = {
2676+
'id': attachment_id,
2677+
'connection_info': copy.deepcopy(
2678+
self.VOLUME_CONNECTION_INFO.get(
2679+
volume_id, self.VOLUME_CONNECTION_INFO.get('fake')))
2680+
}
26562681
return attachment_ref
26572682

26582683
def fake_get_all_volume_types(*args, **kwargs):
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Copyright 2021, Red Hat, Inc. All Rights Reserved.
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 fixtures
16+
import mock
17+
18+
from oslo_serialization import jsonutils
19+
20+
from nova import context
21+
from nova import objects
22+
from nova.tests.functional import integrated_helpers
23+
from nova.tests.functional.libvirt import base
24+
from nova.tests.unit.virt.libvirt import fake_os_brick_connector
25+
from nova.tests.unit.virt.libvirt import fakelibvirt
26+
27+
28+
class TestLiveMigrateUpdateDevicePath(
29+
base.ServersTestBase,
30+
integrated_helpers.InstanceHelperMixin
31+
):
32+
"""Regression test for bug 1939545
33+
34+
Assert the behaviour of the libvirt driver during pre_live_migration with
35+
instances that have block based volumes attached.
36+
37+
Bug #1939545 covering the case where the returned path from os-brick
38+
isn't being saved into the connection_info of the associated bdm in Nova.
39+
"""
40+
41+
api_major_version = 'v2.1'
42+
microversion = 'latest'
43+
ADMIN_API = True
44+
45+
def setUp(self):
46+
super().setUp()
47+
48+
self.useFixture(fixtures.MonkeyPatch(
49+
'nova.virt.libvirt.driver.connector',
50+
fake_os_brick_connector))
51+
52+
# TODO(lyarwood): Move into base.ServersTestBase to allow live
53+
# migrations to pass without changes by the test classes.
54+
self.useFixture(fixtures.MonkeyPatch(
55+
'nova.tests.unit.virt.libvirt.fakelibvirt.Domain.migrateToURI3',
56+
self._migrate_stub))
57+
58+
self.start_compute(
59+
hostname='src',
60+
host_info=fakelibvirt.HostInfo(
61+
cpu_nodes=1, cpu_sockets=1, cpu_cores=4, cpu_threads=1))
62+
self.start_compute(
63+
hostname='dest',
64+
host_info=fakelibvirt.HostInfo(
65+
cpu_nodes=1, cpu_sockets=1, cpu_cores=4, cpu_threads=1))
66+
67+
def _migrate_stub(self, domain, destination, params, flags):
68+
dest = self.computes['dest']
69+
dest.driver._host.get_connection().createXML(
70+
params['destination_xml'],
71+
'fake-createXML-doesnt-care-about-flags')
72+
source = self.computes['src']
73+
conn = source.driver._host.get_connection()
74+
dom = conn.lookupByUUIDString(self.server['id'])
75+
dom.complete_job()
76+
77+
# TODO(lyarwood): Move this into the os-brick fixture and repeat for all
78+
# provided connectors at runtime.
79+
@mock.patch(
80+
'os_brick.initiator.connectors.iscsi.ISCSIConnector.connect_volume')
81+
@mock.patch(
82+
'os_brick.initiator.connectors.iscsi.ISCSIConnector.disconnect_volume')
83+
def test_live_migrate_update_device_path(
84+
self, mock_disconnect_volume, mock_connect_volume
85+
):
86+
self.server = self._create_server(host='src', networks='none')
87+
volume_id = self.cinder.ISCSI_BACKED_VOL
88+
89+
# TODO(lyarwood): As above, move this into the os-brick fixture.
90+
mock_connect_volume.return_value = {'path': '/dev/sda'}
91+
92+
self.api.post_server_volume(
93+
self.server['id'], {'volumeAttachment': {'volumeId': volume_id}})
94+
95+
# Get the volume bdm and assert that the connection_info is set with
96+
# device_path from the volume driver.
97+
ctxt = context.get_admin_context()
98+
bdm = objects.BlockDeviceMapping.get_by_volume_and_instance(
99+
ctxt, volume_id, self.server['id'])
100+
connection_info = jsonutils.loads(bdm.connection_info)
101+
self.assertIn('device_path', connection_info.get('data'))
102+
103+
# Live migrate the instance to another host
104+
self._live_migrate(self.server)
105+
106+
# FIXME(lyarwood): This is bug #1939545, again get the volume bdm and
107+
# assert that the saved connection_info doesn't contain device_path.
108+
bdm = objects.BlockDeviceMapping.get_by_volume_and_instance(
109+
ctxt, volume_id, self.server['id'])
110+
connection_info = jsonutils.loads(bdm.connection_info)
111+
self.assertNotIn('device_path', connection_info.get('data'))

0 commit comments

Comments
 (0)