[ironic] Factor out metadata and send to ironic

This change migrates the code currently written only to serve libvirt
driver to be generally useful, adding driver-neutral data structures
and a method to build them.

The libvirt driver is reworked to use get_instance_driver_metadata
instead of it's current code.

The ironic driver is reworked, per the blueprint, to send along some of
this additional metadata to the Ironic node.instance_info.

blueprint ironic-guest-metadata
Needed-By: https://review.opendev.org/c/openstack/ironic/+/924887

Change-Id: I2b23c8463f66c38e64625486157f245cd74cec61
This commit is contained in:
Jay Faulkner
2024-07-10 16:02:46 -07:00
parent eb5e3374bc
commit 93b90d2b6a
8 changed files with 387 additions and 115 deletions

View File

@@ -1263,6 +1263,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
instance_id=instance.uuid,
fields=ironic_driver._NODE_FIELDS)])
@mock.patch.object(ironic_driver.IronicDriver,
'get_instance_driver_metadata')
@mock.patch.object(objects.Instance, 'save')
@mock.patch.object(loopingcall, 'FixedIntervalLoopingCall')
@mock.patch.object(ironic_driver.IronicDriver, '_add_volume_target_info')
@@ -1271,11 +1273,15 @@ class IronicDriverTestCase(test.NoDBTestCase):
'_add_instance_info_to_node')
def _test_spawn(self, mock_aiitn, mock_wait_active,
mock_avti, mock_looping, mock_save,
config_drive_value=None):
mock_metadata, config_drive_value=None):
node_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = _get_cached_node(driver='fake', id=node_id)
instance = fake_instance.fake_instance_obj(self.ctx, node=node_id)
fake_flavor = objects.Flavor(ephemeral_gb=0)
mock_metadata.return_value = (
ironic_utils.get_test_instance_driver_metadata(
flavor_ephemeralgb=0)
)
instance.flavor = fake_flavor
self.mock_conn.get_node.return_value = node
@@ -1295,9 +1301,10 @@ class IronicDriverTestCase(test.NoDBTestCase):
self.mock_conn.validate_node.assert_called_once_with(
node_id, required=None,
)
mock_aiitn.assert_called_once_with(node, instance,
test.MatchType(objects.ImageMeta),
fake_flavor, block_device_info=None)
mock_aiitn.assert_called_once_with(
node, instance, test.MatchType(objects.ImageMeta),
fake_flavor, test.MatchType(driver.InstanceDriverMetadata),
block_device_info=None)
mock_avti.assert_called_once_with(self.ctx, instance, None)
self.mock_conn.set_node_provision_state.assert_called_once_with(
node_id, 'active', config_drive=config_drive_value,
@@ -1331,6 +1338,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
mock.ANY, extra_md={},
files=[])
@mock.patch.object(ironic_driver.IronicDriver,
'get_instance_driver_metadata')
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(loopingcall, 'FixedIntervalLoopingCall')
@mock.patch.object(ironic_driver.IronicDriver, 'destroy')
@@ -1341,13 +1350,18 @@ class IronicDriverTestCase(test.NoDBTestCase):
def test_spawn_destroyed_after_failure(self, mock_aiitn,
mock_wait_active, mock_avti,
mock_destroy,
mock_looping, mock_required_by):
mock_looping, mock_required_by,
mock_metadata):
mock_required_by.return_value = False
node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = _get_cached_node(driver='fake', id=node_uuid)
fake_flavor = objects.Flavor(ephemeral_gb=0)
instance = fake_instance.fake_instance_obj(self.ctx, node=node_uuid)
instance.flavor = fake_flavor
mock_metadata.return_value = (
ironic_utils.get_test_instance_driver_metadata(
flavor_ephemeralgb=0
))
self.mock_conn.get_node.return_value = node
self.mock_conn.validate_node.return_value = \
@@ -1368,6 +1382,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
instance = fake_instance.fake_instance_obj(self.ctx, node=node.id)
image_meta = ironic_utils.get_test_image_meta()
flavor = ironic_utils.get_test_flavor()
metadata = ironic_utils.get_test_instance_driver_metadata()
instance.flavor = flavor
expected_patch = [{'path': '/instance_info/image_source', 'op': 'add',
'value': image_meta.id},
@@ -1384,10 +1399,25 @@ class IronicDriverTestCase(test.NoDBTestCase):
{'path': '/instance_info/memory_mb', 'op': 'add',
'value': str(instance.flavor.memory_mb)},
{'path': '/instance_info/local_gb', 'op': 'add',
'value': str(node.properties.get('local_gb', 0))}]
'value': str(node.properties.get('local_gb', 0))},
{'path': '/instance_info/project_id', 'op': 'add',
'value': 'ppppppp-pppp-pppp-pppp-pppppppppppp'},
{'path': '/instance_info/project_name', 'op': 'add',
'value': 'testproject'},
{'path': '/instance_info/user_id', 'op': 'add',
'value': 'uuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu'},
{'op': 'add', 'path': '/instance_info/user_name',
'value': 'testuser'},
{'path': '/instance_info/flavor_name',
'op': 'add', 'value': 'fake.flavor'},
{'path': '/instance_info/fixed_ips',
'op': 'add', 'value': '[]'},
{'path': '/instance_info/floating_ips',
'op': 'add', 'value': '[]'},
]
self.driver._add_instance_info_to_node(node, instance,
image_meta, flavor)
image_meta, flavor, metadata)
# TODO(dustinc): Add check for call to patcher.create
self.mock_conn.patch_node.assert_called_once_with(node, expected_patch)
@@ -1533,16 +1563,22 @@ class IronicDriverTestCase(test.NoDBTestCase):
]
)
@mock.patch.object(ironic_driver.IronicDriver,
'get_instance_driver_metadata')
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(ironic_driver.IronicDriver, '_add_volume_target_info')
def test_spawn_node_driver_validation_fail(self, mock_avti,
mock_required_by):
mock_required_by,
mock_metadata):
mock_required_by.return_value = False
node_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = _get_cached_node(driver='fake', id=node_id)
flavor = ironic_utils.get_test_flavor()
instance = fake_instance.fake_instance_obj(self.ctx, node=node_id)
instance.flavor = flavor
mock_metadata.return_value = (
ironic_utils.get_test_instance_driver_metadata()
)
self.mock_conn.validate_node.return_value = \
ironic_utils.get_test_validation(
@@ -1562,20 +1598,23 @@ class IronicDriverTestCase(test.NoDBTestCase):
node_id, required=None,
)
@mock.patch.object(ironic_driver.IronicDriver,
'get_instance_driver_metadata')
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(objects.Instance, 'save')
@mock.patch.object(ironic_driver.IronicDriver, '_add_volume_target_info')
@mock.patch.object(ironic_driver.IronicDriver, '_generate_configdrive')
def test_spawn_node_configdrive_fail(self,
mock_configdrive,
mock_avti, mock_save,
mock_required_by):
def test_spawn_node_configdrive_fail(self, mock_configdrive, mock_avti,
mock_save, mock_required_by,
mock_metadata):
mock_required_by.return_value = True
node_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = _get_cached_node(driver='fake', id=node_id)
flavor = ironic_utils.get_test_flavor()
instance = fake_instance.fake_instance_obj(self.ctx, node=node_id)
instance.flavor = flavor
mock_metadata.return_value = (
ironic_utils.get_test_instance_driver_metadata())
self.mock_conn.get_node.return_value = node
self.mock_conn.validate_node.return_value = \
ironic_utils.get_test_validation()
@@ -1594,12 +1633,14 @@ class IronicDriverTestCase(test.NoDBTestCase):
)
mock_cleanup_deploy.assert_called_with(node, instance, None)
@mock.patch.object(ironic_driver.IronicDriver,
'get_instance_driver_metadata')
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(ironic_driver.IronicDriver, '_add_volume_target_info')
@mock.patch.object(ironic_driver.IronicDriver, '_cleanup_deploy')
def test_spawn_node_trigger_deploy_fail(self, mock_cleanup_deploy,
mock_avti,
mock_required_by):
mock_required_by, mock_metadata):
mock_required_by.return_value = False
node_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = _get_cached_node(driver='fake', id=node_id)
@@ -1607,6 +1648,9 @@ class IronicDriverTestCase(test.NoDBTestCase):
instance = fake_instance.fake_instance_obj(self.ctx, node=node_id)
instance.flavor = flavor
image_meta = ironic_utils.get_test_image_meta()
mock_metadata.return_value = (
ironic_utils.get_test_instance_driver_metadata()
)
self.mock_conn.get_node.return_value = node
self.mock_conn.validate_node.return_value = \
@@ -1627,6 +1671,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
)
mock_cleanup_deploy.assert_called_once_with(node, instance, None)
@mock.patch.object(ironic_driver.IronicDriver,
'get_instance_driver_metadata')
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(loopingcall, 'FixedIntervalLoopingCall')
@mock.patch.object(objects.Instance, 'save')
@@ -1636,12 +1682,18 @@ class IronicDriverTestCase(test.NoDBTestCase):
mock_wait, mock_avti,
mock_save,
mock_looping,
mock_required_by):
mock_required_by,
mock_metadata):
mock_required_by.return_value = False
node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
flavor = ironic_utils.get_test_flavor(ephemeral_gb=1)
instance = fake_instance.fake_instance_obj(self.ctx, node=node_uuid)
instance.flavor = flavor
mock_metadata.return_value = (
ironic_utils.get_test_instance_driver_metadata(
flavor_ephemeralgb=1)
)
image_meta = ironic_utils.get_test_image_meta()
self.driver.spawn(self.ctx, instance, image_meta, [], None, {})
@@ -2148,13 +2200,16 @@ class IronicDriverTestCase(test.NoDBTestCase):
'fake_vif')
mock_uv.assert_called_once_with('fake_instance', ['fake_vif'])
@mock.patch.object(ironic_driver.IronicDriver,
'get_instance_driver_metadata')
@mock.patch.object(ironic_driver.IronicDriver, '_wait_for_active')
@mock.patch.object(loopingcall, 'FixedIntervalLoopingCall')
@mock.patch.object(ironic_driver.IronicDriver,
'_add_instance_info_to_node')
@mock.patch.object(objects.Instance, 'save')
def _test_rebuild(self, mock_save, mock_add_instance_info,
mock_looping, mock_wait_active, preserve=False):
mock_looping, mock_wait_active, mock_metadata,
preserve=False):
node_uuid = uuidutils.generate_uuid()
node = _get_cached_node(id=node_uuid, instance_id=self.instance_id)
self.mock_conn.get_node.return_value = node
@@ -2165,6 +2220,13 @@ class IronicDriverTestCase(test.NoDBTestCase):
instance = fake_instance.fake_instance_obj(
self.ctx, uuid=self.instance_uuid, node=node_uuid, flavor=flavor)
mock_metadata.return_value = (
ironic_utils.get_test_instance_driver_metadata(
flavor_id=5,
flavor_name='baremetal',
instance_uuid=self.instance_uuid,
))
fake_looping_call = FakeLoopingCall()
mock_looping.return_value = fake_looping_call
@@ -2179,6 +2241,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
mock_add_instance_info.assert_called_once_with(
node, instance,
test.MatchType(objects.ImageMeta),
test.MatchType(driver.InstanceDriverMetadata),
flavor, preserve)
self.mock_conn.set_node_provision_state.assert_called_once_with(
node_uuid, ironic_states.REBUILD, config_drive=mock.ANY,
@@ -2214,6 +2277,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
mock_configdrive.assert_called_once_with(
self.ctx, mock.ANY, mock.ANY, mock.ANY, extra_md={}, files=None)
@mock.patch.object(ironic_driver.IronicDriver,
'get_instance_driver_metadata')
@mock.patch.object(ironic_driver.IronicDriver, '_generate_configdrive')
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(ironic_driver.IronicDriver,
@@ -2222,13 +2287,18 @@ class IronicDriverTestCase(test.NoDBTestCase):
def test_rebuild_with_configdrive_failure(self, mock_save,
mock_add_instance_info,
mock_required_by,
mock_configdrive):
mock_configdrive,
mock_metadata):
node_uuid = uuidutils.generate_uuid()
node = _get_cached_node(
id=node_uuid, instance_id=self.instance_uuid)
self.mock_conn.get_node.return_value = node
mock_required_by.return_value = True
mock_configdrive.side_effect = exception.NovaException()
mock_metadata.return_value = (
ironic_utils.get_test_instance_driver_metadata(
flavor_id=5, flavor_name='baremetal')
)
image_meta = ironic_utils.get_test_image_meta()
flavor = objects.Flavor(flavor_id=5, name='baremetal')
@@ -2243,6 +2313,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
bdms=None, detach_block_devices=None,
attach_block_devices=None)
@mock.patch.object(ironic_driver.IronicDriver,
'get_instance_driver_metadata')
@mock.patch.object(ironic_driver.IronicDriver, '_generate_configdrive')
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(ironic_driver.IronicDriver,
@@ -2250,7 +2322,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
@mock.patch.object(objects.Instance, 'save')
def test_rebuild_failures(self, mock_save,
mock_add_instance_info,
mock_required_by, mock_configdrive):
mock_required_by, mock_configdrive,
mock_metadata):
node_uuid = uuidutils.generate_uuid()
node = _get_cached_node(
id=node_uuid, instance_id=self.instance_uuid)
@@ -2259,6 +2332,12 @@ class IronicDriverTestCase(test.NoDBTestCase):
image_meta = ironic_utils.get_test_image_meta()
flavor = objects.Flavor(flavor_id=5, name='baremetal')
mock_metadata.return_value = (
ironic_utils.get_test_instance_driver_metadata(
flavor_id=5,
flavor_name='baremetal',
instance_uuid=self.instance_uuid,
))
instance = fake_instance.fake_instance_obj(
self.ctx, uuid=self.instance_uuid, node=node_uuid, flavor=flavor)

View File

@@ -37,6 +37,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
self.instance = fake_instance.fake_instance_obj(self.ctx)
self.instance.flavor = self.flavor
self.node = ironic_utils.get_test_node(driver='fake')
self.metadata = ironic_utils.get_test_instance_driver_metadata()
# Generic expected patches
self._expected_deploy_patch = [
{'path': '/instance_info/image_source',
@@ -60,9 +61,24 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
{'path': '/instance_info/local_gb',
'value': str(self.node.properties.get('local_gb', 0)),
'op': 'add'},
{'path': '/instance_info/nova_host_id',
'value': u'fake-host',
'op': 'add'},
{'op': 'add', 'path': '/instance_info/fixed_ips',
'value': '[]'},
{'op': 'add', 'path': '/instance_info/floating_ips',
'value': '[]'},
{'op': 'add', 'path': '/instance_info/flavor_name',
'value': str(self.flavor.name)},
{'op': 'add', 'path': '/instance_info/nova_host_id',
'value': 'fake-host'},
{'op': 'add',
'path': '/instance_info/project_id',
'value': 'ppppppp-pppp-pppp-pppp-pppppppppppp'},
{'op': 'add', 'path': '/instance_info/project_name',
'value': 'testproject'},
{'op': 'add', 'path': '/instance_info/user_name',
'value': 'testuser'},
{'op': 'add',
'path': '/instance_info/user_id',
'value': 'uuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu'},
]
def assertPatchEqual(self, expected, observed):
@@ -79,7 +95,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
def test_generic_get_deploy_patch(self):
node = ironic_utils.get_test_node(driver='fake')
patch = patcher.create(node).get_deploy_patch(
self.instance, self.image_meta, self.flavor)
self.instance, self.image_meta, self.flavor, self.metadata)
self.assertPatchEqual(self._expected_deploy_patch, patch)
def test_generic_get_deploy_patch_capabilities(self):
@@ -90,7 +106,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
'op': 'add'}]
expected += self._expected_deploy_patch
patch = patcher.create(node).get_deploy_patch(
self.instance, self.image_meta, self.flavor)
self.instance, self.image_meta, self.flavor, self.metadata)
self.assertPatchEqual(expected, patch)
def test_generic_get_deploy_patch_capabilities_op(self):
@@ -101,7 +117,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
'op': 'add'}]
expected += self._expected_deploy_patch
patch = patcher.create(node).get_deploy_patch(
self.instance, self.image_meta, self.flavor)
self.instance, self.image_meta, self.flavor, self.metadata)
self.assertPatchEqual(expected, patch)
def test_generic_get_deploy_patch_capabilities_nested_key(self):
@@ -112,7 +128,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
'op': 'add'}]
expected += self._expected_deploy_patch
patch = patcher.create(node).get_deploy_patch(
self.instance, self.image_meta, self.flavor)
self.instance, self.image_meta, self.flavor, self.metadata)
self.assertPatchEqual(expected, patch)
def test_generic_get_deploy_patch_traits(self):
@@ -123,7 +139,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
'op': 'add'}]
expected += self._expected_deploy_patch
patch = patcher.create(node).get_deploy_patch(
self.instance, self.image_meta, self.flavor)
self.instance, self.image_meta, self.flavor, self.metadata)
self.assertPatchEqual(expected, patch)
def test_generic_get_deploy_patch_traits_granular(self):
@@ -134,7 +150,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
'op': 'add'}]
expected += self._expected_deploy_patch
patch = patcher.create(node).get_deploy_patch(
self.instance, self.image_meta, self.flavor)
self.instance, self.image_meta, self.flavor, self.metadata)
self.assertPatchEqual(expected, patch)
def test_generic_get_deploy_patch_traits_ignores_not_required(self):
@@ -142,7 +158,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
self.flavor['extra_specs']['trait:CUSTOM_FOO'] = 'invalid'
expected = self._expected_deploy_patch
patch = patcher.create(node).get_deploy_patch(
self.instance, self.image_meta, self.flavor)
self.instance, self.image_meta, self.flavor, self.metadata)
self.assertPatchEqual(expected, patch)
def test_generic_get_deploy_patch_image_traits_required(self):
@@ -154,7 +170,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
'op': 'add'}]
expected += self._expected_deploy_patch
patch = patcher.create(node).get_deploy_patch(
self.instance, self.image_meta, self.flavor)
self.instance, self.image_meta, self.flavor, self.metadata)
self.assertPatchEqual(expected, patch)
def test_generic_get_deploy_patch_image_flavor_traits_required(self):
@@ -167,7 +183,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
'op': 'add'}]
expected += self._expected_deploy_patch
patch = patcher.create(node).get_deploy_patch(
self.instance, self.image_meta, self.flavor)
self.instance, self.image_meta, self.flavor, self.metadata)
self.assertPatchEqual(expected, patch)
def test_generic_get_deploy_patch_image_flavor_traits_none(self):
@@ -175,7 +191,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
self.image_meta.properties = objects.ImageMetaProps()
expected = self._expected_deploy_patch
patch = patcher.create(node).get_deploy_patch(
self.instance, self.image_meta, self.flavor)
self.instance, self.image_meta, self.flavor, self.metadata)
self.assertPatchEqual(expected, patch)
def test_generic_get_deploy_patch_boot_from_volume_image_traits_required(
@@ -192,7 +208,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
'op': 'add'}]
expected += expected_deploy_patch_volume
patch = patcher.create(node).get_deploy_patch(
self.instance, self.image_meta, self.flavor,
self.instance, self.image_meta, self.flavor, self.metadata,
boot_from_volume=True)
self.assertPatchEqual(expected, patch)
@@ -204,7 +220,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
flavor=objects.Flavor(root_gb=1, vcpus=1,
memory_mb=1, ephemeral_gb=10))
patch = patcher.create(node).get_deploy_patch(
instance, self.image_meta, self.flavor)
instance, self.image_meta, self.flavor, self.metadata)
expected = [{'path': '/instance_info/ephemeral_gb',
'value': str(instance.flavor.ephemeral_gb),
'op': 'add'},
@@ -218,7 +234,7 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
node = ironic_utils.get_test_node(driver='fake')
for preserve in [True, False]:
patch = patcher.create(node).get_deploy_patch(
self.instance, self.image_meta, self.flavor,
self.instance, self.image_meta, self.flavor, self.metadata,
preserve_ephemeral=preserve)
expected = [{'path': '/instance_info/preserve_ephemeral',
'value': str(preserve), 'op': 'add', }]
@@ -230,6 +246,6 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase):
expected = [patch for patch in self._expected_deploy_patch
if patch['path'] != '/instance_info/image_source']
patch = patcher.create(node).get_deploy_patch(
self.instance, self.image_meta, self.flavor,
self.instance, self.image_meta, self.flavor, self.metadata,
boot_from_volume=True)
self.assertPatchEqual(expected, patch)

View File

@@ -19,9 +19,27 @@ from openstack.baremetal.v1 import port_group as _port_group
from openstack.baremetal.v1 import volume_connector as _volume_connector
from openstack.baremetal.v1 import volume_target as _volume_target
from nova.network import model as network_model
from nova import objects
from nova.virt import driver
from nova.virt.ironic import ironic_states
# NOTE(JayF): These exist to make it trivial to unify test data
# between both the driver_metadata generators and the generated
# objects.
TEST_IMAGE_UUID = "cccccccc-cccc-cccc-cccc-cccccccccccc"
TEST_IMAGE_NAME = "test-image"
TEST_FLAVOR_ID = "1"
TEST_FLAVOR_NAME = "fake.flavor"
TEST_FLAVOR_EXTRA_SPECS = {
'baremetal:deploy_kernel_id': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
'baremetal:deploy_ramdisk_id': 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'}
TEST_FLAVOR_SWAP = 1
TEST_FLAVOR_ROOTGB = 1
TEST_FLAVOR_MEMORYMB = 1
TEST_FLAVOR_VCPUS = 1
TEST_FLAVOR_EPHEMERALGB = 0
def get_test_validation(**kw):
result = {
@@ -174,18 +192,15 @@ def get_test_volume_target(**kw):
def get_test_flavor(**kw):
default_extra_specs = {
'baremetal:deploy_kernel_id': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
'baremetal:deploy_ramdisk_id': 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
}
flavor = {
'name': kw.get('name', 'fake.flavor'),
'extra_specs': kw.get('extra_specs', default_extra_specs),
'swap': kw.get('swap', 0),
'root_gb': 1,
'memory_mb': 1,
'vcpus': 1,
'ephemeral_gb': kw.get('ephemeral_gb', 0),
'id': kw.get('id', TEST_FLAVOR_ID),
'name': kw.get('name', TEST_FLAVOR_NAME),
'extra_specs': kw.get('extra_specs', TEST_FLAVOR_EXTRA_SPECS),
'swap': kw.get('swap', TEST_FLAVOR_SWAP),
'root_gb': TEST_FLAVOR_ROOTGB,
'memory_mb': TEST_FLAVOR_MEMORYMB,
'vcpus': TEST_FLAVOR_VCPUS,
'ephemeral_gb': kw.get('ephemeral_gb', TEST_FLAVOR_EPHEMERALGB),
}
return objects.Flavor(**flavor)
@@ -193,5 +208,41 @@ def get_test_flavor(**kw):
def get_test_image_meta(**kw):
return objects.ImageMeta.from_dict(
{'id': kw.get('id', 'cccccccc-cccc-cccc-cccc-cccccccccccc')},
{'id': kw.get('id', TEST_IMAGE_UUID)},
)
def get_test_instance_driver_metadata(**kw):
default_instance_meta = driver.NovaInstanceMeta(
name=kw.get('instance_name', 'testinstance'),
uuid=kw.get('instance_uuid', 'iiiiiii-iiii-iiii-iiii-iiiiiiiiiiii'))
default_owner_meta = driver.OwnerMeta(
userid='uuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu',
username='testuser',
projectid='ppppppp-pppp-pppp-pppp-pppppppppppp',
projectname='testproject')
default_image_meta = driver.ImageMeta(id=TEST_IMAGE_UUID,
name=TEST_IMAGE_NAME,
properties={})
default_flavor_meta = driver.FlavorMeta(
name=kw.get('flavor_name', TEST_FLAVOR_NAME),
memory_mb=kw.get('flavor_memorymb',
TEST_FLAVOR_MEMORYMB),
vcpus=kw.get('flavor_vcpus', TEST_FLAVOR_VCPUS),
root_gb=kw.get('flavor_rootgb',
TEST_FLAVOR_ROOTGB),
ephemeral_gb=kw.get('flavor_ephemeralgb',
TEST_FLAVOR_EPHEMERALGB),
extra_specs=kw.get('flavor_extra_specs',
TEST_FLAVOR_EXTRA_SPECS),
swap=kw.get('flavor_swap', TEST_FLAVOR_SWAP))
return driver.InstanceDriverMetadata(
root_type=kw.get('root_type', 'roottype'),
root_id=kw.get('root_id', 'rootid'),
instance_meta=kw.get('instance_meta', default_instance_meta),
owner=kw.get('owner_meta', default_owner_meta),
image=kw.get('image_meta', default_image_meta),
flavor=kw.get('flavor_meta', default_flavor_meta),
network_info=kw.get('network_info', network_model.NetworkInfo())
)

View File

@@ -2591,17 +2591,19 @@ class LibvirtConnTestCase(test.NoDBTestCase,
def test_get_guest_config_meta_with_no_port(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
meta = drvr._get_guest_config_meta(
idm = drvr.get_instance_driver_metadata(
objects.Instance(**self.test_instance),
_fake_network_info(self, num_networks=0))
meta = drvr._get_guest_config_meta(idm)
self.assertEqual(len(meta.ports.ports), 0)
def test_get_guest_config_meta_with_multiple_ports(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
meta = drvr._get_guest_config_meta(
idm = drvr.get_instance_driver_metadata(
objects.Instance(**self.test_instance),
_fake_network_info(self, num_networks=2))
meta = drvr._get_guest_config_meta(idm)
self.assertEqual(len(meta.ports.ports), 2)
@@ -2649,6 +2651,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
user_name="pie",
)
flavor = objects.Flavor(
id=1,
name='m1.small',
memory_mb=6,
vcpus=28,
@@ -2775,6 +2778,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
ephemeral_gb=8128,
swap=33550336,
extra_specs={},
id=42
)
instance_ref = objects.Instance(**test_instance)
instance_ref.flavor = flavor
@@ -2927,13 +2931,10 @@ class LibvirtConnTestCase(test.NoDBTestCase,
user_id=456,
user_name="pie")
flavor = objects.Flavor(name='m1.small',
memory_mb=6,
vcpus=28,
root_gb=496,
ephemeral_gb=8128,
swap=33550336,
extra_specs={})
flavor = objects.Flavor(
id=42, name='m1.small', memory_mb=6,
vcpus=28, root_gb=496, ephemeral_gb=8128,
swap=33550336, extra_specs={})
instance_ref = objects.Instance(**test_instance)
instance_ref.flavor = flavor
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
@@ -3106,7 +3107,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
flavor = objects.Flavor(memory_mb=1, vcpus=2, root_gb=496,
ephemeral_gb=8128, swap=33550336, name='fake',
extra_specs={})
extra_specs={}, id=42)
instance_ref.flavor = flavor
caps = vconfig.LibvirtConfigCaps()
@@ -3140,7 +3141,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
def test_get_guest_config_numa_host_instance_no_fit(self):
instance_ref = objects.Instance(**self.test_instance)
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
flavor = objects.Flavor(memory_mb=4096, vcpus=4, root_gb=496,
flavor = objects.Flavor(
id=42, memory_mb=4096, vcpus=4, root_gb=496,
ephemeral_gb=8128, swap=33550336, name='fake',
extra_specs={})
instance_ref.flavor = flavor
@@ -3475,13 +3477,10 @@ class LibvirtConnTestCase(test.NoDBTestCase,
extra_specs = {
"hw:mem_encryption": True,
}
flavor = objects.Flavor(name='m1.small',
memory_mb=6,
vcpus=28,
root_gb=496,
ephemeral_gb=8128,
swap=33550336,
extra_specs=extra_specs)
flavor = objects.Flavor(
id=42, name='m1.small', memory_mb=6,
vcpus=28, root_gb=496, ephemeral_gb=8128,
swap=33550336, extra_specs=extra_specs)
instance_ref = objects.Instance(**self.test_instance)
instance_ref.flavor = flavor
@@ -3573,7 +3572,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
flavor = objects.Flavor(memory_mb=1, vcpus=2, root_gb=496,
ephemeral_gb=8128, swap=33550336, name='fake',
extra_specs={})
extra_specs={}, id=42)
instance_ref.flavor = flavor
caps = vconfig.LibvirtConfigCaps()
@@ -3626,7 +3625,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
flavor = objects.Flavor(memory_mb=4096, vcpus=4, root_gb=496,
ephemeral_gb=8128, swap=33550336, name='fake',
extra_specs={})
extra_specs={}, id=42)
instance_ref.flavor = flavor
caps = vconfig.LibvirtConfigCaps()
@@ -3744,7 +3743,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
flavor = objects.Flavor(memory_mb=1024, vcpus=2, root_gb=496,
ephemeral_gb=8128, swap=33550336, name='fake',
extra_specs={})
extra_specs={}, id=42)
instance_ref.flavor = flavor
caps = vconfig.LibvirtConfigCaps()
@@ -3787,7 +3786,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
flavor = objects.Flavor(memory_mb=2048, vcpus=2, root_gb=496,
ephemeral_gb=8128, swap=33550336, name='fake',
extra_specs={})
extra_specs={}, id=42)
instance_ref.flavor = flavor
caps = vconfig.LibvirtConfigCaps()
@@ -3840,7 +3839,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
flavor = objects.Flavor(memory_mb=2048, vcpus=4, root_gb=496,
ephemeral_gb=8128, swap=33550336, name='fake',
extra_specs={})
extra_specs={}, id=42)
instance_ref.flavor = flavor
caps = vconfig.LibvirtConfigCaps()
@@ -3917,7 +3916,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
flavor = objects.Flavor(memory_mb=2048, vcpus=4, root_gb=496,
ephemeral_gb=8128, swap=33550336, name='fake',
extra_specs={})
extra_specs={}, id=42)
instance_ref.flavor = flavor
caps = vconfig.LibvirtConfigCaps()
@@ -3995,7 +3994,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
flavor = objects.Flavor(memory_mb=2048, vcpus=2, root_gb=496,
ephemeral_gb=8128, swap=33550336, name='fake',
extra_specs={})
extra_specs={}, id=42)
instance_ref.flavor = flavor
caps = vconfig.LibvirtConfigCaps()
@@ -4084,7 +4083,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
flavor = objects.Flavor(memory_mb=2048, vcpus=8, root_gb=496,
ephemeral_gb=8128, swap=33550336, name='fake',
extra_specs={})
extra_specs={}, id=42)
instance_ref.flavor = flavor
caps = vconfig.LibvirtConfigCaps()
@@ -4198,7 +4197,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
flavor = objects.Flavor(memory_mb=2048, vcpus=8, root_gb=496,
ephemeral_gb=8128, swap=33550336, name='fake',
extra_specs={})
extra_specs={}, id=42)
instance_ref.flavor = flavor
caps = vconfig.LibvirtConfigCaps()
@@ -4301,7 +4300,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
"hw:cpu_realtime": "yes",
"hw:cpu_policy": "mixed",
"hw:cpu_realtime_mask": "^2-3"
})
}, id=42)
instance_ref.flavor = flavor
caps = vconfig.LibvirtConfigCaps()
@@ -4410,7 +4409,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
flavor = objects.Flavor(memory_mb=2048, vcpus=4, root_gb=496,
ephemeral_gb=8128, swap=33550336, name='fake',
extra_specs={})
extra_specs={}, id=42)
instance_ref.flavor = flavor
caps = vconfig.LibvirtConfigCaps()
@@ -4494,7 +4493,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
"hw:cpu_realtime": "yes",
"hw:cpu_policy": "dedicated",
"hw:cpu_realtime_mask": "^0-1"
})
}, id=42)
instance_ref.flavor = flavor
caps = vconfig.LibvirtConfigCaps()
@@ -22933,6 +22932,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
inst = {}
inst['id'] = 1
inst['uuid'] = uuids.fake_instance_id
inst['display_name'] = 'fake-instance'
inst['os_type'] = 'linux'
inst['image_ref'] = uuids.fake_image_ref
inst['reservation_id'] = 'r-fakeres'
@@ -25002,13 +25002,16 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
instance, 'get_network_info', return_value=network_info),
mock.patch.object(
self.drvr, '_detach_with_retry'),
mock.patch.object(
self.drvr, 'get_instance_driver_metadata'
),
mock.patch.object(
self.drvr, '_get_guest_config_meta', return_value=config_meta),
mock.patch.object(guest, 'set_metadata')
) as (
mock_get_guest, mock_get_config, mock_get_network_info,
mock_detach_with_retry, mock_get_guest_config_meta,
mock_set_metadata
mock_detach_with_retry, mock_get_instance_driver_metadata,
mock_get_guest_config_meta, mock_set_metadata
):
self.drvr.detach_interface(self.context, instance, vif)
mock_get_guest.assert_called_once_with(instance)
@@ -25018,8 +25021,9 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
mock_detach_with_retry.assert_called_once_with(
guest, instance.uuid, mock.ANY, device_name=None)
mock_get_network_info.assert_called_once_with()
mock_get_guest_config_meta.assert_called_once_with(
mock_get_instance_driver_metadata.assert_called_once_with(
instance, network_info[1:])
mock_get_guest_config_meta.assert_called_once()
mock_set_metadata.assert_called_once_with(config_meta)
def test__detach_with_retry_persistent_success(self):

View File

@@ -20,26 +20,78 @@ Driver base-classes:
types that support that contract
"""
import dataclasses
import itertools
import sys
import time
import typing as ty
import os_resource_classes as orc
import os_traits
from oslo_log import log as logging
from oslo_utils import importutils
import nova.conf
import nova.virt.node
from nova import context as nova_context
from nova.i18n import _
from nova.network import model as network_model
from nova import objects
from nova import version
from nova.virt import event as virtevent
import nova.virt.node
CONF = nova.conf.CONF
LOG = logging.getLogger(__name__)
@dataclasses.dataclass
class FlavorMeta:
name: str
memory_mb: int
vcpus: int
root_gb: int
ephemeral_gb: int
extra_specs: dict
swap: int
@dataclasses.dataclass
class ImageMeta:
id: str
name: str
properties: dict
@dataclasses.dataclass
class NovaInstanceMeta:
name: str
uuid: str
@dataclasses.dataclass
class OwnerMeta:
userid: str
username: str
projectid: str
projectname: str
@dataclasses.dataclass
class InstanceDriverMetadata:
root_type: str
root_id: str
instance_meta: NovaInstanceMeta
owner: OwnerMeta
image: ImageMeta
flavor: FlavorMeta
network_info: network_model.NetworkInfo
nova_package: str = dataclasses.field(
default_factory=version.version_string_with_package)
creation_time: float = dataclasses.field(default_factory=time.time)
def get_block_device_info(instance, block_device_mapping):
"""Converts block device mappings for an instance to driver format.
@@ -294,6 +346,55 @@ class ComputeDriver(object):
# TODO(Vek): Need to pass context in for access to auth_token
raise NotImplementedError()
@classmethod
def get_instance_driver_metadata(
cls, instance: 'nova.objects.instance.Instance',
network_info: network_model.NetworkInfo
) -> InstanceDriverMetadata:
"""Get driver metadata from instance and network info
:param instance: nova.objects.instance.Instance
:param network_info: instance network information
:returns: InstanceDriverMetadata
"""
instance_name = instance.display_name or instance.uuid
system_meta = instance.system_metadata
instance_meta = NovaInstanceMeta(
str(instance_name), str(instance.uuid))
owner = OwnerMeta(
userid=instance.user_id,
username=system_meta.get('owner_user_name', 'N/A'),
projectid=instance.project_id,
projectname=system_meta.get('owner_project_name', 'N/A')
)
flavor = FlavorMeta(
name=instance.flavor.name,
memory_mb=instance.flavor.memory_mb,
vcpus=instance.flavor.vcpus,
ephemeral_gb=instance.flavor.ephemeral_gb,
root_gb=instance.flavor.root_gb,
swap=instance.flavor.swap,
extra_specs=instance.flavor.extra_specs,
)
image = ImageMeta(
id=instance.image_ref,
name=system_meta.get('image_name'),
properties=instance.image_meta.properties
)
meta = InstanceDriverMetadata(
instance_meta=instance_meta,
owner=owner,
flavor=flavor,
image=image,
root_type = 'image' if instance.image_ref else 'volume',
root_id = instance.image_ref,
creation_time = time.time(),
network_info=network_info
)
LOG.debug('InstanceDriverMetadata: %s', meta)
return meta
def get_num_instances(self):
"""Return the total number of virtual machines.

View File

@@ -404,7 +404,7 @@ class IronicDriver(virt_driver.ComputeDriver):
self._cleanup_deploy(node, instance)
def _add_instance_info_to_node(self, node, instance, image_meta, flavor,
preserve_ephemeral=None,
metadata, preserve_ephemeral=None,
block_device_info=None):
root_bdm = block_device.get_root_bdm(
@@ -413,6 +413,7 @@ class IronicDriver(virt_driver.ComputeDriver):
patch = patcher.create(node).get_deploy_patch(instance,
image_meta,
flavor,
metadata,
preserve_ephemeral,
boot_from_volume)
@@ -1157,7 +1158,7 @@ class IronicDriver(virt_driver.ComputeDriver):
:param network_info: Instance network information.
:param block_device_info: Instance block device
information.
:param arqs: Accelerator requests for this instance.
:param accel_info: Accelerator requests for this instance.
:param power_on: True if the instance should be powered on, False
otherwise
"""
@@ -1175,7 +1176,9 @@ class IronicDriver(virt_driver.ComputeDriver):
node = self._get_node(node_id)
flavor = instance.flavor
metadata = self.get_instance_driver_metadata(instance, network_info)
self._add_instance_info_to_node(node, instance, image_meta, flavor,
metadata,
block_device_info=block_device_info)
try:
@@ -1739,7 +1742,8 @@ class IronicDriver(virt_driver.ComputeDriver):
node_id = instance.node
node = self._get_node(node_id)
self._add_instance_info_to_node(node, instance, image_meta,
metadata = self.get_instance_driver_metadata(instance, network_info)
self._add_instance_info_to_node(node, instance, image_meta, metadata,
instance.flavor, preserve_ephemeral)
# Config drive

View File

@@ -40,13 +40,14 @@ class GenericDriverFields(object):
def __init__(self, node):
self.node = node
def get_deploy_patch(self, instance, image_meta, flavor,
def get_deploy_patch(self, instance, image_meta, flavor, metadata,
preserve_ephemeral=None, boot_from_volume=False):
"""Build a patch to add the required fields to deploy a node.
:param instance: the instance object.
:param image_meta: the nova.objects.ImageMeta object instance
:param flavor: the flavor object.
:param metadata: nova.virt.driver.InstanceDriverMetadata dataclass
:param preserve_ephemeral: preserve_ephemeral status (bool) to be
specified during rebuild.
:param boot_from_volume: True if node boots from volume. Then,
@@ -73,6 +74,21 @@ class GenericDriverFields(object):
patch.append({'path': '/instance_info/local_gb', 'op': 'add',
'value': str(self.node.properties.get('local_gb', 0))})
patch.append({'path': '/instance_info/project_id', 'op': 'add',
'value': str(metadata.owner.projectid)})
patch.append({'path': '/instance_info/project_name', 'op': 'add',
'value': str(metadata.owner.projectname)})
patch.append({'path': '/instance_info/user_id', 'op': 'add',
'value': str(metadata.owner.userid)})
patch.append({'path': '/instance_info/user_name', 'op': 'add',
'value': str(metadata.owner.username)})
patch.append({'path': '/instance_info/flavor_name', 'op': 'add',
'value': str(metadata.flavor.name)})
patch.append({'path': '/instance_info/fixed_ips', 'op': 'add',
'value': str(metadata.network_info.fixed_ips())})
patch.append({'path': '/instance_info/floating_ips', 'op': 'add',
'value': str(metadata.network_info.floating_ips())})
if instance.flavor.ephemeral_gb:
patch.append({'path': '/instance_info/ephemeral_gb',
'op': 'add',

View File

@@ -3052,7 +3052,8 @@ class LibvirtDriver(driver.ComputeDriver):
try:
guest.set_metadata(
self._get_guest_config_meta(
instance, instance.get_network_info()))
self.get_instance_driver_metadata(
instance, instance.get_network_info())))
except libvirt.libvirtError:
LOG.warning('updating libvirt metadata failed.', instance=instance)
@@ -3091,7 +3092,9 @@ class LibvirtDriver(driver.ComputeDriver):
network_info = list(filter(lambda info: info['id'] != vif['id'],
instance.get_network_info()))
guest.set_metadata(
self._get_guest_config_meta(instance, network_info))
self._get_guest_config_meta(
self.get_instance_driver_metadata(
instance, network_info)))
except libvirt.libvirtError:
LOG.warning('updating libvirt metadata failed.', instance=instance)
@@ -6073,39 +6076,35 @@ class LibvirtDriver(driver.ComputeDriver):
return dev
def _get_guest_config_meta(self, instance, network_info):
def _get_guest_config_meta(self, dmeta: driver.InstanceDriverMetadata):
"""Get metadata config for guest."""
meta = vconfig.LibvirtConfigGuestMetaNovaInstance()
meta.package = version.version_string_with_package()
meta.name = instance.display_name
meta.creationTime = time.time()
meta.package = dmeta.nova_package
meta.name = dmeta.instance_meta.name
meta.creationTime = dmeta.creation_time
meta.roottype = dmeta.root_type
meta.rootid = dmeta.root_id
if instance.image_ref not in ("", None):
meta.roottype = "image"
meta.rootid = instance.image_ref
system_meta = instance.system_metadata
ometa = vconfig.LibvirtConfigGuestMetaNovaOwner()
ometa.userid = instance.user_id
ometa.username = system_meta.get('owner_user_name', 'N/A')
ometa.projectid = instance.project_id
ometa.projectname = system_meta.get('owner_project_name', 'N/A')
ometa.userid = dmeta.owner.userid
ometa.username = dmeta.owner.username
ometa.projectid = dmeta.owner.projectid
ometa.projectname = dmeta.owner.projectname
meta.owner = ometa
fmeta = vconfig.LibvirtConfigGuestMetaNovaFlavor()
flavor = instance.flavor
fmeta.name = flavor.name
fmeta.memory = flavor.memory_mb
fmeta.vcpus = flavor.vcpus
fmeta.ephemeral = flavor.ephemeral_gb
fmeta.disk = flavor.root_gb
fmeta.swap = flavor.swap
fmeta.name = dmeta.flavor.name
fmeta.memory = dmeta.flavor.memory_mb
fmeta.vcpus = dmeta.flavor.vcpus
fmeta.ephemeral = dmeta.flavor.ephemeral_gb
fmeta.disk = dmeta.flavor.root_gb
fmeta.swap = dmeta.flavor.swap
meta.flavor = fmeta
ports = []
for vif in network_info:
for vif in dmeta.network_info:
ips = []
for subnet in vif.get('network', {}).get('subnets', []):
for ip in subnet.get('ips', []):
@@ -7353,8 +7352,10 @@ class LibvirtDriver(driver.ComputeDriver):
guest_numa_config.numatune,
flavor, image_meta)
guest.metadata.append(self._get_guest_config_meta(
instance, network_info))
guest.metadata.append(
self._get_guest_config_meta(
self.get_instance_driver_metadata(
instance, network_info)))
guest.idmaps = self._get_guest_idmaps()
for event in self._supported_perf_events: