393 lines
15 KiB
Python
393 lines
15 KiB
Python
# 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 copy
|
|
import fixtures
|
|
|
|
from oslo_utils.fixture import uuidsentinel
|
|
|
|
from nova import context as nova_context
|
|
from nova import exception
|
|
from nova import objects
|
|
from nova.tests.functional.libvirt import base
|
|
from nova.virt.libvirt import machine_type_utils
|
|
|
|
|
|
class LibvirtMachineTypeTest(base.ServersTestBase):
|
|
|
|
microversion = 'latest'
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.context = nova_context.get_admin_context()
|
|
|
|
# Add the q35 image to the glance fixture
|
|
hw_machine_type_q35_image = copy.deepcopy(self.glance.image1)
|
|
hw_machine_type_q35_image['id'] = uuidsentinel.q35_image_id
|
|
hw_machine_type_q35_image['properties']['hw_machine_type'] = 'q35'
|
|
self.glance.create(self.context, hw_machine_type_q35_image)
|
|
|
|
# Create a pass-through mock around _get_guest_config to capture the
|
|
# config of an instance so we can assert things about it later.
|
|
# TODO(lyarwood): This seems like a useful thing to do in the libvirt
|
|
# func tests for all computes we start?
|
|
self.start_compute()
|
|
self.guest_configs = {}
|
|
orig_get_config = self.computes['compute1'].driver._get_guest_config
|
|
|
|
def _get_guest_config(_self, *args, **kwargs):
|
|
guest_config = orig_get_config(*args, **kwargs)
|
|
instance = args[0]
|
|
self.guest_configs[instance.uuid] = guest_config
|
|
return self.guest_configs[instance.uuid]
|
|
|
|
self.useFixture(fixtures.MonkeyPatch(
|
|
'nova.virt.libvirt.LibvirtDriver._get_guest_config',
|
|
_get_guest_config))
|
|
|
|
def _create_servers(self):
|
|
server_with = self._create_server(
|
|
image_uuid=uuidsentinel.q35_image_id,
|
|
networks='none',
|
|
)
|
|
server_without = self._create_server(
|
|
image_uuid=self.glance.image1['id'],
|
|
networks='none',
|
|
)
|
|
return (server_with, server_without)
|
|
|
|
def _assert_machine_type(self, server_id, expected_machine_type):
|
|
instance = objects.Instance.get_by_uuid(self.context, server_id)
|
|
self.assertEqual(
|
|
expected_machine_type,
|
|
instance.image_meta.properties.hw_machine_type
|
|
)
|
|
self.assertEqual(
|
|
expected_machine_type,
|
|
instance.system_metadata['image_hw_machine_type']
|
|
)
|
|
self.assertEqual(
|
|
expected_machine_type,
|
|
self.guest_configs[server_id].os_mach_type
|
|
)
|
|
|
|
def _unset_machine_type(self, server_id):
|
|
instance = objects.Instance.get_by_uuid(
|
|
self.context,
|
|
server_id,
|
|
)
|
|
instance.system_metadata.pop('image_hw_machine_type')
|
|
instance.save()
|
|
|
|
def test_init_host_register_machine_type(self):
|
|
"""Assert that the machine type of an instance is recorded during
|
|
init_host if not already captured by an image prop.
|
|
"""
|
|
self.flags(hw_machine_type='x86_64=pc', group='libvirt')
|
|
|
|
server_with, server_without = self._create_servers()
|
|
self._assert_machine_type(server_with['id'], 'q35')
|
|
self._assert_machine_type(server_without['id'], 'pc')
|
|
|
|
# Stop n-cpu and clear the recorded machine type from server_without to
|
|
# allow init_host to register the machine type.
|
|
self.computes['compute1'].stop()
|
|
self._unset_machine_type(server_without['id'])
|
|
|
|
self.flags(hw_machine_type='x86_64=pc-q35-1.2.3', group='libvirt')
|
|
|
|
# Restart the compute
|
|
self.computes['compute1'].start()
|
|
|
|
# Assert the server_with remains pinned to q35
|
|
self._assert_machine_type(server_with['id'], 'q35')
|
|
|
|
# reboot the server so the config is rebuilt and _assert_machine_type
|
|
# is able to pass. This just keeps the tests clean.
|
|
self._reboot_server(server_without, hard=True)
|
|
|
|
# Assert server_without now has a machine type of pc-q35-1.2.3 picked
|
|
# up from [libvirt]hw_machine_type during init_host
|
|
self._assert_machine_type(server_without['id'], 'pc-q35-1.2.3')
|
|
|
|
def test_machine_type_after_config_change(self):
|
|
"""Assert new instances pick up a new default machine type after the
|
|
config has been updated.
|
|
"""
|
|
self.flags(hw_machine_type='x86_64=pc', group='libvirt')
|
|
|
|
server_with, server_without = self._create_servers()
|
|
self._assert_machine_type(server_with['id'], 'q35')
|
|
self._assert_machine_type(server_without['id'], 'pc')
|
|
|
|
self.flags(hw_machine_type='x86_64=pc-q35-1.2.3', group='libvirt')
|
|
|
|
server_with_new, server_without_new = self._create_servers()
|
|
self._assert_machine_type(server_with_new['id'], 'q35')
|
|
self._assert_machine_type(server_without_new['id'], 'pc-q35-1.2.3')
|
|
|
|
def test_machine_type_after_server_rebuild(self):
|
|
"""Assert that the machine type of an instance changes with a full
|
|
rebuild of the instance pointing at a new image.
|
|
"""
|
|
self.flags(hw_machine_type='x86_64=pc', group='libvirt')
|
|
|
|
server_with, server_without = self._create_servers()
|
|
self._assert_machine_type(server_with['id'], 'q35')
|
|
self._assert_machine_type(server_without['id'], 'pc')
|
|
|
|
# rebuild each server with the opposite image
|
|
self._rebuild_server(
|
|
server_with,
|
|
'155d900f-4e14-4e4c-a73d-069cbf4541e6',
|
|
)
|
|
self._rebuild_server(
|
|
server_without,
|
|
uuidsentinel.q35_image_id
|
|
)
|
|
|
|
# Assert that the machine types were updated during the rebuild
|
|
self._assert_machine_type(server_with['id'], 'pc')
|
|
self._assert_machine_type(server_without['id'], 'q35')
|
|
|
|
def _test_machine_type_after_server_reboot(self, hard=False):
|
|
"""Assert that the recorded machine types don't change with the
|
|
reboot of a server, even when the underlying config changes.
|
|
"""
|
|
self.flags(hw_machine_type='x86_64=pc', group='libvirt')
|
|
|
|
server_with, server_without = self._create_servers()
|
|
self._assert_machine_type(server_with['id'], 'q35')
|
|
self._assert_machine_type(server_without['id'], 'pc')
|
|
|
|
self.flags(hw_machine_type='x86_64=pc-q35-1.2.3', group='libvirt')
|
|
|
|
self._reboot_server(server_with, hard=hard)
|
|
self._reboot_server(server_without, hard=hard)
|
|
|
|
# Assert that the machine types don't change after a reboot
|
|
self._assert_machine_type(server_with['id'], 'q35')
|
|
self._assert_machine_type(server_without['id'], 'pc')
|
|
|
|
def test_machine_type_after_server_soft_reboot(self):
|
|
self._test_machine_type_after_server_reboot()
|
|
|
|
def test_machine_type_after_server_hard_reboot(self):
|
|
self._test_machine_type_after_server_reboot(hard=True)
|
|
|
|
def test_machine_type_get(self):
|
|
self.flags(hw_machine_type='x86_64=pc', group='libvirt')
|
|
|
|
server_with, server_without = self._create_servers()
|
|
self.assertEqual(
|
|
'q35',
|
|
machine_type_utils.get_machine_type(
|
|
self.context, server_with['id']
|
|
)
|
|
)
|
|
self.assertEqual(
|
|
'pc',
|
|
machine_type_utils.get_machine_type(
|
|
self.context, server_without['id']
|
|
)
|
|
)
|
|
|
|
def test_machine_type_update_stopped(self):
|
|
self.flags(hw_machine_type='x86_64=pc-1.2.3', group='libvirt')
|
|
|
|
server = self._create_server(networks='none')
|
|
self._assert_machine_type(server['id'], 'pc-1.2.3')
|
|
|
|
self._stop_server(server)
|
|
machine_type_utils.update_machine_type(
|
|
self.context,
|
|
server['id'],
|
|
'pc-1.2.4'
|
|
)
|
|
|
|
self._start_server(server)
|
|
self._assert_machine_type(server['id'], 'pc-1.2.4')
|
|
|
|
def test_machine_type_update_blocked_active(self):
|
|
self.flags(hw_machine_type='x86_64=pc-1.2.3', group='libvirt')
|
|
|
|
server = self._create_server(networks='none')
|
|
self._assert_machine_type(server['id'], 'pc-1.2.3')
|
|
|
|
self.assertRaises(
|
|
exception.InstanceInvalidState,
|
|
machine_type_utils.update_machine_type,
|
|
self.context,
|
|
server['id'],
|
|
'pc-1.2.4'
|
|
)
|
|
|
|
def test_machine_type_update_blocked_between_alias_and_versioned(self):
|
|
self.flags(hw_machine_type='x86_64=pc', group='libvirt')
|
|
|
|
server = self._create_server(networks='none')
|
|
self._assert_machine_type(server['id'], 'pc')
|
|
self._stop_server(server)
|
|
|
|
self.assertRaises(
|
|
exception.InvalidMachineTypeUpdate,
|
|
machine_type_utils.update_machine_type,
|
|
self.context,
|
|
server['id'],
|
|
'pc-1.2.4'
|
|
)
|
|
|
|
def test_machine_type_update_blocked_between_versioned_and_alias(self):
|
|
self.flags(hw_machine_type='x86_64=pc-1.2.3', group='libvirt')
|
|
|
|
server = self._create_server(networks='none')
|
|
self._assert_machine_type(server['id'], 'pc-1.2.3')
|
|
self._stop_server(server)
|
|
|
|
self.assertRaises(
|
|
exception.InvalidMachineTypeUpdate,
|
|
machine_type_utils.update_machine_type,
|
|
self.context,
|
|
server['id'],
|
|
'pc'
|
|
)
|
|
|
|
def test_machine_type_update_blocked_between_types(self):
|
|
self.flags(hw_machine_type='x86_64=pc', group='libvirt')
|
|
|
|
server = self._create_server(networks='none')
|
|
self._assert_machine_type(server['id'], 'pc')
|
|
self._stop_server(server)
|
|
|
|
self.assertRaises(
|
|
exception.InvalidMachineTypeUpdate,
|
|
machine_type_utils.update_machine_type,
|
|
self.context,
|
|
server['id'],
|
|
'q35'
|
|
)
|
|
|
|
def test_machine_type_update_force(self):
|
|
self.flags(hw_machine_type='x86_64=pc', group='libvirt')
|
|
|
|
server = self._create_server(networks='none')
|
|
self._assert_machine_type(server['id'], 'pc')
|
|
|
|
# Force through the update on an ACTIVE instance
|
|
machine_type_utils.update_machine_type(
|
|
self.context,
|
|
server['id'],
|
|
'q35',
|
|
force=True
|
|
)
|
|
|
|
# Reboot the server so the config is updated so we can assert
|
|
self._reboot_server(server, hard=True)
|
|
self._assert_machine_type(server['id'], 'q35')
|
|
|
|
def test_machine_type_list_unset_machine_type(self):
|
|
self.flags(hw_machine_type='x86_64=pc', group='libvirt')
|
|
|
|
server_with, server_without = self._create_servers()
|
|
self._unset_machine_type(server_without['id'])
|
|
|
|
instances = machine_type_utils.get_instances_without_type(self.context)
|
|
self.assertEqual(
|
|
server_without['id'],
|
|
instances[0].uuid
|
|
)
|
|
|
|
def test_hw_machine_type_config_update_workflow(self):
|
|
"""Mimic a real world [libvirt]hw_machine_type config update workflow
|
|
|
|
This test ties together various bits tested above into a complete
|
|
workflow that should be typical of an operator attempting to update
|
|
the host [libvirt]hw_machine_type configurable within their env.
|
|
"""
|
|
self.flags(hw_machine_type='x86_64=pc', group='libvirt')
|
|
|
|
# Launch some instances and assert their machine type
|
|
server_with, server_without = self._create_servers()
|
|
self._assert_machine_type(server_with['id'], 'q35')
|
|
self._assert_machine_type(server_without['id'], 'pc')
|
|
|
|
# Launch and shelve some additional instances
|
|
server_shelved_with, server_shelved_without = self._create_servers()
|
|
self._shelve_server(server_shelved_with)
|
|
self._shelve_server(server_shelved_without)
|
|
|
|
# Unset the machine type of the _without instances to mimic an existing
|
|
# instance without a machine type set
|
|
self._unset_machine_type(server_without['id'])
|
|
self._unset_machine_type(server_shelved_without['id'])
|
|
|
|
# Assert that both _without instances are listed by
|
|
# get_instances_without_type as used by `nova-manage machine_type
|
|
# list_unset` and `nova-status upgrade check`
|
|
instances = machine_type_utils.get_instances_without_type(self.context)
|
|
instance_uuids = [i.uuid for i in instances]
|
|
self.assertIn(
|
|
server_without['id'],
|
|
instance_uuids
|
|
)
|
|
self.assertIn(
|
|
server_shelved_without['id'],
|
|
instance_uuids
|
|
)
|
|
|
|
# Restart the compute service
|
|
self.computes['compute1'].stop()
|
|
self.computes['compute1'].start()
|
|
|
|
# Assert that after a service restart only server_shelved_without
|
|
# remains listed by get_instances_without_type as init_host has
|
|
# populated a machine type for server_without
|
|
instances = machine_type_utils.get_instances_without_type(self.context)
|
|
self.assertEqual(
|
|
server_shelved_without['id'],
|
|
instances[0].uuid
|
|
)
|
|
|
|
# Manually update the machine type of server_shelved_without using
|
|
# machine_type_utils as used by `nova-manage machine_type update`
|
|
machine_type_utils.update_machine_type(
|
|
self.context,
|
|
server_shelved_without['id'],
|
|
'pc',
|
|
)
|
|
|
|
# Assert that no instances are listed so we can go ahead and change the
|
|
# [libvirt]hw_machine_type config on the compute
|
|
self.assertFalse(
|
|
machine_type_utils.get_instances_without_type(self.context)
|
|
)
|
|
|
|
# Change the actual config on the compute
|
|
self.flags(hw_machine_type='x86_64=pc-q35-1.2', group='libvirt')
|
|
|
|
# Assert the existing instances remain the same after being rebooted or
|
|
# unshelved, rebuilding their domain configs
|
|
self._reboot_server(server_with, hard=True)
|
|
self._reboot_server(server_without, hard=True)
|
|
self._assert_machine_type(server_with['id'], 'q35')
|
|
self._assert_machine_type(server_without['id'], 'pc')
|
|
|
|
self._unshelve_server(server_shelved_with)
|
|
self._unshelve_server(server_shelved_without)
|
|
self._assert_machine_type(server_shelved_with['id'], 'q35')
|
|
self._assert_machine_type(server_shelved_without['id'], 'pc')
|
|
|
|
# Assert that new instances are spawned with the expected machine types
|
|
server_with_new, server_without_new = self._create_servers()
|
|
self._assert_machine_type(server_with_new['id'], 'q35')
|
|
self._assert_machine_type(server_without_new['id'], 'pc-q35-1.2')
|