libvirt: Add vTPM config support

There are two changes to our XML element generation code needed for vTPM
support: the '<secret>' element needs to gain support for the 'vtpm'
usage type, and a wholly new '<tpm>' element [1].

None of this is actually used yet, outside of tests. That will come
later.

Part of blueprint add-emulated-virtual-tpm

[1] https://libvirt.org/formatsecret.html
[2] https://libvirt.org/formatdomain.html#elementsTpm

Change-Id: I8b7d2d45963160bc8acf19f36f7945c7545570b3
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane 2020-07-03 10:26:45 +01:00
parent 969a6d49e5
commit e3b0412dda
5 changed files with 121 additions and 14 deletions

View File

@ -19,6 +19,7 @@ from oslo_utils import units
from nova.objects import fields as obj_fields
from nova import test
from nova.tests.unit.virt.libvirt import fake_libvirt_data
from nova.virt import hardware
from nova.virt.libvirt import config
@ -3729,8 +3730,8 @@ class LibvirtConfigSecretTest(LibvirtConfigBaseTest):
def test_config_secret_volume(self):
secret = config.LibvirtConfigSecret()
secret.ephemeral = True
secret.private = True
secret.ephemeral = False
secret.private = False
secret.description = 'sample desc'
secret.uuid = 'c7a5fdbd-edaf-9455-926a-d65c16db1809'
secret.usage_type = 'volume'
@ -3738,7 +3739,7 @@ class LibvirtConfigSecretTest(LibvirtConfigBaseTest):
xml = secret.to_xml()
expected_xml = """
<secret ephemeral="yes" private="yes">
<secret ephemeral="no" private="no">
<description>sample desc</description>
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
<usage type="volume">
@ -3750,15 +3751,15 @@ class LibvirtConfigSecretTest(LibvirtConfigBaseTest):
def test_config_secret_ceph(self):
secret = config.LibvirtConfigSecret()
secret.ephemeral = True
secret.private = True
secret.ephemeral = False
secret.private = False
secret.description = 'sample desc'
secret.usage_type = 'ceph'
secret.usage_id = 'sample_name'
xml = secret.to_xml()
expected_xml = """
<secret ephemeral="yes" private="yes">
<secret ephemeral="no" private="no">
<description>sample desc</description>
<usage type="ceph">
<name>sample_name</name>
@ -3767,17 +3768,36 @@ class LibvirtConfigSecretTest(LibvirtConfigBaseTest):
self.assertXmlEqual(expected_xml, xml)
def test_config_secret_iscsi(self):
def test_config_secret_vtpm(self):
secret = config.LibvirtConfigSecret()
secret.ephemeral = True
secret.private = True
secret.usage_type = 'vtpm'
secret.usage_id = 'sample_name'
secret.uuid = uuids.vtpm
xml = secret.to_xml()
expected_xml = f"""
<secret ephemeral="yes" private="yes">
<uuid>{uuids.vtpm}</uuid>
<usage type="vtpm">
<name>sample_name</name>
</usage>
</secret>"""
self.assertXmlEqual(expected_xml, xml)
def test_config_secret_iscsi(self):
secret = config.LibvirtConfigSecret()
secret.ephemeral = False
secret.private = False
secret.description = 'sample desc'
secret.usage_type = 'iscsi'
secret.usage_id = 'sample_target'
xml = secret.to_xml()
expected_xml = """
<secret ephemeral="yes" private="yes">
<secret ephemeral="no" private="no">
<description>sample desc</description>
<usage type="iscsi">
<target>sample_target</target>
@ -3937,3 +3957,51 @@ class LibvirtConfigDomainCapsDevicesTests(LibvirtConfigBaseTest):
obj.disk, config.LibvirtConfigDomainCapsDiskBuses)
self.assertIsInstance(
obj.video, config.LibvirtConfigDomainCapsVideoModels)
class LibvirtConfigTPMTest(LibvirtConfigBaseTest):
def test_config_tpm_tis_1_2(self):
vtpm_config = hardware.VTPMConfig('1.2', 'tpm-tis')
vtpm_secret_uuid = 'b028130c-bdcb-4d5f-9bca-b9175ca6c28c'
expected_xml = """
<tpm model='tpm-tis'>
<backend type='emulator' version='1.2'>
<encryption secret='b028130c-bdcb-4d5f-9bca-b9175ca6c28c'/>
</backend>
</tpm>"""
tpm = config.LibvirtConfigGuestVTPM(vtpm_config, vtpm_secret_uuid)
xml = tpm.to_xml()
self.assertXmlEqual(expected_xml, xml)
def test_config_tpm_tis_2_0(self):
vtpm_config = hardware.VTPMConfig('2.0', 'tpm-tis')
vtpm_secret_uuid = 'b028130c-bdcb-4d5f-9bca-b9175ca6c28c'
expected_xml = """
<tpm model='tpm-tis'>
<backend type='emulator' version='2.0'>
<encryption secret='b028130c-bdcb-4d5f-9bca-b9175ca6c28c'/>
</backend>
</tpm>"""
tpm = config.LibvirtConfigGuestVTPM(vtpm_config, vtpm_secret_uuid)
xml = tpm.to_xml()
self.assertXmlEqual(expected_xml, xml)
def test_config_tpm_crb_2_0(self):
vtpm_config = hardware.VTPMConfig('2.0', 'tpm-crb')
vtpm_secret_uuid = 'b028130c-bdcb-4d5f-9bca-b9175ca6c28c'
expected_xml = """
<tpm model='tpm-crb'>
<backend type='emulator' version='2.0'>
<encryption secret='b028130c-bdcb-4d5f-9bca-b9175ca6c28c'/>
</backend>
</tpm>"""
tpm = config.LibvirtConfigGuestVTPM(vtpm_config, vtpm_secret_uuid)
xml = tpm.to_xml()
self.assertXmlEqual(expected_xml, xml)

View File

@ -917,6 +917,7 @@ class HostTestCase(test.NoDBTestCase):
self.host.create_secret('ceph', 'cephvol')
self.host.create_secret('iscsi', 'iscsivol')
self.host.create_secret('volume', 'vol')
self.host.create_secret('vtpm', 'vtpmdev')
self.assertRaises(exception.NovaException,
self.host.create_secret, "foo", "foovol")

View File

@ -42,6 +42,11 @@ MEMPAGES_LARGE = -2
MEMPAGES_ANY = -3
class VTPMConfig(ty.NamedTuple):
version: str
model: str
def get_vcpu_pin_set():
"""Parse ``vcpu_pin_set`` config.

View File

@ -958,6 +958,33 @@ class LibvirtConfigGuestDevice(LibvirtConfigObject):
return False
class LibvirtConfigGuestVTPM(LibvirtConfigGuestDevice):
def __init__(self, vtpm_config, vtpm_secret_uuid, **kwargs):
super(LibvirtConfigGuestVTPM, self).__init__(root_name="tpm", **kwargs)
self.version = vtpm_config.version
self.model = vtpm_config.model
self.secret_uuid = vtpm_secret_uuid
def format_dom(self):
# <tpm model='$model'>
dev = super(LibvirtConfigGuestVTPM, self).format_dom()
dev.set("model", self.model)
# <backend type='emulator' version='$version'>
back = etree.Element("backend")
back.set("type", "emulator")
back.set("version", self.version)
# <encryption secret='$secret_uuid'/>
enc = etree.Element("encryption")
enc.set("secret", self.secret_uuid)
back.append(enc)
dev.append(back)
return dev
class LibvirtConfigGuestDisk(LibvirtConfigGuestDevice):
def __init__(self, **kwargs):
@ -3272,7 +3299,7 @@ class LibvirtConfigSecret(LibvirtConfigObject):
root.append(self._text_node("uuid", str(self.uuid)))
usage = self._new_node("usage")
usage.set("type", self.usage_type)
if self.usage_type == 'ceph':
if self.usage_type in ('ceph', 'vtpm'):
usage.append(self._text_node('name', str(self.usage_id)))
elif self.usage_type == 'iscsi':
usage.append(self._text_node('target', str(self.usage_id)))

View File

@ -996,24 +996,30 @@ class Host(object):
if e.get_error_code() == libvirt.VIR_ERR_NO_SECRET:
return None
def create_secret(self, usage_type, usage_id, password=None):
def create_secret(self, usage_type, usage_id, password=None, uuid=None):
"""Create a secret.
:param usage_type: one of 'iscsi', 'ceph', 'rbd' or 'volume'
'rbd' will be converted to 'ceph'.
:param usage_type: one of 'iscsi', 'ceph', 'rbd', 'volume', 'vtpm'.
'rbd' will be converted to 'ceph'. 'vtpm' secrets
are private and ephemeral; others are not.
:param usage_id: name of resource in secret
:param password: optional secret value to set
:param uuid: optional UUID of the secret; else one is generated by
libvirt
"""
secret_conf = vconfig.LibvirtConfigSecret()
secret_conf.ephemeral = False
secret_conf.private = False
secret_conf.ephemeral = usage_type == 'vtpm'
secret_conf.private = usage_type == 'vtpm'
secret_conf.usage_id = usage_id
secret_conf.uuid = uuid
if usage_type in ('rbd', 'ceph'):
secret_conf.usage_type = 'ceph'
elif usage_type == 'iscsi':
secret_conf.usage_type = 'iscsi'
elif usage_type == 'volume':
secret_conf.usage_type = 'volume'
elif usage_type == 'vtpm':
secret_conf.usage_type = 'vtpm'
else:
msg = _("Invalid usage_type: %s")
raise exception.InternalError(msg % usage_type)