nova/nova/tests/functional/libvirt/test_machine_type.py

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')