Hello Dan Kenigsberg, Francesco Romani, I'd like you to do a code review. Please visit
http://gerrit.ovirt.org/30076 to review the following change. Change subject: Add API.VM.setIoTune ...................................................................... Add API.VM.setIoTune This allows MoM (and the engine if necessary) to set the iotune limits for disk devices. Change-Id: I0bd48f13311ad2efc4241117a777ca3400c259ea Signed-off-by: Martin Sivak <msi...@redhat.com> Reviewed-on: http://gerrit.ovirt.org/28714 Reviewed-by: Francesco Romani <from...@redhat.com> Reviewed-by: Dan Kenigsberg <dan...@redhat.com> --- M lib/vdsm/define.py M tests/vmTests.py M vdsm/API.py M vdsm/rpc/vdsmapi-schema.json M vdsm/virt/vm.py 5 files changed, 173 insertions(+), 1 deletion(-) git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/76/30076/1 diff --git a/lib/vdsm/define.py b/lib/vdsm/define.py index 4b25f89..34f423a 100644 --- a/lib/vdsm/define.py +++ b/lib/vdsm/define.py @@ -144,6 +144,9 @@ 'updateVmPolicyErr': {'status': { 'code': 63, 'message': 'Failed to update VM SLA policy'}}, + 'updateIoTuneErr': {'status': { + 'code': 64, + 'message': 'Failed to update ioTune values'}}, 'recovery': {'status': { 'code': 99, 'message': 'Recovering from crash or Initializing'}}, diff --git a/tests/vmTests.py b/tests/vmTests.py index 5c3d445..11a256d 100644 --- a/tests/vmTests.py +++ b/tests/vmTests.py @@ -60,6 +60,7 @@ self.devXml = '' self._virtError = virtError self._metadata = "" + self._io_tune = {} def _failIfRequested(self): if self._virtError != libvirt.VIR_ERR_OK: @@ -94,6 +95,10 @@ def schedulerParameters(self): return {'vcpu_quota': vm._NO_CPU_QUOTA, 'vcpu_period': vm._NO_CPU_PERIOD} + + def setBlockIoTune(self, name, io_tune, flags): + self._io_tune[name] = io_tune + return 1 class TestVm(TestCaseBase): @@ -1145,6 +1150,76 @@ self.maxDiff = None self.assertEqual(expected_xml, self._xml_sanitizer(dom._metadata)) + def testSetIoTune(self): + + drives = [ + vm.Drive({ + "specParams": { + "ioTune": { + "total_bytes_sec": 9999, + "total_iops_sec": 9999} + }}, + log=self.log, + index=0, + device="hdd", + path="/dev/dummy", + type=vm.DISK_DEVICES, + iface="ide") + ] + + # Make the drive look like a VDSM volume + required = ('domainID', 'imageID', 'poolID', 'volumeID') + for p in required: + setattr(drives[0], p, "1") + drives[0]._blockDev = True + + tunables = [ + { + "name": drives[0].name, + "ioTune": { + "write_bytes_sec": 1, + "total_bytes_sec": 0, + "read_bytes_sec": 2 + } + } + ] + + expected_io_tune = { + drives[0].name: { + "write_bytes_sec": 1, + "total_bytes_sec": 0, + "read_bytes_sec": 2 + } + } + + expected_xml = """ + <disk device="hdd" snapshot="no" type="block"> + <source dev="/dev/dummy"/> + <target bus="ide" dev="hda"/> + <serial></serial> + <iotune> + <read_bytes_sec>2</read_bytes_sec> + <write_bytes_sec>1</write_bytes_sec> + <total_bytes_sec>0</total_bytes_sec> + </iotune> + </disk>""" + + with FakeVM() as machine: + dom = FakeDomain() + machine._dom = dom + for drive in drives: + machine._devices[drive.type].append(drive) + + machine.setIoTune(tunables) + + self.assertEqual(expected_io_tune, dom._io_tune) + + # Test that caches were properly updated + self.assertEqual(drives[0].specParams["ioTune"], + expected_io_tune[drives[0].name]) + self.assertEqual(self._xml_sanitizer(drives[0]._deviceXML), + self._xml_sanitizer(expected_xml)) + class FakeGuestAgent(object): def getGuestInfo(self): diff --git a/vdsm/API.py b/vdsm/API.py index ace68d1..509fa9d 100644 --- a/vdsm/API.py +++ b/vdsm/API.py @@ -708,6 +708,12 @@ return errCode['noVM'] return v.setCpuTuneQuota(quota) + def setIoTune(self, tunables): + v = self._cif.vmContainer.get(self._UUID) + if not v: + return errCode['noVM'] + return v.setIoTune(tunables) + def setCpuTunePeriod(self, period): v = self._cif.vmContainer.get(self._UUID) if not v: diff --git a/vdsm/rpc/vdsmapi-schema.json b/vdsm/rpc/vdsmapi-schema.json index 47124af..31b1873 100644 --- a/vdsm/rpc/vdsmapi-schema.json +++ b/vdsm/rpc/vdsmapi-schema.json @@ -7496,6 +7496,24 @@ 'returns': 'VmDefinition'} ## +# @VM.setIoTune: +# +# Sets the ioTune parameters for block devices +# +# @vmID: The UUID of the VM +# +# @tunables: list of VmDiskDeviceTuneParams objects +# describing the new settings +# Returns: +# Status code +# +# Since: 4.15.0 +## +{'command': {'class': 'VM', 'name': 'setIoTune'}, + 'data': {'vmID': 'UUID', 'tunables': ['VmDiskDeviceTuneParams']}, + 'returns': 'TaskStatus'} + +## # @VM.setCpuTuneQuota: # # Set the vCpu quota tune parameter to the VM diff --git a/vdsm/virt/vm.py b/vdsm/virt/vm.py index b3dd8f2..80ede4c 100644 --- a/vdsm/virt/vm.py +++ b/vdsm/virt/vm.py @@ -64,7 +64,8 @@ from . import sampling from . import vmexitreason from . import vmstatus -from .vmtune import updateIoTuneDom +from .vmtune import updateIoTuneDom, collectInnerElements +from .vmtune import ioTuneValuesToDom from .utils import XMLElement from vmpowerdown import VmShutdown, VmReboot @@ -3743,6 +3744,75 @@ metadata = xml.dom.minidom.parseString(metadata_xml) return metadata.getElementsByTagName("qos")[0] + def _findDeviceByNameOrPath(self, device_name, device_path): + for device in self._devices[DISK_DEVICES]: + if ((device.name == device_name + or ("path" in device and device["path"] == device_path)) + and isVdsmImage(device)): + return device + else: + return None + + def setIoTune(self, tunables): + for io_tune_change in tunables: + device_name = io_tune_change.get('name', None) + device_path = io_tune_change.get('path', None) + io_tune = io_tune_change['ioTune'] + + # Find the proper device object + found_device = self._findDeviceByNameOrPath(device_name, + device_path) + if found_device is None: + return self._reportError( + key='updateIoTuneErr', + msg="Device {} not found".format(device_name)) + + # Merge the update with current values + dom = found_device.getXML() + io_dom_list = dom.getElementsByTagName("iotune") + old_io_tune = {} + if io_dom_list: + collectInnerElements(io_dom_list[0], old_io_tune) + old_io_tune.update(io_tune) + io_tune = old_io_tune + + # Verify the ioTune params + try: + found_device._validateIoTuneParams(io_tune) + except ValueError: + return self._reportException(key='updateIoTuneErr', + msg='Invalid ioTune value') + + try: + self._dom.setBlockIoTune(found_device.name, io_tune, + libvirt.VIR_DOMAIN_AFFECT_LIVE) + except libvirt.libvirtError as e: + self.log.exception("setVmIoTune failed") + if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: + return errCode['noVM'] + else: + return self._reportError(key='updateIoTuneErr', + msg=e.message) + + # Update both the ioTune arguments and device xml DOM + # so we are still up-to-date + # TODO: improve once libvirt gets support for iotune events + # see https://bugzilla.redhat.com/show_bug.cgi?id=1114492 + if io_dom_list: + dom.removeChild(io_dom_list[0]) + io_dom = XMLElement("iotune") + ioTuneValuesToDom(io_tune, io_dom) + dom.appendChild(io_dom) + found_device.specParams['ioTune'] = io_tune + + # Make sure the cached XML representation is valid as well + xml = found_device.getXML().toprettyxml(encoding='utf-8') + self.log.debug("New device XML for %s: %s", + found_device.name, xml) + found_device._deviceXML = xml + + return {'status': doneCode} + def _createTransientDisk(self, diskParams): if diskParams.get('shared', None) != DRIVE_SHARED_TYPE.TRANSIENT: return -- To view, visit http://gerrit.ovirt.org/30076 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I0bd48f13311ad2efc4241117a777ca3400c259ea Gerrit-PatchSet: 1 Gerrit-Project: vdsm Gerrit-Branch: ovirt-3.5 Gerrit-Owner: Martin Sivák <msi...@redhat.com> Gerrit-Reviewer: Dan Kenigsberg <dan...@redhat.com> Gerrit-Reviewer: Francesco Romani <from...@redhat.com> _______________________________________________ vdsm-patches mailing list vdsm-patches@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches