Omer Frenkel has uploaded a new change for review. Change subject: Memory Hotplug for vm ......................................................................
Memory Hotplug for vm Introduces a new verb to hotplug memory to a running vm. the memory is plugged to a guest NUMA node. exposes it in the XML-RPC and JSON-RPC. http://www.ovirt.org/Features/Memory_Hotplug Change-Id: Ib2cdea311c3ff010b1d232abf6cc0a7d60937b1e Signed-off-by: Omer Frenkel <ofren...@redhat.com> --- M client/vdsClient.py M lib/vdsm/define.py M vdsm/API.py M vdsm/rpc/BindingXMLRPC.py M vdsm/rpc/Bridge.py M vdsm/rpc/vdsmapi-schema.json M vdsm/virt/vm.py M vdsm/virt/vmdevices/core.py M vdsm/virt/vmdevices/hwclass.py 9 files changed, 205 insertions(+), 2 deletions(-) git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/49/40549/1 diff --git a/client/vdsClient.py b/client/vdsClient.py index 375fa1a..687f33f 100755 --- a/client/vdsClient.py +++ b/client/vdsClient.py @@ -325,6 +325,13 @@ params = {'vmId': args[0], 'drive': drive} return self.ExecAndExit(self.s.hotunplugDisk(params)) + def hotplugMemory(self, args): + memory = self._parseDriveSpec(args[1]) + memory['type'] = 'memory' + memory['device'] = 'memory' + params = {'vmId': args[0], 'memory': memory} + return self.ExecAndExit(self.s.hotplugMemory(params)) + def setNumberOfCpus(self, args): return self.ExecAndExit(self.s.setNumberOfCpus(args[0], args[1])) @@ -2750,6 +2757,14 @@ '<vmId> <spUUID> <sdUUID> <imgUUID> <volUUID> <newSize>', 'Extends the virtual size of a disk' )), + 'hotplugMemory': ( + serv.hotplugMemory, ( + '<vmId> <memDeviceSpec>', + 'Hotplug memory to a running VM NUMA node', + 'memDeviceSpec parameters list: r=required, o=optional', + 'r size: memory size to plug in mb.', + 'r node: guest NUMA node id to plug into' + )), 'setNumberOfCpus': ( serv.setNumberOfCpus, ( '<vmId> <numberOfCpus>', diff --git a/lib/vdsm/define.py b/lib/vdsm/define.py index f725748..a1b32d2 100644 --- a/lib/vdsm/define.py +++ b/lib/vdsm/define.py @@ -150,6 +150,9 @@ 'V2VConnection': {'status': { 'code': 65, 'message': 'error connecting to hypervisor'}}, + 'hotplugMem': {'status': { + 'code': 66, + 'message': 'Failed to hotplug memory'}}, 'recovery': {'status': { 'code': 99, 'message': 'Recovering from crash or Initializing'}}, diff --git a/vdsm/API.py b/vdsm/API.py index 3cdc465..d3efaf9 100644 --- a/vdsm/API.py +++ b/vdsm/API.py @@ -470,6 +470,23 @@ return curVm.hotunplugDisk(params) + def hotplugMemory(self, params): + try: + utils.validateMinimalKeySet(params, ('vmId', 'memory')) + except ValueError: + self.log.error('Missing one of required parameters: vmId, memory') + return {'status': {'code': errCode['MissParam']['status']['code'], + 'message': 'Missing one of required ' + 'parameters: vmId, memory'}} + + try: + curVm = self._cif.vmContainer[self._UUID] + except KeyError: + self.log.warning("vm %s doesn't exist", self._UUID) + return errCode['noVM'] + + return curVm.hotplugMemory(params) + def setNumberOfCpus(self, numberOfCpus): if self._UUID is None or numberOfCpus is None: diff --git a/vdsm/rpc/BindingXMLRPC.py b/vdsm/rpc/BindingXMLRPC.py index 9a1da99..5fa511d 100644 --- a/vdsm/rpc/BindingXMLRPC.py +++ b/vdsm/rpc/BindingXMLRPC.py @@ -434,6 +434,10 @@ vm = API.VM(vmId) return vm.updateDevice(params) + def vmHotplugMemory(self, params): + vm = API.VM(params['vmId']) + return vm.hotplugMemory(params) + def vmSetNumberOfCpus(self, vmId, numberOfCpus): vm = API.VM(vmId) return vm.setNumberOfCpus(numberOfCpus) @@ -1031,6 +1035,7 @@ (self.vmHotunplugNic, 'hotunplugNic'), (self.vmUpdateDevice, 'vmUpdateDevice'), (self.vmSetNumberOfCpus, 'setNumberOfCpus'), + (self.vmHotplugMemory, 'hotplugMemory'), (self.merge, 'merge'), (self.vmUpdateVmPolicy, 'updateVmPolicy'), (self.vmSetIoTune, 'setIoTune'), diff --git a/vdsm/rpc/Bridge.py b/vdsm/rpc/Bridge.py index cb149bc..d063e67 100644 --- a/vdsm/rpc/Bridge.py +++ b/vdsm/rpc/Bridge.py @@ -473,6 +473,7 @@ 'VM_pause': {'ret': VM_running_state_change_Ret}, 'VM_setCpuTunePeriod': {'ret': 'taskStatus'}, 'VM_setCpuTuneQuota': {'ret': 'taskStatus'}, + 'VM_hotplugMemory': {'ret': 'vmList'}, 'VM_setNumberOfCpus': {'ret': 'vmList'}, 'VM_setIoTune': {'ret': 'taskStatus'}, 'VM_updateDevice': {'ret': 'vmList'}, diff --git a/vdsm/rpc/vdsmapi-schema.json b/vdsm/rpc/vdsmapi-schema.json index fa8f336..03f3cb0 100644 --- a/vdsm/rpc/vdsmapi-schema.json +++ b/vdsm/rpc/vdsmapi-schema.json @@ -2280,11 +2280,15 @@ # @hostdev: A host device # (new in version 4.17.0) # +# @memory: A memory device +# (new in version 4.17.0) +# # Since: 4.10.0 ## {'enum': 'VmDeviceType', 'data': ['disk', 'interface', 'video', 'sound', 'controller', 'balloon', - 'channel', 'console', 'smartcard', 'rng', 'graphics', 'hostdev']} + 'channel', 'console', 'smartcard', 'rng', 'graphics', 'hostdev', + 'memory']} ## # @VmDiskDeviceType: @@ -8142,6 +8146,75 @@ 'legality': 'VolumeLegality'}} ## +# @VmMemoryDeviceType: +# +# An enumeration of VM memory device types. +# +# @memory: A memory device +# +# Since: 4.17.0 +## +{'enum': 'VmMemoryDeviceType', 'data': ['memory']} + +## +# @VmMemoryDevice: +# +# Properties of a VM memory device +# +# @deviceType: The device type (always @memory) +# +# @device: The type of the memory device +# +# @address: Device hardware address +# +# @alias: Alias used to identify this device in commands +# +# @deviceId: A unique ID for this device +# +# @size: The memory size to hotplug +# +# @node: The NUMA node to use for hotplug +# +# Since: 4.17.0 +## +{'type': 'VmMemoryDevice', + 'data': {'deviceType': 'VmDeviceType', 'device': 'VmMemoryDeviceType', + 'address': 'VmDeviceAddress', 'alias': 'str', 'deviceId': 'UUID', + 'size': 'uint', 'node': 'uint'}} + +## +# @HotplugMemoryParams: +# +# Parameters for @VM.hotplugMemory. +# +# @vmId: The UUID of the VM to modify +# +# @memory: The memory device specification +# +# Since: 4.17.0 +## +{'type': 'HotplugMemoryParams', + 'data': {'vmId': 'UUID', 'memory': 'VmMemoryDevice'}} + +## +# @VM.hotplugMemory: +# +# Hotplug memory to a running VM NUMA node +# +# @vmID: The UUID of the VM +# +# @params: The VM UUID and memory device information +# +# Returns: +# The VM definition, as updated +# +# Since: 4.17.0 +## +{'command': {'class': 'VM', 'name': 'hotplugMemory'}, + 'data': {'vmID': 'UUID', 'params': 'HotplugMemoryParams'}, + 'returns': 'VmDefinition'} + +## # @VM.setNumberOfCpus: # # Set the number CPUs for a VM diff --git a/vdsm/virt/vm.py b/vdsm/virt/vm.py index 6741f06..15b2bdc 100644 --- a/vdsm/virt/vm.py +++ b/vdsm/virt/vm.py @@ -256,7 +256,8 @@ (hwclass.RNG, vmdevices.core.Rng), (hwclass.SMARTCARD, vmdevices.core.Smartcard), (hwclass.TPM, vmdevices.core.Tpm), - (hwclass.HOSTDEV, vmdevices.hostdevice.HostDevice)) + (hwclass.HOSTDEV, vmdevices.hostdevice.HostDevice), + (hwclass.MEMORY, vmdevices.core.Memory)) def _emptyDevMap(self): return dict((dev, []) for dev, _ in self.DeviceMapping) @@ -1609,6 +1610,7 @@ self._getUnderlyingRngDeviceInfo() self._getUnderlyingConsoleDeviceInfo() self._getUnderlyingHostDeviceInfo() + self._getUnderlyingMemoryDeviceInfo() # Obtain info of all unknown devices. Must be last! self._getUnderlyingUnknownDeviceInfo() @@ -2140,6 +2142,36 @@ hooks.after_nic_hotunplug(nicXml, self.conf, params=nic.custom) + return {'status': doneCode, 'vmList': self.status()} + + def hotplugMemory(self, params): + + if self.isMigrating(): + return errCode['migInProgress'] + + memParams = params.get('memory', {}) + device = vmdevices.core.Memory(self.conf, self.log, **memParams) + + deviceXml = device.getXML().toprettyxml(encoding='utf-8') + deviceXml = hooks.before_memory_hotplug(deviceXml) + device._deviceXML = deviceXml + self.log.debug("Hotplug memory xml: %s", deviceXml) + + try: + self._dom.attachDevice(deviceXml) + except libvirt.libvirtError as e: + self.log.exception("hotplugMemory failed") + if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: + return errCode['noVM'] + return response.error('hotplugMem', e.message) + + self._devices[hwclass.MEMORY].append(device) + with self._confLock: + self.conf['devices'].append(device) + self.saveState() + + hooks.after_memory_hotplug(deviceXml) + return {'status': doneCode, 'vmList': self.status()} def setNumberOfCpus(self, numberOfCpus): @@ -4109,6 +4141,28 @@ nicDev['network'] = network self.conf['devices'].append(nicDev) + def _getUnderlyingMemoryDeviceInfo(self): + """ + Obtain memory device info from libvirt. + """ + for x in self._domain.get_device_elements('memory'): + alias = x.getElementsByTagName('alias')[0].getAttribute('name') + # Get device address + address = self._getUnderlyingDeviceAddress(x) + + for mem in self._devices[hwclass.MEMORY]: + if not hasattr(mem, 'address') or not hasattr(mem, 'alias'): + mem.alias = alias + mem.address = address + break + # Update vm's conf with address + for dev in self.conf['devices']: + if ((dev['type'] == hwclass.MEMORY) and + (not dev.get('address') or not dev.get('alias'))): + dev['address'] = address + dev['alias'] = alias + break + def _setWriteWatermarks(self): """ Define when to receive an event about high write to guest image diff --git a/vdsm/virt/vmdevices/core.py b/vdsm/virt/vmdevices/core.py index 1eb50d0..d184ace 100644 --- a/vdsm/virt/vmdevices/core.py +++ b/vdsm/virt/vmdevices/core.py @@ -232,3 +232,37 @@ m.setAttrs(model=self.specParams.get('model', 'i6300esb'), action=self.specParams.get('action', 'none')) return m + + +class Memory(Base): + __slots__ = ('address', 'size', 'node') + + def __init__(self, conf, log, **kwargs): + super(Memory, self).__init__(conf, log, **kwargs) + # we get size in mb and send in kb + self.size = int(kwargs.get('size')) * 1024 + self.node = kwargs.get('node') + + def getXML(self): + """ + <memory model='dimm'> + <target> + <size unit='KiB'>524287</size> + <node>1</node> + </target> + </memory> + """ + + mem = self.createXmlElem('memory', None) + mem.setAttrs(model='dimm') + target = self.createXmlElem('target', None) + mem.appendChild(target) + size = self.createXmlElem('size', None) + size.setAttrs(unit='KiB') + size.appendTextNode(str(self.size)) + target.appendChild(size) + node = self.createXmlElem('node', None) + node.appendTextNode(str(self.node)) + target.appendChild(node) + + return mem diff --git a/vdsm/virt/vmdevices/hwclass.py b/vdsm/virt/vmdevices/hwclass.py index 2a55ee8..d44f0bc 100644 --- a/vdsm/virt/vmdevices/hwclass.py +++ b/vdsm/virt/vmdevices/hwclass.py @@ -34,3 +34,4 @@ SMARTCARD = 'smartcard' TPM = 'tpm' HOSTDEV = 'hostdev' +MEMORY = 'memory' -- To view, visit https://gerrit.ovirt.org/40549 To unsubscribe, visit https://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ib2cdea311c3ff010b1d232abf6cc0a7d60937b1e Gerrit-PatchSet: 1 Gerrit-Project: vdsm Gerrit-Branch: master Gerrit-Owner: Omer Frenkel <ofren...@redhat.com> _______________________________________________ vdsm-patches mailing list vdsm-patches@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches