Add enhanced KVM storage QoS quotas

This will add additonal KVM-based qoutas for Cinder front-end QoS

The new quotas are:

 - read_bytes_sec_max
 - write_bytes_sec_max
 - total_bytes_sec_max
 - read_iops_sec_max
 - write_iops_sec_max
 - total_iops_sec_max
 - size_iops_sec

Depends-On: https://review.openstack.org/574804
Change-Id: I5a8f34c7ed3dea940700dffd54bc7d44f7518ab4
Implements: blueprint enhanced-kvm-storage-qos
This commit is contained in:
Simon Dodsley 2018-04-03 08:46:51 -04:00 committed by Matt Riedemann
parent 0a642e2eee
commit 37aea88845
5 changed files with 116 additions and 29 deletions

View File

@ -1016,12 +1016,22 @@ class LibvirtConfigGuestDiskTest(LibvirtConfigBaseTest):
obj.source_path = "/tmp/hello"
obj.target_dev = "/dev/hda"
obj.target_bus = "ide"
# Note that read/write iops/bytes values cannot be used with
# total values. These are only here for illustrative purposes.
obj.disk_read_bytes_sec = 1024000
obj.disk_read_iops_sec = 1000
obj.disk_total_bytes_sec = 2048000
obj.disk_write_bytes_sec = 1024000
obj.disk_write_iops_sec = 1000
obj.disk_total_iops_sec = 2000
obj.disk_write_bytes_sec_max = 1536000
obj.disk_write_iops_sec_max = 1500
obj.disk_total_iops_sec_max = 3072000
obj.disk_read_bytes_sec_max = 1536000
obj.disk_read_iops_sec_max = 1500
obj.disk_total_iops_sec_max = 2000
obj.disk_total_bytes_sec_max = 3072000
obj.disk_size_iops_sec = 16
xml = obj.to_xml()
self.assertXmlEqual(xml, """
@ -1035,6 +1045,13 @@ class LibvirtConfigGuestDiskTest(LibvirtConfigBaseTest):
<write_iops_sec>1000</write_iops_sec>
<total_bytes_sec>2048000</total_bytes_sec>
<total_iops_sec>2000</total_iops_sec>
<read_bytes_sec_max>1536000</read_bytes_sec_max>
<write_bytes_sec_max>1536000</write_bytes_sec_max>
<total_bytes_sec_max>3072000</total_bytes_sec_max>
<read_iops_sec_max>1500</read_iops_sec_max>
<write_iops_sec_max>1500</write_iops_sec_max>
<total_iops_sec_max>2000</total_iops_sec_max>
<size_iops_sec>16</size_iops_sec>
</iotune>
</disk>""")

View File

@ -118,8 +118,18 @@ class LibvirtISCSIVolumeBaseTestCase(LibvirtVolumeBaseTestCase):
'target_lun': 1,
'device_path': dev_path,
'qos_specs': {
# Note that read/write iops/bytes values cannot
# be used with total values.
# These are only here for illustrative purposes.
'total_bytes_sec': '102400',
'read_iops_sec': '200',
'read_bytes_sec_max': '150000',
'read_iops_sec_max': '2000',
'write_bytes_sec_max': '250000',
'write_iops_sec_max': '3000',
'total_bytes_sec_max': '400000',
'total_iops_sec_max': '4000',
'size_iops_sec': '16',
}
}
}

View File

@ -707,6 +707,13 @@ class LibvirtConfigGuestDisk(LibvirtConfigGuestDevice):
self.disk_write_iops_sec = None
self.disk_total_bytes_sec = None
self.disk_total_iops_sec = None
self.disk_read_bytes_sec_max = None
self.disk_write_bytes_sec_max = None
self.disk_total_bytes_sec_max = None
self.disk_read_iops_sec_max = None
self.disk_write_iops_sec_max = None
self.disk_total_iops_sec_max = None
self.disk_size_iops_sec = None
self.logical_block_size = None
self.physical_block_size = None
self.readonly = False
@ -718,6 +725,64 @@ class LibvirtConfigGuestDisk(LibvirtConfigGuestDevice):
self.mirror = None
self.encryption = None
def _format_iotune(self, dev):
iotune = etree.Element("iotune")
if self.disk_read_bytes_sec is not None:
iotune.append(self._text_node("read_bytes_sec",
self.disk_read_bytes_sec))
if self.disk_read_iops_sec is not None:
iotune.append(self._text_node("read_iops_sec",
self.disk_read_iops_sec))
if self.disk_write_bytes_sec is not None:
iotune.append(self._text_node("write_bytes_sec",
self.disk_write_bytes_sec))
if self.disk_write_iops_sec is not None:
iotune.append(self._text_node("write_iops_sec",
self.disk_write_iops_sec))
if self.disk_total_bytes_sec is not None:
iotune.append(self._text_node("total_bytes_sec",
self.disk_total_bytes_sec))
if self.disk_total_iops_sec is not None:
iotune.append(self._text_node("total_iops_sec",
self.disk_total_iops_sec))
if self.disk_read_bytes_sec_max is not None:
iotune.append(self._text_node("read_bytes_sec_max",
self.disk_read_bytes_sec_max))
if self.disk_write_bytes_sec_max is not None:
iotune.append(self._text_node("write_bytes_sec_max",
self.disk_write_bytes_sec_max))
if self.disk_total_bytes_sec_max is not None:
iotune.append(self._text_node("total_bytes_sec_max",
self.disk_total_bytes_sec_max))
if self.disk_read_iops_sec_max is not None:
iotune.append(self._text_node("read_iops_sec_max",
self.disk_read_iops_sec_max))
if self.disk_write_iops_sec_max is not None:
iotune.append(self._text_node("write_iops_sec_max",
self.disk_write_iops_sec_max))
if self.disk_total_iops_sec_max is not None:
iotune.append(self._text_node("total_iops_sec_max",
self.disk_total_iops_sec_max))
if self.disk_size_iops_sec is not None:
iotune.append(self._text_node("size_iops_sec",
self.disk_size_iops_sec))
if len(iotune) > 0:
dev.append(iotune)
def format_dom(self):
dev = super(LibvirtConfigGuestDisk, self).format_dom()
@ -774,34 +839,7 @@ class LibvirtConfigGuestDisk(LibvirtConfigGuestDevice):
if self.serial is not None:
dev.append(self._text_node("serial", self.serial))
iotune = etree.Element("iotune")
if self.disk_read_bytes_sec is not None:
iotune.append(self._text_node("read_bytes_sec",
self.disk_read_bytes_sec))
if self.disk_read_iops_sec is not None:
iotune.append(self._text_node("read_iops_sec",
self.disk_read_iops_sec))
if self.disk_write_bytes_sec is not None:
iotune.append(self._text_node("write_bytes_sec",
self.disk_write_bytes_sec))
if self.disk_write_iops_sec is not None:
iotune.append(self._text_node("write_iops_sec",
self.disk_write_iops_sec))
if self.disk_total_bytes_sec is not None:
iotune.append(self._text_node("total_bytes_sec",
self.disk_total_bytes_sec))
if self.disk_total_iops_sec is not None:
iotune.append(self._text_node("total_iops_sec",
self.disk_total_iops_sec))
if len(iotune) > 0:
dev.append(iotune)
self._format_iotune(dev)
# Block size tuning
if (self.logical_block_size is not None or

View File

@ -63,7 +63,11 @@ class LibvirtBaseVolumeDriver(object):
if 'qos_specs' in data and data['qos_specs']:
tune_opts = ['total_bytes_sec', 'read_bytes_sec',
'write_bytes_sec', 'total_iops_sec',
'read_iops_sec', 'write_iops_sec']
'read_iops_sec', 'write_iops_sec',
'read_bytes_sec_max', 'read_iops_sec_max',
'write_bytes_sec_max', 'write_iops_sec_max',
'total_bytes_sec_max', 'total_iops_sec_max',
'size_iops_sec']
specs = data['qos_specs']
if isinstance(specs, dict):
for k, v in specs.items():

View File

@ -0,0 +1,18 @@
---
features:
- |
The libvirt driver now supports additional Cinder front-end
QoS specs, allowing the specification of additional IO
burst limits applied for each attached disk, individually.
- quota:read_bytes_sec_max
- quota:write_bytes_sec_max
- quota:total_bytes_sec_max
- quota:read_iops_sec_max
- quota:write_iops_sec_max
- quota:total_iops_sec_max
- quota:size_iops_sec
For more information, see the Cinder admin guide:
https://docs.openstack.org/cinder/latest/admin/blockstorage-basic-volume-qos.html