libvirt: Support VM creation with vpmems and vpmems cleanup
Add the vpmems config into guest config xml according to resources info in the instance object, then users can build a VM with vpmems. Also this patch add the support for data cleanup on the backend device of the vpmems. note: We modify the root <domain> element generated for libvirt to include <maxMemory> elements when memory device is used.[1] Requiring vpmems implies a NUMA topology because libvirt won't let us use vpmem without NUMA.[2] [1]https://github.com/libvirt/libvirt/blob/master/src/qemu/qemu_domain.c#L11593-L11599 [2]https://github.com/libvirt/libvirt/blob/master/src/qemu/qemu_domain.c#L11604-L11615 Change-Id: I725deb0312c930087c9e60115abe68b4e06e6804 Partially-Implements: blueprint virtual-persistent-memory Co-Authored-By: He Jie Xu <hejie.xu@intel.com>
This commit is contained in:
parent
d7c07584f1
commit
c39ad2383c
@ -2532,3 +2532,8 @@ class PMEMNamespaceConfigInvalid(NovaException):
|
|||||||
|
|
||||||
class GetPMEMNamespaceFailed(NovaException):
|
class GetPMEMNamespaceFailed(NovaException):
|
||||||
msg_fmt = _("Get PMEM namespaces on host failed: %(reason)s.")
|
msg_fmt = _("Get PMEM namespaces on host failed: %(reason)s.")
|
||||||
|
|
||||||
|
|
||||||
|
class VPMEMCleanupFailed(NovaException):
|
||||||
|
msg_fmt = _("Failed to clean up the vpmem backend device %(dev)s: "
|
||||||
|
"%(error)s")
|
||||||
|
@ -253,3 +253,9 @@ def get_pmem_namespaces():
|
|||||||
ndctl_cmd = ['ndctl', 'list', '-X']
|
ndctl_cmd = ['ndctl', 'list', '-X']
|
||||||
nss_info = processutils.execute(*ndctl_cmd)[0]
|
nss_info = processutils.execute(*ndctl_cmd)[0]
|
||||||
return nss_info
|
return nss_info
|
||||||
|
|
||||||
|
|
||||||
|
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||||
|
def cleanup_vpmem(devpath):
|
||||||
|
daxio_cmd = ['daxio', '-z', '-o', '%s' % devpath]
|
||||||
|
processutils.execute(*daxio_cmd)
|
||||||
|
@ -864,6 +864,24 @@ class Domain(object):
|
|||||||
})
|
})
|
||||||
devices['hostdevs'] = hostdev_info
|
devices['hostdevs'] = hostdev_info
|
||||||
|
|
||||||
|
vpmem_info = []
|
||||||
|
vpmems = device_nodes.findall('./memory')
|
||||||
|
for vpmem in vpmems:
|
||||||
|
model = vpmem.get('model')
|
||||||
|
if model == 'nvdimm':
|
||||||
|
source = vpmem.find('./source')
|
||||||
|
target = vpmem.find('./target')
|
||||||
|
path = source.find('./path').text
|
||||||
|
alignsize = source.find('./alignsize').text
|
||||||
|
size = target.find('./size').text
|
||||||
|
node = target.find('./node').text
|
||||||
|
vpmem_info.append({
|
||||||
|
'path': path,
|
||||||
|
'size': size,
|
||||||
|
'alignsize': alignsize,
|
||||||
|
'node': node})
|
||||||
|
devices['vpmems'] = vpmem_info
|
||||||
|
|
||||||
definition['devices'] = devices
|
definition['devices'] = devices
|
||||||
|
|
||||||
return definition
|
return definition
|
||||||
@ -1023,6 +1041,25 @@ class Domain(object):
|
|||||||
</hostdev>
|
</hostdev>
|
||||||
''' % hostdev # noqa
|
''' % hostdev # noqa
|
||||||
|
|
||||||
|
vpmems = ''
|
||||||
|
for vpmem in self._def['devices']['vpmems']:
|
||||||
|
vpmems += '''
|
||||||
|
<memory model='nvdimm' access='shared'>
|
||||||
|
<source>
|
||||||
|
<path>%(path)s</path>
|
||||||
|
<alignsize>%(alignsize)s</alignsize>
|
||||||
|
<pmem/>
|
||||||
|
</source>
|
||||||
|
<target>
|
||||||
|
<size>%(size)s</size>
|
||||||
|
<node>%(node)s</node>
|
||||||
|
<label>
|
||||||
|
<size>2097152</size>
|
||||||
|
</label>
|
||||||
|
</target>
|
||||||
|
</memory>
|
||||||
|
''' % vpmem
|
||||||
|
|
||||||
return '''<domain type='kvm'>
|
return '''<domain type='kvm'>
|
||||||
<name>%(name)s</name>
|
<name>%(name)s</name>
|
||||||
<uuid>%(uuid)s</uuid>
|
<uuid>%(uuid)s</uuid>
|
||||||
@ -1079,6 +1116,7 @@ class Domain(object):
|
|||||||
function='0x0'/>
|
function='0x0'/>
|
||||||
</memballoon>
|
</memballoon>
|
||||||
%(hostdevs)s
|
%(hostdevs)s
|
||||||
|
%(vpmems)s
|
||||||
</devices>
|
</devices>
|
||||||
</domain>''' % {'name': self._def['name'],
|
</domain>''' % {'name': self._def['name'],
|
||||||
'uuid': self._def['uuid'],
|
'uuid': self._def['uuid'],
|
||||||
@ -1087,7 +1125,8 @@ class Domain(object):
|
|||||||
'arch': self._def['os']['arch'],
|
'arch': self._def['os']['arch'],
|
||||||
'disks': disks,
|
'disks': disks,
|
||||||
'nics': nics,
|
'nics': nics,
|
||||||
'hostdevs': hostdevs}
|
'hostdevs': hostdevs,
|
||||||
|
'vpmems': vpmems}
|
||||||
|
|
||||||
def managedSave(self, flags):
|
def managedSave(self, flags):
|
||||||
self._connection._mark_not_running(self)
|
self._connection._mark_not_running(self)
|
||||||
|
@ -3758,3 +3758,26 @@ class LibvirtConfigSecretTest(LibvirtConfigBaseTest):
|
|||||||
</secret>"""
|
</secret>"""
|
||||||
|
|
||||||
self.assertXmlEqual(expected_xml, xml)
|
self.assertXmlEqual(expected_xml, xml)
|
||||||
|
|
||||||
|
|
||||||
|
class LibvirtConfigGuestVPMEMTest(LibvirtConfigBaseTest):
|
||||||
|
def test_config_vpmem(self):
|
||||||
|
obj = config.LibvirtConfigGuestVPMEM(
|
||||||
|
devpath='/dev/dax0.0', size_kb=4096 * units.Ki, align_kb=2048)
|
||||||
|
|
||||||
|
xml = obj.to_xml()
|
||||||
|
self.assertXmlEqual(xml, """
|
||||||
|
<memory model='nvdimm' access="shared">
|
||||||
|
<source>
|
||||||
|
<path>/dev/dax0.0</path>
|
||||||
|
<alignsize>2048</alignsize>
|
||||||
|
<pmem/>
|
||||||
|
</source>
|
||||||
|
<target>
|
||||||
|
<size>4194304</size>
|
||||||
|
<node>0</node>
|
||||||
|
<label>
|
||||||
|
<size>2048</size>
|
||||||
|
</label>
|
||||||
|
</target>
|
||||||
|
</memory>""")
|
||||||
|
@ -902,7 +902,8 @@ def _create_test_instance():
|
|||||||
'host': 'fake-host',
|
'host': 'fake-host',
|
||||||
'task_state': None,
|
'task_state': None,
|
||||||
'vm_state': None,
|
'vm_state': None,
|
||||||
'trusted_certs': None
|
'trusted_certs': None,
|
||||||
|
'resources': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -14709,7 +14710,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||||||
mock_unplug_vifs):
|
mock_unplug_vifs):
|
||||||
instance = fake_instance.fake_instance_obj(
|
instance = fake_instance.fake_instance_obj(
|
||||||
None, name='instancename', id=1,
|
None, name='instancename', id=1,
|
||||||
uuid='875a8070-d0b9-4949-8b31-104d125c9a64')
|
uuid='875a8070-d0b9-4949-8b31-104d125c9a64',
|
||||||
|
expected_attrs=['resources'])
|
||||||
|
|
||||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||||
drvr.destroy(self.context, instance, [], None, False)
|
drvr.destroy(self.context, instance, [], None, False)
|
||||||
@ -18194,7 +18196,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI())
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI())
|
||||||
drvr.firewall_driver = mock.Mock()
|
drvr.firewall_driver = mock.Mock()
|
||||||
drvr._disconnect_volume = mock.Mock()
|
drvr._disconnect_volume = mock.Mock()
|
||||||
fake_inst = {'name': 'foo'}
|
fake_inst = objects.Instance(**self.test_instance)
|
||||||
fake_bdms = [{'connection_info': 'foo',
|
fake_bdms = [{'connection_info': 'foo',
|
||||||
'mount_device': None}]
|
'mount_device': None}]
|
||||||
with mock.patch('nova.virt.driver'
|
with mock.patch('nova.virt.driver'
|
||||||
@ -18207,7 +18209,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||||||
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._undefine_domain')
|
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._undefine_domain')
|
||||||
def test_cleanup_wants_vif_errors_ignored(self, undefine, unplug):
|
def test_cleanup_wants_vif_errors_ignored(self, undefine, unplug):
|
||||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI())
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI())
|
||||||
fake_inst = {'name': 'foo'}
|
fake_inst = objects.Instance(**self.test_instance)
|
||||||
with mock.patch.object(drvr._conn, 'lookupByUUIDString') as lookup:
|
with mock.patch.object(drvr._conn, 'lookupByUUIDString') as lookup:
|
||||||
lookup.return_value = fake_inst
|
lookup.return_value = fake_inst
|
||||||
# NOTE(danms): Make unplug cause us to bail early, since
|
# NOTE(danms): Make unplug cause us to bail early, since
|
||||||
@ -18951,6 +18953,41 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||||||
else:
|
else:
|
||||||
assert False, "Unable to find any mediated device for the guest."
|
assert False, "Unable to find any mediated device for the guest."
|
||||||
|
|
||||||
|
@mock.patch('nova.virt.hardware.get_vpmems')
|
||||||
|
def test_get_guest_config_with_vpmems(self, mock_get_vpmems_label):
|
||||||
|
vpmem_0 = objects.LibvirtVPMEMDevice(
|
||||||
|
label='4GB', name='ns_0', devpath='/dev/dax0.0',
|
||||||
|
size=4292870144, align=2097152)
|
||||||
|
vpmem_1 = objects.LibvirtVPMEMDevice(
|
||||||
|
label='16GB', name='ns_1', devpath='/dev/dax0.1',
|
||||||
|
size=17177772032, align=2097152)
|
||||||
|
resource_0 = objects.Resource(
|
||||||
|
provider_uuid=uuids.rp,
|
||||||
|
resource_class="CUSTOM_PMEM_NAMESPACE_4GB",
|
||||||
|
identifier='ns_0', metadata=vpmem_0)
|
||||||
|
resource_1 = objects.Resource(
|
||||||
|
provider_uuid=uuids.rp,
|
||||||
|
resource_class="CUSTOM_PMEM_NAMESPACE_16GB",
|
||||||
|
identifier='ns_1', metadata=vpmem_1)
|
||||||
|
resources = objects.ResourceList(objects=[resource_0, resource_1])
|
||||||
|
|
||||||
|
instance_ref = objects.Instance(**self.test_instance)
|
||||||
|
instance_ref.resources = resources
|
||||||
|
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
|
||||||
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||||
|
drvr._vpmems_by_name = {"ns_0": vpmem_0, "ns_1": vpmem_1}
|
||||||
|
|
||||||
|
mock_get_vpmems_label.return_value = ['4GB', '16GB']
|
||||||
|
cfg = drvr._get_guest_config(instance_ref,
|
||||||
|
_fake_network_info(self, 1),
|
||||||
|
image_meta, {'mapping': {}})
|
||||||
|
vpmem_amount = 0
|
||||||
|
for device in cfg.devices:
|
||||||
|
if isinstance(device, vconfig.LibvirtConfigGuestVPMEM):
|
||||||
|
self.assertEqual("nvdimm", device.model)
|
||||||
|
vpmem_amount += 1
|
||||||
|
self.assertEqual(2, vpmem_amount)
|
||||||
|
|
||||||
|
|
||||||
class TestGuestConfigSysinfoSerialOS(test.NoDBTestCase):
|
class TestGuestConfigSysinfoSerialOS(test.NoDBTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -19702,7 +19739,8 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
|
|||||||
|
|
||||||
# Attributes which we need to be set so they don't touch the db,
|
# Attributes which we need to be set so they don't touch the db,
|
||||||
# but it's not worth the effort to fake properly
|
# but it's not worth the effort to fake properly
|
||||||
for field in ['numa_topology', 'vcpu_model', 'trusted_certs']:
|
for field in ['numa_topology', 'vcpu_model', 'trusted_certs',
|
||||||
|
'resources', 'migration_context']:
|
||||||
setattr(instance, field, None)
|
setattr(instance, field, None)
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
@ -21770,7 +21808,8 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
|
|||||||
drv = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
drv = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||||
instance = objects.Instance(
|
instance = objects.Instance(
|
||||||
uuid=uuids.instance, id=1,
|
uuid=uuids.instance, id=1,
|
||||||
ephemeral_key_uuid=uuids.ephemeral_key_uuid)
|
ephemeral_key_uuid=uuids.ephemeral_key_uuid,
|
||||||
|
resources=None)
|
||||||
instance.system_metadata = {}
|
instance.system_metadata = {}
|
||||||
block_device_info = {'root_device_name': '/dev/vda',
|
block_device_info = {'root_device_name': '/dev/vda',
|
||||||
'ephemerals': [],
|
'ephemerals': [],
|
||||||
@ -24319,6 +24358,7 @@ class LibvirtPMEMNamespaceTests(test.NoDBTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(LibvirtPMEMNamespaceTests, self).setUp()
|
super(LibvirtPMEMNamespaceTests, self).setUp()
|
||||||
self.useFixture(fakelibvirt.FakeLibvirtFixture())
|
self.useFixture(fakelibvirt.FakeLibvirtFixture())
|
||||||
|
self.context = context.get_admin_context()
|
||||||
self.vpmem_0 = objects.LibvirtVPMEMDevice(
|
self.vpmem_0 = objects.LibvirtVPMEMDevice(
|
||||||
label='4GB',
|
label='4GB',
|
||||||
name='ns_0', devpath='/dev/dax0.0',
|
name='ns_0', devpath='/dev/dax0.0',
|
||||||
@ -24331,6 +24371,22 @@ class LibvirtPMEMNamespaceTests(test.NoDBTestCase):
|
|||||||
label='SMALL',
|
label='SMALL',
|
||||||
name='ns_2', devpath='/dev/dax0.2',
|
name='ns_2', devpath='/dev/dax0.2',
|
||||||
size=17177772032, align=2097152)
|
size=17177772032, align=2097152)
|
||||||
|
self.resource_0 = objects.Resource(
|
||||||
|
provider_uuid=uuids.rp_uuid,
|
||||||
|
resource_class="CUSTOM_PMEM_NAMESPACE_4GB",
|
||||||
|
identifier='ns_0', metadata=self.vpmem_0)
|
||||||
|
self.resource_1 = objects.Resource(
|
||||||
|
provider_uuid=uuids.rp_uuid,
|
||||||
|
resource_class="CUSTOM_PMEM_NAMESPACE_SMALL",
|
||||||
|
identifier='ns_1', metadata=self.vpmem_1)
|
||||||
|
self.resource_2 = objects.Resource(
|
||||||
|
provider_uuid=uuids.rp_uuid,
|
||||||
|
resource_class="CUSTOM_PMEM_NAMESPACE_SMALL",
|
||||||
|
identifier='ns_2', metadata=self.vpmem_2)
|
||||||
|
self.resource_3 = objects.Resource(
|
||||||
|
provider_uuid=uuids.rp_uuid,
|
||||||
|
resource_class="CUSTOM_RESOURCE_0",
|
||||||
|
identifier='resource_0')
|
||||||
|
|
||||||
self.pmem_namespaces = '''
|
self.pmem_namespaces = '''
|
||||||
[{"dev":"namespace0.0",
|
[{"dev":"namespace0.0",
|
||||||
@ -24417,3 +24473,49 @@ class LibvirtPMEMNamespaceTests(test.NoDBTestCase):
|
|||||||
vpmem_conf = ["4GB:ns_0", "SMALL:ns_0"]
|
vpmem_conf = ["4GB:ns_0", "SMALL:ns_0"]
|
||||||
self.assertRaises(exception.PMEMNamespaceConfigInvalid,
|
self.assertRaises(exception.PMEMNamespaceConfigInvalid,
|
||||||
drvr._discover_vpmems, vpmem_conf)
|
drvr._discover_vpmems, vpmem_conf)
|
||||||
|
|
||||||
|
@mock.patch('nova.virt.hardware.get_vpmems')
|
||||||
|
def test_get_ordered_vpmems(self, mock_labels):
|
||||||
|
# get orgered vpmems based on flavor extra_specs
|
||||||
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||||
|
drvr._vpmems_by_name = {'ns_0': self.vpmem_0,
|
||||||
|
'ns_1': self.vpmem_1,
|
||||||
|
'ns_2': self.vpmem_2}
|
||||||
|
instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
instance.flavor = objects.Flavor(
|
||||||
|
name='m1.small', memory_mb=2048, vcpus=2, root_gb=10,
|
||||||
|
ephemeral_gb=20, swap=0, extra_specs={
|
||||||
|
'hw:pmem': 'SMALL,4GB,SMALL'})
|
||||||
|
mock_labels.return_value = ['SMALL', '4GB', 'SMALL']
|
||||||
|
# self.resource_3 is not vpmem resource
|
||||||
|
instance.resources = objects.ResourceList(objects=[
|
||||||
|
self.resource_0, self.resource_1,
|
||||||
|
self.resource_2, self.resource_3])
|
||||||
|
ordered_vpmems = drvr._get_ordered_vpmems(instance, instance.flavor)
|
||||||
|
# keep consistent with the order in flavor extra_specs
|
||||||
|
self.assertEqual('SMALL', ordered_vpmems[0].label)
|
||||||
|
self.assertEqual('4GB', ordered_vpmems[1].label)
|
||||||
|
self.assertEqual('SMALL', ordered_vpmems[2].label)
|
||||||
|
vpmems = drvr._get_vpmems(instance)
|
||||||
|
# this is not sorted, keep the same as instance.resources
|
||||||
|
self.assertEqual('4GB', vpmems[0].label)
|
||||||
|
self.assertEqual('SMALL', vpmems[1].label)
|
||||||
|
self.assertEqual('SMALL', vpmems[2].label)
|
||||||
|
|
||||||
|
@mock.patch('nova.privsep.libvirt.cleanup_vpmem')
|
||||||
|
def test_cleanup_vpmems(self, mock_cleanup_vpmem):
|
||||||
|
vpmems = [self.vpmem_0, self.vpmem_1, self.vpmem_2]
|
||||||
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||||
|
drvr._cleanup_vpmems(vpmems)
|
||||||
|
mock_cleanup_vpmem.assert_has_calls([
|
||||||
|
mock.call(self.vpmem_0.devpath),
|
||||||
|
mock.call(self.vpmem_1.devpath),
|
||||||
|
mock.call(self.vpmem_2.devpath)])
|
||||||
|
|
||||||
|
@mock.patch('nova.privsep.libvirt.cleanup_vpmem')
|
||||||
|
def test_cleanup_vpmems_fail(self, mock_cleanup_vpmem):
|
||||||
|
mock_cleanup_vpmem.side_effect = Exception('Not known')
|
||||||
|
vpmems = [self.vpmem_0, self.vpmem_1, self.vpmem_2]
|
||||||
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||||
|
self.assertRaises(exception.VPMEMCleanupFailed,
|
||||||
|
drvr._cleanup_vpmems, vpmems)
|
||||||
|
@ -2066,3 +2066,14 @@ def numa_usage_from_instance_numa(host_topology, instance_topology,
|
|||||||
cells.append(new_cell)
|
cells.append(new_cell)
|
||||||
|
|
||||||
return objects.NUMATopology(cells=cells)
|
return objects.NUMATopology(cells=cells)
|
||||||
|
|
||||||
|
|
||||||
|
def get_vpmems(flavor):
|
||||||
|
"""Return vpmems related to input request.
|
||||||
|
|
||||||
|
:param flavor: a flavor object to read extra specs from
|
||||||
|
:returns: a vpmem label list
|
||||||
|
"""
|
||||||
|
# TODO(Luyao) Return vpmem label list when the whole
|
||||||
|
# vpmem feature is supported.
|
||||||
|
return []
|
||||||
|
@ -2546,6 +2546,8 @@ class LibvirtConfigGuest(LibvirtConfigObject):
|
|||||||
self.uuid = None
|
self.uuid = None
|
||||||
self.name = None
|
self.name = None
|
||||||
self.memory = 500 * units.Mi
|
self.memory = 500 * units.Mi
|
||||||
|
self.max_memory_size = None
|
||||||
|
self.max_memory_slots = 0
|
||||||
self.membacking = None
|
self.membacking = None
|
||||||
self.memtune = None
|
self.memtune = None
|
||||||
self.numatune = None
|
self.numatune = None
|
||||||
@ -2578,6 +2580,10 @@ class LibvirtConfigGuest(LibvirtConfigObject):
|
|||||||
root.append(self._text_node("uuid", self.uuid))
|
root.append(self._text_node("uuid", self.uuid))
|
||||||
root.append(self._text_node("name", self.name))
|
root.append(self._text_node("name", self.name))
|
||||||
root.append(self._text_node("memory", self.memory))
|
root.append(self._text_node("memory", self.memory))
|
||||||
|
if self.max_memory_size is not None:
|
||||||
|
max_memory = self._text_node("maxMemory", self.max_memory_size)
|
||||||
|
max_memory.set("slots", str(self.max_memory_slots))
|
||||||
|
root.append(max_memory)
|
||||||
if self.membacking is not None:
|
if self.membacking is not None:
|
||||||
root.append(self.membacking.format_dom())
|
root.append(self.membacking.format_dom())
|
||||||
if self.memtune is not None:
|
if self.memtune is not None:
|
||||||
@ -2752,6 +2758,7 @@ class LibvirtConfigGuest(LibvirtConfigObject):
|
|||||||
# LibvirtConfigGuestUidMap
|
# LibvirtConfigGuestUidMap
|
||||||
# LibvirtConfigGuestGidMap
|
# LibvirtConfigGuestGidMap
|
||||||
# LibvirtConfigGuestCPU
|
# LibvirtConfigGuestCPU
|
||||||
|
# LibvirtConfigGuestVPMEM
|
||||||
for c in xmldoc:
|
for c in xmldoc:
|
||||||
if c.tag == 'devices':
|
if c.tag == 'devices':
|
||||||
for d in c:
|
for d in c:
|
||||||
@ -2775,6 +2782,10 @@ class LibvirtConfigGuest(LibvirtConfigObject):
|
|||||||
obj = LibvirtConfigGuestInterface()
|
obj = LibvirtConfigGuestInterface()
|
||||||
obj.parse_dom(d)
|
obj.parse_dom(d)
|
||||||
self.devices.append(obj)
|
self.devices.append(obj)
|
||||||
|
elif d.tag == 'memory' and d.get('model') == 'nvdimm':
|
||||||
|
obj = LibvirtConfigGuestVPMEM()
|
||||||
|
obj.parse_dom(d)
|
||||||
|
self.devices.append(obj)
|
||||||
if c.tag == 'idmap':
|
if c.tag == 'idmap':
|
||||||
for idmap in c:
|
for idmap in c:
|
||||||
obj = None
|
obj = None
|
||||||
@ -3154,3 +3165,60 @@ class LibvirtConfigSecret(LibvirtConfigObject):
|
|||||||
usage.append(self._text_node('volume', str(self.usage_id)))
|
usage.append(self._text_node('volume', str(self.usage_id)))
|
||||||
root.append(usage)
|
root.append(usage)
|
||||||
return root
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
class LibvirtConfigGuestVPMEM(LibvirtConfigGuestDevice):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super(LibvirtConfigGuestVPMEM, self).__init__(
|
||||||
|
root_name="memory", **kwargs)
|
||||||
|
|
||||||
|
self.model = "nvdimm"
|
||||||
|
self.access = "shared"
|
||||||
|
self.source_path = kwargs.get("devpath", "")
|
||||||
|
self.align_size = kwargs.get("align_kb", 0)
|
||||||
|
self.pmem = True
|
||||||
|
|
||||||
|
self.target_size = kwargs.get("size_kb", 0)
|
||||||
|
self.target_node = 0
|
||||||
|
self.label_size = 2 * units.Ki
|
||||||
|
|
||||||
|
def format_dom(self):
|
||||||
|
memory = super(LibvirtConfigGuestVPMEM, self).format_dom()
|
||||||
|
|
||||||
|
memory.set("model", self.model)
|
||||||
|
memory.set("access", self.access)
|
||||||
|
|
||||||
|
source = etree.Element("source")
|
||||||
|
source.append(self._text_node("path", self.source_path))
|
||||||
|
source.append(self._text_node("alignsize", self.align_size))
|
||||||
|
if self.pmem is True:
|
||||||
|
source.append(etree.Element("pmem"))
|
||||||
|
|
||||||
|
target = etree.Element("target")
|
||||||
|
target.append(self._text_node("size", self.target_size))
|
||||||
|
target.append(self._text_node("node", self.target_node))
|
||||||
|
label = etree.Element("label")
|
||||||
|
label.append(self._text_node("size", self.label_size))
|
||||||
|
target.append(label)
|
||||||
|
|
||||||
|
memory.append(source)
|
||||||
|
memory.append(target)
|
||||||
|
|
||||||
|
return memory
|
||||||
|
|
||||||
|
def parse_dom(self, xmldoc):
|
||||||
|
super(LibvirtConfigGuestVPMEM, self).parse_dom(xmldoc)
|
||||||
|
self.model = xmldoc.get("model")
|
||||||
|
self.access = xmldoc.get("access")
|
||||||
|
|
||||||
|
for c in xmldoc.getchildren():
|
||||||
|
if c.tag == "source":
|
||||||
|
for sub in c.getchildren():
|
||||||
|
if sub.tag == "path":
|
||||||
|
self.source_path = sub.text
|
||||||
|
if sub.tag == "alignsize":
|
||||||
|
self.align_size = sub.text
|
||||||
|
elif c.tag == "target":
|
||||||
|
for sub in c.getchildren():
|
||||||
|
if sub.tag == "size":
|
||||||
|
self.target_size = sub.text
|
||||||
|
@ -1266,6 +1266,11 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
|
|
||||||
def cleanup(self, context, instance, network_info, block_device_info=None,
|
def cleanup(self, context, instance, network_info, block_device_info=None,
|
||||||
destroy_disks=True, migrate_data=None, destroy_vifs=True):
|
destroy_disks=True, migrate_data=None, destroy_vifs=True):
|
||||||
|
# zero the data on backend pmem device
|
||||||
|
vpmems = self._get_vpmems(instance)
|
||||||
|
if vpmems:
|
||||||
|
self._cleanup_vpmems(vpmems)
|
||||||
|
|
||||||
if destroy_vifs:
|
if destroy_vifs:
|
||||||
self._unplug_vifs(instance, network_info, True)
|
self._unplug_vifs(instance, network_info, True)
|
||||||
|
|
||||||
@ -1359,6 +1364,14 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
|
|
||||||
self._undefine_domain(instance)
|
self._undefine_domain(instance)
|
||||||
|
|
||||||
|
def _cleanup_vpmems(self, vpmems):
|
||||||
|
for vpmem in vpmems:
|
||||||
|
try:
|
||||||
|
nova.privsep.libvirt.cleanup_vpmem(vpmem.devpath)
|
||||||
|
except Exception as e:
|
||||||
|
raise exception.VPMEMCleanupFailed(dev=vpmem.devpath,
|
||||||
|
error=e)
|
||||||
|
|
||||||
def _detach_encrypted_volumes(self, instance, block_device_info):
|
def _detach_encrypted_volumes(self, instance, block_device_info):
|
||||||
"""Detaches encrypted volumes attached to instance."""
|
"""Detaches encrypted volumes attached to instance."""
|
||||||
disks = self._get_instance_disk_info(instance, block_device_info)
|
disks = self._get_instance_disk_info(instance, block_device_info)
|
||||||
@ -1452,6 +1465,11 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
inst_base = libvirt_utils.get_instance_path(instance)
|
inst_base = libvirt_utils.get_instance_path(instance)
|
||||||
target = inst_base + '_resize'
|
target = inst_base + '_resize'
|
||||||
|
|
||||||
|
# zero the data on backend old pmem device
|
||||||
|
vpmems = self._get_vpmems(instance, prefix='old')
|
||||||
|
if vpmems:
|
||||||
|
self._cleanup_vpmems(vpmems)
|
||||||
|
|
||||||
# Deletion can fail over NFS, so retry the deletion as required.
|
# Deletion can fail over NFS, so retry the deletion as required.
|
||||||
# Set maximum attempt as 5, most test can remove the directory
|
# Set maximum attempt as 5, most test can remove the directory
|
||||||
# for the second time.
|
# for the second time.
|
||||||
@ -5534,6 +5552,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
flavor = instance.flavor
|
flavor = instance.flavor
|
||||||
inst_path = libvirt_utils.get_instance_path(instance)
|
inst_path = libvirt_utils.get_instance_path(instance)
|
||||||
disk_mapping = disk_info['mapping']
|
disk_mapping = disk_info['mapping']
|
||||||
|
vpmems = self._get_ordered_vpmems(instance, flavor)
|
||||||
|
|
||||||
virt_type = CONF.libvirt.virt_type
|
virt_type = CONF.libvirt.virt_type
|
||||||
guest = vconfig.LibvirtConfigGuest()
|
guest = vconfig.LibvirtConfigGuest()
|
||||||
@ -5650,8 +5669,54 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
self._guest_configure_sev(guest, caps.host.cpu.arch,
|
self._guest_configure_sev(guest, caps.host.cpu.arch,
|
||||||
guest.os_mach_type)
|
guest.os_mach_type)
|
||||||
|
|
||||||
|
if vpmems:
|
||||||
|
self._guest_add_vpmems(guest, vpmems)
|
||||||
|
|
||||||
return guest
|
return guest
|
||||||
|
|
||||||
|
def _get_ordered_vpmems(self, instance, flavor):
|
||||||
|
ordered_vpmems = []
|
||||||
|
vpmems = self._get_vpmems(instance)
|
||||||
|
labels = hardware.get_vpmems(flavor)
|
||||||
|
for label in labels:
|
||||||
|
for vpmem in vpmems:
|
||||||
|
if vpmem.label == label:
|
||||||
|
ordered_vpmems.append(vpmem)
|
||||||
|
vpmems.remove(vpmem)
|
||||||
|
break
|
||||||
|
return ordered_vpmems
|
||||||
|
|
||||||
|
def _get_vpmems(self, instance, prefix=None):
|
||||||
|
vpmems = []
|
||||||
|
resources = instance.resources
|
||||||
|
if prefix == 'old' and instance.migration_context:
|
||||||
|
if 'old_resources' in instance.migration_context:
|
||||||
|
resources = instance.migration_context.old_resources
|
||||||
|
if not resources:
|
||||||
|
return vpmems
|
||||||
|
for resource in resources:
|
||||||
|
rc = resource.resource_class
|
||||||
|
if rc.startswith("CUSTOM_PMEM_NAMESPACE_"):
|
||||||
|
vpmem = self._vpmems_by_name[resource.identifier]
|
||||||
|
vpmems.append(vpmem)
|
||||||
|
return vpmems
|
||||||
|
|
||||||
|
def _guest_add_vpmems(self, guest, vpmems):
|
||||||
|
guest.max_memory_size = guest.memory
|
||||||
|
guest.max_memory_slots = 0
|
||||||
|
for vpmem in vpmems:
|
||||||
|
size_kb = vpmem.size / units.Ki
|
||||||
|
align_kb = vpmem.align / units.Ki
|
||||||
|
|
||||||
|
vpmem_config = vconfig.LibvirtConfigGuestVPMEM(
|
||||||
|
devpath=vpmem.devpath, size_kb=size_kb, align_kb=align_kb)
|
||||||
|
|
||||||
|
# max memory size needs contain vpmem size
|
||||||
|
guest.max_memory_size += size_kb
|
||||||
|
# one vpmem will occupy one memory slot
|
||||||
|
guest.max_memory_slots += 1
|
||||||
|
guest.add_device(vpmem_config)
|
||||||
|
|
||||||
def _sev_enabled(self, flavor, image_meta):
|
def _sev_enabled(self, flavor, image_meta):
|
||||||
"""To enable AMD SEV, the following should be true:
|
"""To enable AMD SEV, the following should be true:
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user