Skip to content

Commit 7ecdfb6

Browse files
lyarwoodmelwitt
authored andcommitted
libvirt: Register defaults for undefined hw image properties
Currently, device bus and model types defined as image properties associated with an instance are always used when launching instances with the libvirt driver. When these types are not defined as image properties, their values either come from libosinfo or those directly hardcoded into the libvirt driver. This means that any changes to the defaults provided by libosinfo or the libvirt driver could result in unforeseen changes to existing instances. This has been encountered in the past as libosinfo assumes that libvirt domain definitions are static when OpenStack Nova specifically rewrites and redefines these domains during a hard reboot or migration allowing changes to possibly occur. This adds persistence of device bus and model type defaults to the instance's system metadata so that they will remain stable across reboots and migrations. Co-Authored-By: melanie witt <[email protected]> Blueprint: libvirt-device-bus-model-update Change-Id: I44d41a134a7fab638e2ea88e7ae86d25070e8a43
1 parent b502989 commit 7ecdfb6

File tree

4 files changed

+681
-95
lines changed

4 files changed

+681
-95
lines changed
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
13+
import datetime
14+
from unittest import mock
15+
16+
from oslo_utils.fixture import uuidsentinel as uuids
17+
18+
from nova import context as nova_context
19+
from nova import objects
20+
from nova import test
21+
from nova.tests.functional.libvirt import base
22+
from nova.virt.libvirt import driver as libvirt_driver
23+
24+
25+
class LibvirtDeviceBusMigration(base.ServersTestBase):
26+
27+
microversion = 'latest'
28+
# needed for move operations
29+
ADMIN_API = True
30+
31+
def setUp(self):
32+
super().setUp()
33+
self.context = nova_context.get_admin_context()
34+
self.compute_hostname = self.start_compute()
35+
self.compute = self.computes[self.compute_hostname]
36+
37+
def _unset_stashed_image_properties(self, server_id, properties):
38+
instance = objects.Instance.get_by_uuid(self.context, server_id)
39+
for p in properties:
40+
instance.system_metadata.pop(f'image_{p}')
41+
instance.save()
42+
43+
def _assert_stashed_image_properties(self, server_id, properties):
44+
instance = objects.Instance.get_by_uuid(self.context, server_id)
45+
for p, value in properties.items():
46+
self.assertEqual(instance.system_metadata.get(f'image_{p}'), value)
47+
48+
def _assert_stashed_image_properties_persist(self, server, properties):
49+
# Assert the stashed properties persist across a host reboot
50+
self.restart_compute_service(self.compute)
51+
self._assert_stashed_image_properties(server['id'], properties)
52+
53+
# Assert the stashed properties persist across a guest reboot
54+
self._reboot_server(server, hard=True)
55+
self._assert_stashed_image_properties(server['id'], properties)
56+
57+
# Assert the stashed properties persist across a migration
58+
if 'other_compute' not in self.computes:
59+
self.start_compute('other_compute')
60+
# TODO(stephenfin): The mock of 'migrate_disk_and_power_off' should
61+
# probably be less...dumb
62+
with mock.patch(
63+
'nova.virt.libvirt.driver.LibvirtDriver'
64+
'.migrate_disk_and_power_off', return_value='{}',
65+
):
66+
self._migrate_server(server)
67+
self._confirm_resize(server)
68+
self._assert_stashed_image_properties(server['id'], properties)
69+
70+
def test_default_image_property_registration(self):
71+
"""Assert that the defaults for various hw image properties don't
72+
change over the lifecycle of an instance.
73+
"""
74+
default_image_properties = {
75+
'hw_machine_type': 'pc',
76+
'hw_cdrom_bus': 'ide',
77+
'hw_disk_bus': 'virtio',
78+
'hw_input_bus': 'usb',
79+
'hw_pointer_model': 'usbtablet',
80+
'hw_video_model': 'virtio',
81+
'hw_vif_model': 'virtio',
82+
}
83+
84+
server = self._create_server(networks='none')
85+
self._assert_stashed_image_properties(
86+
server['id'], default_image_properties)
87+
88+
# Unset the defaults here to ensure that init_host resets them
89+
# when the compute restarts the libvirt driver
90+
self._unset_stashed_image_properties(
91+
server['id'], libvirt_driver.REGISTER_IMAGE_PROPERTY_DEFAULTS)
92+
93+
# Assert the defaults persist across a host reboot, guest reboot, and
94+
# guest migration
95+
self._assert_stashed_image_properties_persist(
96+
server, default_image_properties)
97+
98+
def test_non_default_image_property_registration(self):
99+
"""Assert that non-default values for various hw image properties
100+
don't change over the lifecycle of an instance.
101+
"""
102+
non_default_image_properties = {
103+
'hw_machine_type': 'q35',
104+
'hw_cdrom_bus': 'sata',
105+
'hw_disk_bus': 'sata',
106+
'hw_input_bus': 'virtio',
107+
'hw_video_model': 'qxl',
108+
'hw_vif_model': 'e1000',
109+
}
110+
self.glance.create(
111+
None,
112+
{
113+
'id': uuids.hw_bus_model_image_uuid,
114+
'name': 'hw_bus_model_image',
115+
'created_at': datetime.datetime(2011, 1, 1, 1, 2, 3),
116+
'updated_at': datetime.datetime(2011, 1, 1, 1, 2, 3),
117+
'deleted_at': None,
118+
'deleted': False,
119+
'status': 'active',
120+
'is_public': False,
121+
'container_format': 'bare',
122+
'disk_format': 'qcow2',
123+
'size': '74185822',
124+
'min_ram': 0,
125+
'min_disk': 0,
126+
'protected': False,
127+
'visibility': 'public',
128+
'tags': [],
129+
'properties': non_default_image_properties,
130+
}
131+
)
132+
server = self._create_server(
133+
networks='none', image_uuid=uuids.hw_bus_model_image_uuid)
134+
self._assert_stashed_image_properties(
135+
server['id'], non_default_image_properties)
136+
137+
# Assert the non defaults persist across a host reboot, guest reboot,
138+
# and guest migration
139+
self._assert_stashed_image_properties_persist(
140+
server, non_default_image_properties)
141+
142+
def test_default_image_property_persists_across_osinfo_changes(self):
143+
# Create a server with default image properties
144+
default_image_properties = {
145+
'hw_vif_model': 'virtio',
146+
'hw_disk_bus': 'virtio',
147+
}
148+
server = self._create_server(networks='none')
149+
self._assert_stashed_image_properties(
150+
server['id'], default_image_properties)
151+
152+
with test.nested(
153+
mock.patch('nova.virt.osinfo.HardwareProperties.network_model',
154+
new=mock.PropertyMock()),
155+
mock.patch('nova.virt.osinfo.HardwareProperties.disk_model',
156+
new=mock.PropertyMock())
157+
) as (mock_nw_model, mock_disk_model):
158+
# osinfo returning new things
159+
mock_nw_model.return_value = 'e1000'
160+
mock_disk_model.return_value = 'sata'
161+
162+
# Assert the defaults persist across a host reboot, guest reboot,
163+
# and guest migration
164+
self._assert_stashed_image_properties_persist(
165+
server, default_image_properties)
166+
167+
def test_default_image_property_persists_across_host_flag_changes(self):
168+
# Set the default to ps2 via host flag
169+
self.flags(pointer_model='ps2mouse')
170+
# Restart compute to pick up ps2 setting, which means the guest will
171+
# not get a prescribed pointer device
172+
self.restart_compute_service(self.compute)
173+
174+
# Create a server with default image properties
175+
default_image_properties1 = {
176+
'hw_pointer_model': None,
177+
'hw_input_bus': None,
178+
}
179+
server1 = self._create_server(networks='none')
180+
self._assert_stashed_image_properties(
181+
server1['id'], default_image_properties1)
182+
183+
# Assert the defaults persist across a host flag change
184+
self.flags(pointer_model='usbtablet')
185+
# Restart compute to pick up usb setting
186+
self.restart_compute_service(self.compute)
187+
self._assert_stashed_image_properties(
188+
server1['id'], default_image_properties1)
189+
190+
# Assert the defaults persist across a host reboot, guest reboot, and
191+
# guest migration
192+
self._assert_stashed_image_properties_persist(
193+
server1, default_image_properties1)
194+
195+
# Create a server with new default image properties since the host flag
196+
# change
197+
default_image_properties2 = {
198+
'hw_pointer_model': 'usbtablet',
199+
'hw_input_bus': 'usb',
200+
}
201+
server2 = self._create_server(networks='none')
202+
self._assert_stashed_image_properties(
203+
server2['id'], default_image_properties2)
204+
205+
# Assert the defaults persist across a host reboot, guest reboot, and
206+
# guest migration
207+
self._assert_stashed_image_properties_persist(
208+
server2, default_image_properties2)
209+
210+
# Finally, try changing the host flag again to None. Note that it is
211+
# not possible for a user to specify None for this option:
212+
# https://bugs.launchpad.net/nova/+bug/1866106
213+
self.flags(pointer_model=None)
214+
# Restart compute to pick up None setting
215+
self.restart_compute_service(self.compute)
216+
self._assert_stashed_image_properties(
217+
server1['id'], default_image_properties1)
218+
self._assert_stashed_image_properties(
219+
server2['id'], default_image_properties2)
220+
221+
# Create a server since the host flag change to None. The defaults
222+
# should be the same as for ps2mouse
223+
server3 = self._create_server(networks='none')
224+
self._assert_stashed_image_properties(
225+
server3['id'], default_image_properties1)
226+
227+
# Assert the defaults persist across a host reboot, guest reboot, and
228+
# guest migration for server1, server2, and server3
229+
self._assert_stashed_image_properties_persist(
230+
server1, default_image_properties1)
231+
self._assert_stashed_image_properties_persist(
232+
server2, default_image_properties2)
233+
self._assert_stashed_image_properties_persist(
234+
server3, default_image_properties1)

0 commit comments

Comments
 (0)