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

Reply via email to