Add image meta to libvirt XML metadata

This patch adds the image_meta used to launch an instance to its
libvirt domain metadata.

Nova exposes the image_meta structure when publishing
instance notifications. Downstream services that consume these
notifications such as Ceilometer use this to provide metadata about
the image originally used to create an ephemeral instance or
instance boot volume.

Ceilometer also polls the running instances using the Compute Agent
by reading the metadata of active instances from the libvirt socket.

Adding the data stored in image_meta to the libvirt metadata allows
Ceilometer to discover and expose the actual image metadata used to
launch instances using its compute pollsters, without performing
additional API queries to Nova, Cinder and Glance to get this
information (and even if that was done, it could be different to what
is actually running if images are updated after the fact).

To match the existing image_meta definition from Nova notifications,
depending on the type of instance, the behaviour of the metadata is:

* Instance built from image
  => UUID set for image, image metadata added to the XML
* Instance launched from volume built from image
  => UUID empty, volume image metadata added to the XML
* Instance launched from volume NOT built from image
  => UUID empty, no attributes from image meta defined

Signed-off-by: Callum Dickinson <callum.dickinson@catalystcloud.nz>
Implements: blueprint xml-image-meta
Change-Id: I09f4f76fff30f9cccf35f4832b9c870095c380ad
This commit is contained in:
Callum Dickinson
2025-02-26 09:07:19 +13:00
parent 619cd0a9c7
commit 7ce26377af
7 changed files with 521 additions and 96 deletions

View File

@@ -224,6 +224,10 @@ def get_test_instance_driver_metadata(**kw):
projectname='testproject')
default_image_meta = driver.ImageMeta(id=TEST_IMAGE_UUID,
name=TEST_IMAGE_NAME,
container_format=None,
disk_format=None,
min_disk=None,
min_ram=None,
properties={})
default_flavor_meta = driver.FlavorMeta(
name=kw.get('flavor_name', TEST_FLAVOR_NAME),

View File

@@ -23,6 +23,7 @@ from nova import exception
from nova.objects import fields as obj_fields
from nova import test
from nova.tests.fixtures import libvirt_data as fake_libvirt_data
from nova.virt import driver
from nova.virt import hardware
from nova.virt.libvirt import config
@@ -4099,91 +4100,6 @@ class LibvirtConfigGuestNUMATuneTest(LibvirtConfigBaseTest):
class LibvirtConfigGuestMetadataNovaTest(LibvirtConfigBaseTest):
def test_config_metadata(self):
meta = config.LibvirtConfigGuestMetaNovaInstance()
meta.package = "2014.2.3"
meta.name = "moonbuggy"
meta.creationTime = 1234567890
meta.roottype = "image"
meta.rootid = "fe55c69a-8b2e-4bbc-811a-9ad2023a0426"
owner = config.LibvirtConfigGuestMetaNovaOwner()
owner.userid = "3472c2a6-de91-4fb5-b618-42bc781ef670"
owner.username = "buzz"
owner.projectid = "f241e906-010e-4917-ae81-53f4fb8aa021"
owner.projectname = "moonshot"
meta.owner = owner
flavor = config.LibvirtConfigGuestMetaNovaFlavor()
flavor.name = "m1.lowgravity"
flavor.id = "f719a0dd-4b43-4efe-8336-48ef74099ad4"
flavor.vcpus = 8
flavor.memory = 2048
flavor.swap = 10
flavor.disk = 50
flavor.ephemeral = 10
meta.flavor = flavor
meta.ports = config.LibvirtConfigGuestMetaNovaPorts(
ports=[
config.LibvirtConfigGuestMetaNovaPort(
'567a4527-b0e4-4d0a-bcc2-71fda37897f7',
ips=[
config.LibvirtConfigGuestMetaNovaIp(
'fixed', '192.168.1.1', '4'),
config.LibvirtConfigGuestMetaNovaIp(
'fixed', 'fe80::f95c:b030:7094', '6'),
config.LibvirtConfigGuestMetaNovaIp(
'floating', '11.22.33.44', '4')]),
config.LibvirtConfigGuestMetaNovaPort(
'a3ca97e2-0cf9-4159-9bfc-afd55bc13ead',
ips=[
config.LibvirtConfigGuestMetaNovaIp(
'fixed', '10.0.0.1', '4'),
config.LibvirtConfigGuestMetaNovaIp(
'fixed', 'fdf8:f53b:82e4::52', '6'),
config.LibvirtConfigGuestMetaNovaIp(
'floating', '1.2.3.4', '4')])])
xml = meta.to_xml()
self.assertXmlEqual(xml, """
<nova:instance xmlns:nova='http://openstack.org/xmlns/libvirt/nova/1.1'>
<nova:package version="2014.2.3"/>
<nova:name>moonbuggy</nova:name>
<nova:creationTime>2009-02-13 23:31:30</nova:creationTime>
<nova:flavor name="m1.lowgravity"
id="f719a0dd-4b43-4efe-8336-48ef74099ad4">
<nova:memory>2048</nova:memory>
<nova:disk>50</nova:disk>
<nova:swap>10</nova:swap>
<nova:ephemeral>10</nova:ephemeral>
<nova:vcpus>8</nova:vcpus>
<nova:extraSpecs></nova:extraSpecs>
</nova:flavor>
<nova:owner>
<nova:user
uuid="3472c2a6-de91-4fb5-b618-42bc781ef670">buzz</nova:user>
<nova:project
uuid="f241e906-010e-4917-ae81-53f4fb8aa021">moonshot</nova:project>
</nova:owner>
<nova:root type="image" uuid="fe55c69a-8b2e-4bbc-811a-9ad2023a0426"/>
<nova:ports>
<nova:port uuid="567a4527-b0e4-4d0a-bcc2-71fda37897f7">
<nova:ip type="fixed" address="192.168.1.1" ipVersion="4"/>
<nova:ip type="fixed" address="fe80::f95c:b030:7094" ipVersion="6"/>
<nova:ip type="floating" address="11.22.33.44" ipVersion="4"/>
</nova:port>
<nova:port uuid="a3ca97e2-0cf9-4159-9bfc-afd55bc13ead">
<nova:ip type="fixed" address="10.0.0.1" ipVersion="4"/>
<nova:ip type="fixed" address="fdf8:f53b:82e4::52" ipVersion="6"/>
<nova:ip type="floating" address="1.2.3.4" ipVersion="4"/>
</nova:port>
</nova:ports>
</nova:instance>
""")
def test_config_metadata_flavor_extra_specs(self):
meta = config.LibvirtConfigGuestMetaNovaInstance()
meta.package = "2014.2.3"
@@ -4272,6 +4188,317 @@ class LibvirtConfigGuestMetadataNovaTest(LibvirtConfigBaseTest):
</nova:instance>
""")
def test_config_metadata_from_image(self):
meta = config.LibvirtConfigGuestMetaNovaInstance()
meta.package = "2014.2.3"
meta.name = "moonbuggy"
meta.creationTime = 1234567890
meta.roottype = "image"
meta.rootid = "fe55c69a-8b2e-4bbc-811a-9ad2023a0426"
imeta = config.LibvirtConfigGuestMetaImage()
imeta.uuid = meta.rootid
imeta.image_meta = driver.ImageMeta(
id=meta.rootid,
name="ubuntu-24.04-x86_64",
container_format="bare",
disk_format="raw",
min_disk=10,
min_ram=0,
properties={"os_distro": "ubuntu",
"os_type": "linux",
"img_version": 1,
"img_use_agent": True})
meta.image = imeta
owner = config.LibvirtConfigGuestMetaNovaOwner()
owner.userid = "3472c2a6-de91-4fb5-b618-42bc781ef670"
owner.username = "buzz"
owner.projectid = "f241e906-010e-4917-ae81-53f4fb8aa021"
owner.projectname = "moonshot"
meta.owner = owner
flavor = config.LibvirtConfigGuestMetaNovaFlavor()
flavor.name = "m1.lowgravity"
flavor.id = "f719a0dd-4b43-4efe-8336-48ef74099ad4"
flavor.vcpus = 8
flavor.memory = 2048
flavor.swap = 10
flavor.disk = 50
flavor.ephemeral = 10
meta.flavor = flavor
meta.ports = config.LibvirtConfigGuestMetaNovaPorts(
ports=[
config.LibvirtConfigGuestMetaNovaPort(
'567a4527-b0e4-4d0a-bcc2-71fda37897f7',
ips=[
config.LibvirtConfigGuestMetaNovaIp(
'fixed', '192.168.1.1', '4'),
config.LibvirtConfigGuestMetaNovaIp(
'fixed', 'fe80::f95c:b030:7094', '6'),
config.LibvirtConfigGuestMetaNovaIp(
'floating', '11.22.33.44', '4')]),
config.LibvirtConfigGuestMetaNovaPort(
'a3ca97e2-0cf9-4159-9bfc-afd55bc13ead',
ips=[
config.LibvirtConfigGuestMetaNovaIp(
'fixed', '10.0.0.1', '4'),
config.LibvirtConfigGuestMetaNovaIp(
'fixed', 'fdf8:f53b:82e4::52', '6'),
config.LibvirtConfigGuestMetaNovaIp(
'floating', '1.2.3.4', '4')])])
xml = meta.to_xml()
self.assertXmlEqual(xml, """
<nova:instance xmlns:nova='http://openstack.org/xmlns/libvirt/nova/1.1'>
<nova:package version="2014.2.3"/>
<nova:name>moonbuggy</nova:name>
<nova:creationTime>2009-02-13 23:31:30</nova:creationTime>
<nova:flavor name="m1.lowgravity"
id="f719a0dd-4b43-4efe-8336-48ef74099ad4">
<nova:memory>2048</nova:memory>
<nova:disk>50</nova:disk>
<nova:swap>10</nova:swap>
<nova:ephemeral>10</nova:ephemeral>
<nova:vcpus>8</nova:vcpus>
<nova:extraSpecs></nova:extraSpecs>
</nova:flavor>
<nova:image uuid="fe55c69a-8b2e-4bbc-811a-9ad2023a0426">
<nova:containerFormat>bare</nova:containerFormat>
<nova:diskFormat>raw</nova:diskFormat>
<nova:minDisk>10</nova:minDisk>
<nova:minRam>0</nova:minRam>
<nova:properties>
<nova:property name="os_distro">ubuntu</nova:property>
<nova:property name="os_type">linux</nova:property>
<nova:property name="img_version">1</nova:property>
<nova:property name="img_use_agent">True</nova:property>
</nova:properties>
</nova:image>
<nova:owner>
<nova:user
uuid="3472c2a6-de91-4fb5-b618-42bc781ef670">buzz</nova:user>
<nova:project
uuid="f241e906-010e-4917-ae81-53f4fb8aa021">moonshot</nova:project>
</nova:owner>
<nova:root type="image" uuid="fe55c69a-8b2e-4bbc-811a-9ad2023a0426"/>
<nova:ports>
<nova:port uuid="567a4527-b0e4-4d0a-bcc2-71fda37897f7">
<nova:ip type="fixed" address="192.168.1.1" ipVersion="4"/>
<nova:ip type="fixed" address="fe80::f95c:b030:7094" ipVersion="6"/>
<nova:ip type="floating" address="11.22.33.44" ipVersion="4"/>
</nova:port>
<nova:port uuid="a3ca97e2-0cf9-4159-9bfc-afd55bc13ead">
<nova:ip type="fixed" address="10.0.0.1" ipVersion="4"/>
<nova:ip type="fixed" address="fdf8:f53b:82e4::52" ipVersion="6"/>
<nova:ip type="floating" address="1.2.3.4" ipVersion="4"/>
</nova:port>
</nova:ports>
</nova:instance>
""")
def test_config_metadata_from_volume_image(self):
meta = config.LibvirtConfigGuestMetaNovaInstance()
meta.package = "2014.2.3"
meta.name = "moonbuggy"
meta.creationTime = 1234567890
imeta = config.LibvirtConfigGuestMetaImage()
imeta.image_meta = driver.ImageMeta(
id="",
name="",
container_format="bare",
disk_format="raw",
min_disk=10,
min_ram=0,
properties={"os_distro": "ubuntu",
"os_type": "linux",
"img_version": 1,
"img_use_agent": True})
meta.image = imeta
owner = config.LibvirtConfigGuestMetaNovaOwner()
owner.userid = "3472c2a6-de91-4fb5-b618-42bc781ef670"
owner.username = "buzz"
owner.projectid = "f241e906-010e-4917-ae81-53f4fb8aa021"
owner.projectname = "moonshot"
meta.owner = owner
flavor = config.LibvirtConfigGuestMetaNovaFlavor()
flavor.name = "m1.lowgravity"
flavor.id = "f719a0dd-4b43-4efe-8336-48ef74099ad4"
flavor.vcpus = 8
flavor.memory = 2048
flavor.swap = 10
flavor.disk = 50
flavor.ephemeral = 10
meta.flavor = flavor
meta.ports = config.LibvirtConfigGuestMetaNovaPorts(
ports=[
config.LibvirtConfigGuestMetaNovaPort(
'567a4527-b0e4-4d0a-bcc2-71fda37897f7',
ips=[
config.LibvirtConfigGuestMetaNovaIp(
'fixed', '192.168.1.1', '4'),
config.LibvirtConfigGuestMetaNovaIp(
'fixed', 'fe80::f95c:b030:7094', '6'),
config.LibvirtConfigGuestMetaNovaIp(
'floating', '11.22.33.44', '4')]),
config.LibvirtConfigGuestMetaNovaPort(
'a3ca97e2-0cf9-4159-9bfc-afd55bc13ead',
ips=[
config.LibvirtConfigGuestMetaNovaIp(
'fixed', '10.0.0.1', '4'),
config.LibvirtConfigGuestMetaNovaIp(
'fixed', 'fdf8:f53b:82e4::52', '6'),
config.LibvirtConfigGuestMetaNovaIp(
'floating', '1.2.3.4', '4')])])
xml = meta.to_xml()
self.assertXmlEqual(xml, """
<nova:instance xmlns:nova='http://openstack.org/xmlns/libvirt/nova/1.1'>
<nova:package version="2014.2.3"/>
<nova:name>moonbuggy</nova:name>
<nova:creationTime>2009-02-13 23:31:30</nova:creationTime>
<nova:flavor name="m1.lowgravity"
id="f719a0dd-4b43-4efe-8336-48ef74099ad4">
<nova:memory>2048</nova:memory>
<nova:disk>50</nova:disk>
<nova:swap>10</nova:swap>
<nova:ephemeral>10</nova:ephemeral>
<nova:vcpus>8</nova:vcpus>
<nova:extraSpecs></nova:extraSpecs>
</nova:flavor>
<nova:image uuid="">
<nova:containerFormat>bare</nova:containerFormat>
<nova:diskFormat>raw</nova:diskFormat>
<nova:minDisk>10</nova:minDisk>
<nova:minRam>0</nova:minRam>
<nova:properties>
<nova:property name="os_distro">ubuntu</nova:property>
<nova:property name="os_type">linux</nova:property>
<nova:property name="img_version">1</nova:property>
<nova:property name="img_use_agent">True</nova:property>
</nova:properties>
</nova:image>
<nova:owner>
<nova:user
uuid="3472c2a6-de91-4fb5-b618-42bc781ef670">buzz</nova:user>
<nova:project
uuid="f241e906-010e-4917-ae81-53f4fb8aa021">moonshot</nova:project>
</nova:owner>
<nova:ports>
<nova:port uuid="567a4527-b0e4-4d0a-bcc2-71fda37897f7">
<nova:ip type="fixed" address="192.168.1.1" ipVersion="4"/>
<nova:ip type="fixed" address="fe80::f95c:b030:7094" ipVersion="6"/>
<nova:ip type="floating" address="11.22.33.44" ipVersion="4"/>
</nova:port>
<nova:port uuid="a3ca97e2-0cf9-4159-9bfc-afd55bc13ead">
<nova:ip type="fixed" address="10.0.0.1" ipVersion="4"/>
<nova:ip type="fixed" address="fdf8:f53b:82e4::52" ipVersion="6"/>
<nova:ip type="floating" address="1.2.3.4" ipVersion="4"/>
</nova:port>
</nova:ports>
</nova:instance>
""")
def test_config_metadata_from_volume_no_image(self):
meta = config.LibvirtConfigGuestMetaNovaInstance()
meta.package = "2014.2.3"
meta.name = "moonbuggy"
meta.creationTime = 1234567890
imeta = config.LibvirtConfigGuestMetaImage()
meta.image = imeta
owner = config.LibvirtConfigGuestMetaNovaOwner()
owner.userid = "3472c2a6-de91-4fb5-b618-42bc781ef670"
owner.username = "buzz"
owner.projectid = "f241e906-010e-4917-ae81-53f4fb8aa021"
owner.projectname = "moonshot"
meta.owner = owner
flavor = config.LibvirtConfigGuestMetaNovaFlavor()
flavor.name = "m1.lowgravity"
flavor.id = "f719a0dd-4b43-4efe-8336-48ef74099ad4"
flavor.vcpus = 8
flavor.memory = 2048
flavor.swap = 10
flavor.disk = 50
flavor.ephemeral = 10
meta.flavor = flavor
meta.ports = config.LibvirtConfigGuestMetaNovaPorts(
ports=[
config.LibvirtConfigGuestMetaNovaPort(
'567a4527-b0e4-4d0a-bcc2-71fda37897f7',
ips=[
config.LibvirtConfigGuestMetaNovaIp(
'fixed', '192.168.1.1', '4'),
config.LibvirtConfigGuestMetaNovaIp(
'fixed', 'fe80::f95c:b030:7094', '6'),
config.LibvirtConfigGuestMetaNovaIp(
'floating', '11.22.33.44', '4')]),
config.LibvirtConfigGuestMetaNovaPort(
'a3ca97e2-0cf9-4159-9bfc-afd55bc13ead',
ips=[
config.LibvirtConfigGuestMetaNovaIp(
'fixed', '10.0.0.1', '4'),
config.LibvirtConfigGuestMetaNovaIp(
'fixed', 'fdf8:f53b:82e4::52', '6'),
config.LibvirtConfigGuestMetaNovaIp(
'floating', '1.2.3.4', '4')])])
xml = meta.to_xml()
self.assertXmlEqual(xml, """
<nova:instance xmlns:nova='http://openstack.org/xmlns/libvirt/nova/1.1'>
<nova:package version="2014.2.3"/>
<nova:name>moonbuggy</nova:name>
<nova:creationTime>2009-02-13 23:31:30</nova:creationTime>
<nova:flavor name="m1.lowgravity"
id="f719a0dd-4b43-4efe-8336-48ef74099ad4">
<nova:memory>2048</nova:memory>
<nova:disk>50</nova:disk>
<nova:swap>10</nova:swap>
<nova:ephemeral>10</nova:ephemeral>
<nova:vcpus>8</nova:vcpus>
<nova:extraSpecs></nova:extraSpecs>
</nova:flavor>
<nova:image uuid="">
<nova:properties></nova:properties>
</nova:image>
<nova:owner>
<nova:user
uuid="3472c2a6-de91-4fb5-b618-42bc781ef670">buzz</nova:user>
<nova:project
uuid="f241e906-010e-4917-ae81-53f4fb8aa021">moonshot</nova:project>
</nova:owner>
<nova:ports>
<nova:port uuid="567a4527-b0e4-4d0a-bcc2-71fda37897f7">
<nova:ip type="fixed" address="192.168.1.1" ipVersion="4"/>
<nova:ip type="fixed" address="fe80::f95c:b030:7094" ipVersion="6"/>
<nova:ip type="floating" address="11.22.33.44" ipVersion="4"/>
</nova:port>
<nova:port uuid="a3ca97e2-0cf9-4159-9bfc-afd55bc13ead">
<nova:ip type="fixed" address="10.0.0.1" ipVersion="4"/>
<nova:ip type="fixed" address="fdf8:f53b:82e4::52" ipVersion="6"/>
<nova:ip type="floating" address="1.2.3.4" ipVersion="4"/>
</nova:port>
</nova:ports>
</nova:instance>
""")
class LibvirtConfigGuestIDMap(LibvirtConfigBaseTest):
def test_config_id_map_parse_start_not_int(self):

View File

@@ -665,7 +665,8 @@ class FakeNodeDevice(object):
return self.xml
def _create_test_instance():
def _create_test_instance(image_ref='155d900f-4e14-4e4c-a73d-069cbf4541e6',
system_metadata=None):
flavor = objects.Flavor(memory_mb=2048,
swap=0,
vcpu_weight=None,
@@ -677,6 +678,9 @@ def _create_test_instance():
flavorid=u'1',
vcpus=2,
extra_specs={})
if system_metadata is None:
system_metadata = {'image_base_image_ref': image_ref,
'image_disk_format': 'raw'}
return {
'id': 1,
'uuid': uuids.instance,
@@ -687,13 +691,10 @@ def _create_test_instance():
'vcpus': 2,
'project_id': 'fake',
'bridge': 'br101',
'image_ref': '155d900f-4e14-4e4c-a73d-069cbf4541e6',
'image_ref': image_ref,
'root_gb': 10,
'ephemeral_gb': 20,
'system_metadata': {
'image_base_image_ref': '155d900f-4e14-4e4c-a73d-069cbf4541e6',
'image_disk_format': 'raw'
},
'system_metadata': system_metadata,
'instance_type_id': flavor.id,
'flavor': flavor,
'new_flavor': None,
@@ -2945,6 +2946,104 @@ class LibvirtConnTestCase(test.NoDBTestCase,
"image_base_image_ref"],
meta.rootid)
def test_get_guest_config_meta_from_image(self):
image_id = '85daefce-4e20-4d2b-a4f3-11d3765f2a8f'
instance = _create_test_instance(
image_ref=image_id,
system_metadata={
'image_base_image_ref': image_id,
'image_container_format': 'bare',
'image_disk_format': 'raw',
'image_min_disk': 10,
'image_min_ram': 0,
'image_os_type': 'linux'})
instance['info_cache'] = self.test_instance['info_cache']
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
idm = drvr.get_instance_driver_metadata(
objects.Instance(**instance),
_fake_network_info(self, num_networks=0))
meta = drvr._get_guest_config_meta(idm)
self.assertEqual(meta.image.uuid, image_id)
image_meta = meta.image.image_meta
self.assertEqual(image_meta.container_format, 'bare')
self.assertEqual(image_meta.disk_format, 'raw')
self.assertEqual(image_meta.min_disk, 10)
self.assertEqual(image_meta.min_ram, 0)
self.assertEqual(dict(image_meta.properties), {'os_type': 'linux'})
def test_get_guest_config_meta_from_image_unshelved(self):
instance = _create_test_instance(
image_ref='e3e66d4e-43ba-4e3b-8a1d-46cb78a0b527',
system_metadata={
'image_base_image_ref': (
'155d900f-4e14-4e4c-a73d-069cbf4541e6'),
'container_format': 'bare',
'disk_format': 'raw',
'min_disk': 1,
'min_ram': 0,
'image_os_type': 'linux'})
instance['vm_state'] = 'shelved_offloaded'
instance['info_cache'] = self.test_instance['info_cache']
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
idm = drvr.get_instance_driver_metadata(
objects.Instance(**instance),
_fake_network_info(self, num_networks=0))
meta = drvr._get_guest_config_meta(idm)
self.assertEqual(meta.image.uuid,
'155d900f-4e14-4e4c-a73d-069cbf4541e6')
image_meta = meta.image.image_meta
self.assertEqual(image_meta.container_format, 'bare')
self.assertEqual(image_meta.disk_format, 'raw')
self.assertEqual(image_meta.min_disk, 1)
self.assertEqual(image_meta.min_ram, 0)
self.assertEqual(dict(image_meta.properties), {'os_type': 'linux'})
def test_get_guest_config_meta_from_volume_image(self):
instance = _create_test_instance(
image_ref='',
system_metadata={
'image_base_image_ref': '',
'image_container_format': 'bare',
'image_disk_format': 'raw',
'image_min_disk': 10,
'image_min_ram': 0,
'image_os_type': 'linux'})
instance['info_cache'] = self.test_instance['info_cache']
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
idm = drvr.get_instance_driver_metadata(
objects.Instance(**instance),
_fake_network_info(self, num_networks=0))
meta = drvr._get_guest_config_meta(idm)
self.assertEqual(meta.image.uuid, '')
image_meta = meta.image.image_meta
self.assertEqual(image_meta.container_format, 'bare')
self.assertEqual(image_meta.disk_format, 'raw')
self.assertEqual(image_meta.min_disk, 10)
self.assertEqual(image_meta.min_ram, 0)
self.assertEqual(dict(image_meta.properties), {'os_type': 'linux'})
def test_get_guest_config_meta_from_volume_no_image(self):
instance = _create_test_instance(
image_ref='',
system_metadata={'image_base_image_ref': ''})
instance['info_cache'] = self.test_instance['info_cache']
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
idm = drvr.get_instance_driver_metadata(
objects.Instance(**instance),
_fake_network_info(self, num_networks=0))
meta = drvr._get_guest_config_meta(idm)
self.assertEqual(meta.image.uuid, '')
image_meta = meta.image.image_meta
self.assertIsNone(image_meta.container_format)
self.assertIsNone(image_meta.disk_format)
self.assertIsNone(image_meta.min_disk)
self.assertIsNone(image_meta.min_ram)
self.assertEqual(dict(image_meta.properties), {})
@mock.patch.object(time, "time")
def test_get_guest_config(self, time_mock):
"""Generate a "standard" guest with minimal configuration.

View File

@@ -62,8 +62,39 @@ class FlavorMeta:
class ImageMeta:
id: str
name: str
container_format: str | None
disk_format: str | None
min_disk: int | None
min_ram: int | None
properties: dict
@staticmethod
def from_instance(
instance: 'nova.objects.instance.Instance',
) -> 'ImageMeta':
image_meta = instance.image_meta
return ImageMeta(
id=instance.image_ref,
name=instance.system_metadata.get('image_name'),
container_format=(
image_meta.container_format
if image_meta.obj_attr_is_set('container_format')
else None),
disk_format=(
image_meta.disk_format
if image_meta.obj_attr_is_set('disk_format')
else None),
min_disk=(
image_meta.min_disk
if image_meta.obj_attr_is_set('min_disk')
else None),
min_ram=(
image_meta.min_ram
if image_meta.obj_attr_is_set('min_ram')
else None),
properties=image_meta.properties.to_dict(),
)
@dataclasses.dataclass
class NovaInstanceMeta:
@@ -385,11 +416,7 @@ class ComputeDriver(object):
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
)
image = ImageMeta.from_instance(instance)
meta = InstanceDriverMetadata(
instance_meta=instance_meta,
owner=owner,

View File

@@ -3760,6 +3760,7 @@ class LibvirtConfigGuestMetaNovaInstance(LibvirtConfigObject):
self.package = None
self.flavor = None
self.image = None
self.name = None
self.creationTime = None
self.owner = None
@@ -3781,6 +3782,8 @@ class LibvirtConfigGuestMetaNovaInstance(LibvirtConfigObject):
meta.append(self._text_node("creationTime", timestr))
if self.flavor is not None:
meta.append(self.flavor.format_dom())
if self.image is not None:
meta.append(self.image.format_dom())
if self.owner is not None:
meta.append(self.owner.format_dom())
@@ -3851,6 +3854,56 @@ class LibvirtConfigGuestMetaNovaFlavorExtraSpecs(LibvirtConfigObject):
return meta
class LibvirtConfigGuestMetaImage(LibvirtConfigObject):
def __init__(self):
super().__init__(root_name="image",
ns_prefix="nova",
ns_uri=NOVA_NS)
self.uuid = None
self.image_meta = None
# NOTE(callumdickinson): Based on the values of SM_INHERITABLE_KEYS.
self.attrs = [
("container_format", "containerFormat"),
("disk_format", "diskFormat"),
("min_disk", "minDisk"),
("min_ram", "minRam")]
def format_dom(self):
meta = super().format_dom()
# uuid can be empty for instances booted from volume.
meta.set("uuid", self.uuid or "")
if self.image_meta is not None:
for attr, node_key in self.attrs:
value = getattr(self.image_meta, attr)
if value is not None:
node = self._text_node(node_key, value)
meta.append(node)
properties_meta = LibvirtConfigGuestMetaImageProperties()
if self.image_meta is not None:
properties_meta.properties = self.image_meta.properties
meta.append(properties_meta.format_dom())
return meta
class LibvirtConfigGuestMetaImageProperties(LibvirtConfigObject):
def __init__(self):
super().__init__(root_name="properties",
ns_prefix="nova",
ns_uri=NOVA_NS)
self.properties = None
def format_dom(self):
meta = super().format_dom()
if self.properties is not None:
for attr, value in self.properties.items():
node = self._text_node("property", value)
node.set("name", attr)
meta.append(node)
return meta
class LibvirtConfigGuestMetaNovaOwner(LibvirtConfigObject):
def __init__(self):

View File

@@ -6241,6 +6241,13 @@ class LibvirtDriver(driver.ComputeDriver):
meta.roottype = dmeta.root_type
meta.rootid = dmeta.root_id
# Always set the image meta, even when booting from volume
# as volumes can contain image meta as well.
imeta = vconfig.LibvirtConfigGuestMetaImage()
imeta.uuid = meta.rootid
imeta.image_meta = dmeta.image
meta.image = imeta
ometa = vconfig.LibvirtConfigGuestMetaNovaOwner()
ometa.userid = dmeta.owner.userid
ometa.username = dmeta.owner.username

View File

@@ -7,6 +7,14 @@ features:
This allows downstream clients that queries libvirt domain metadata,
such as Ceilometer, to avoid performing additional Nova API queries
to get this information.
- |
Additional attributes relating to the image metadata an instance is
configured with have been added to the libvirt domain metadata under
the ``<nova:image>`` element. This allows downstream services that
read libvirt domain metadata, such as Ceilometer, to use and expose
more information about an instance without needing to perform
additional API queries (potentially to multiple services) to get that
information.
upgrades:
- |