From 8f37626f72ce9865bb848170f46c29123014ba55 Mon Sep 17 00:00:00 2001 From: unicell Date: Wed, 20 Feb 2013 23:09:19 +0800 Subject: Improve I/O performance for periodic tasks Unfortunately, periodic tasks to update local resource and driver status come with performance penalties, especially on LVM image backend type. This is because low level implementation relies 'vgs' command to query LVM info, which itself has a I/O performance price and extravagant use of local_gb_xxx query make things even worse. This commit centralize such query in one single call (volume_group_info) and reduce invocation times in periodic tasks accordingly. Partially resolves LP #1130697 Change-Id: I165f96dd3e4fdad770b8a9643f5a038d353312ff --- nova/tests/test_libvirt.py | 27 +++++++------ nova/virt/libvirt/driver.py | 96 ++++++++++++++++++++------------------------- nova/virt/libvirt/utils.py | 38 ++++++------------ 3 files changed, 70 insertions(+), 91 deletions(-) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 6882bb082..4a74615e7 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -2873,21 +2873,29 @@ class LibvirtConnTestCase(test.TestCase): # NOTE(vish): verifies destroy doesn't raise if the instance disappears conn._destroy(instance) - def test_available_least_handles_missing(self): + def test_disk_over_committed_size_total(self): # Ensure destroy calls managedSaveRemove for saved instance. conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) def list_instances(): - return ['fake'] + return ['fake1', 'fake2'] self.stubs.Set(conn, 'list_instances', list_instances) + fake_disks = {'fake1': [{'type': 'qcow2', 'path': '/somepath/disk1', + 'virt_disk_size': '10737418240', + 'backing_file': '/somepath/disk1', + 'disk_size':'83886080'}], + 'fake2': [{'type': 'raw', 'path': '/somepath/disk2', + 'virt_disk_size': '10737418240', + 'backing_file': '/somepath/disk2', + 'disk_size':'10737418240'}]} + def get_info(instance_name): - raise exception.InstanceNotFound(instance_id='fake') + return jsonutils.dumps(fake_disks.get(instance_name)) self.stubs.Set(conn, 'get_instance_disk_info', get_info) - result = conn.get_disk_available_least() - space = fake_libvirt_utils.get_fs_info(CONF.instances_path)['free'] - self.assertEqual(result, space / 1024 ** 3) + result = conn.get_disk_over_committed_size_total() + self.assertEqual(result, 10653532160) def test_cpu_info(self): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) @@ -3453,11 +3461,8 @@ class HostStateTestCase(test.TestCase): def get_cpu_info(self): return HostStateTestCase.cpu_info - def get_local_gb_total(self): - return 100 - - def get_local_gb_used(self): - return 20 + def get_local_gb_info(self): + return {'total': 100, 'used': 20, 'free': 80} def get_memory_mb_total(self): return 497 diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 6f1cfef6f..0ddf5cf04 100755 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -2420,23 +2420,25 @@ class LibvirtDriver(driver.ComputeDriver): return self._conn.getInfo()[1] @staticmethod - def get_local_gb_total(): - """Get the total hdd size(GB) of physical computer. - - :returns: - The total amount of HDD(GB). - Note that this value shows a partition where - NOVA-INST-DIR/instances mounts. + def get_local_gb_info(): + """Get local storage info of the compute node in GB. + :returns: A dict containing: + :total: How big the overall usable filesystem is (in gigabytes) + :free: How much space is free (in gigabytes) + :used: How much space is used (in gigabytes) """ if CONF.libvirt_images_type == 'lvm': - vg_total = libvirt_utils.volume_group_total_space( + info = libvirt_utils.get_volume_group_info( CONF.libvirt_images_volume_group) - return vg_total / (1024 ** 3) else: - stats = libvirt_utils.get_fs_info(CONF.instances_path) - return stats['total'] / (1024 ** 3) + info = libvirt_utils.get_fs_info(CONF.instances_path) + + for (k, v) in info.iteritems(): + info[k] = v / (1024 ** 3) + + return info def get_vcpu_used(self): """Get vcpu usage number of physical computer. @@ -2502,24 +2504,6 @@ class LibvirtDriver(driver.ComputeDriver): # Convert it to MB return self.get_memory_mb_total() - avail / 1024 - def get_local_gb_used(self): - """Get the free hdd size(GB) of physical computer. - - :returns: - The total usage of HDD(GB). - Note that this value shows a partition where - NOVA-INST-DIR/instances mounts. - - """ - - if CONF.libvirt_images_type == 'lvm': - vg_used = libvirt_utils.volume_group_used_space( - CONF.libvirt_images_volume_group) - return vg_used / (1024 ** 3) - else: - stats = libvirt_utils.get_fs_info(CONF.instances_path) - return stats['used'] / (1024 ** 3) - def get_hypervisor_type(self): """Get hypervisor type. @@ -2691,17 +2675,35 @@ class LibvirtDriver(driver.ComputeDriver): :param nodename: ignored in this driver :returns: dictionary containing resource info """ + + def _get_disk_available_least(): + """Return total real disk available least size. + + The size of available disk, when block_migration command given + disk_over_commit param is FALSE. + + The size that deducted real instance disk size from the total size + of the virtual disk of all instances. + + """ + disk_free_gb = disk_info_dict['free'] + disk_over_committed = self.get_disk_over_committed_size_total() + # Disk available least size + available_least = disk_free_gb * (1024 ** 3) - disk_over_committed + return (available_least / (1024 ** 3)) + + disk_info_dict = self.get_local_gb_info() dic = {'vcpus': self.get_vcpu_total(), 'memory_mb': self.get_memory_mb_total(), - 'local_gb': self.get_local_gb_total(), + 'local_gb': disk_info_dict['total'], 'vcpus_used': self.get_vcpu_used(), 'memory_mb_used': self.get_memory_mb_used(), - 'local_gb_used': self.get_local_gb_used(), + 'local_gb_used': disk_info_dict['used'], 'hypervisor_type': self.get_hypervisor_type(), 'hypervisor_version': self.get_hypervisor_version(), 'hypervisor_hostname': self.get_hypervisor_hostname(), 'cpu_info': self.get_cpu_info(), - 'disk_available_least': self.get_disk_available_least()} + 'disk_available_least': _get_disk_available_least()} return dic def check_can_live_migrate_destination(self, ctxt, instance_ref, @@ -3230,22 +3232,11 @@ class LibvirtDriver(driver.ComputeDriver): 'disk_size': dk_size}) return jsonutils.dumps(disk_info) - def get_disk_available_least(self): - """Return disk available least size. - - The size of available disk, when block_migration command given - disk_over_commit param is FALSE. - - The size that deducted real nstance disk size from the total size - of the virtual disk of all instances. - - """ - # available size of the disk - dk_sz_gb = self.get_local_gb_total() - self.get_local_gb_used() - + def get_disk_over_committed_size_total(self): + """Return total over committed disk size for all instances.""" # Disk size that all instance uses : virtual_size - disk_size instances_name = self.list_instances() - instances_sz = 0 + disk_over_committed_size = 0 for i_name in instances_name: try: disk_infos = jsonutils.loads( @@ -3253,7 +3244,7 @@ class LibvirtDriver(driver.ComputeDriver): for info in disk_infos: i_vt_sz = int(info['virt_disk_size']) i_dk_sz = int(info['disk_size']) - instances_sz += i_vt_sz - i_dk_sz + disk_over_committed_size += i_vt_sz - i_dk_sz except OSError as e: if e.errno == errno.ENOENT: LOG.error(_("Getting disk size of %(i_name)s: %(e)s") % @@ -3265,9 +3256,7 @@ class LibvirtDriver(driver.ComputeDriver): pass # NOTE(gtt116): give change to do other task. greenthread.sleep(0) - # Disk available least size - available_least_size = dk_sz_gb * (1024 ** 3) - instances_sz - return (available_least_size / 1024 / 1024 / 1024) + return disk_over_committed_size def unfilter_instance(self, instance_ref, network_info): """See comments of same method in firewall_driver.""" @@ -3567,9 +3556,10 @@ class HostState(object): data["vcpus"] = self.driver.get_vcpu_total() data["vcpus_used"] = self.driver.get_vcpu_used() data["cpu_info"] = jsonutils.loads(self.driver.get_cpu_info()) - data["disk_total"] = self.driver.get_local_gb_total() - data["disk_used"] = self.driver.get_local_gb_used() - data["disk_available"] = data["disk_total"] - data["disk_used"] + disk_info_dict = self.driver.get_local_gb_info() + data["disk_total"] = disk_info_dict['total'] + data["disk_used"] = disk_info_dict['used'] + data["disk_available"] = disk_info_dict['free'] data["host_memory_total"] = self.driver.get_memory_mb_total() data["host_memory_free"] = (data["host_memory_total"] - self.driver.get_memory_mb_used()) diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py index 20af11ddc..cf3fd9d26 100755 --- a/nova/virt/libvirt/utils.py +++ b/nova/virt/libvirt/utils.py @@ -205,7 +205,8 @@ def create_lvm_image(vg, lv, size, sparse=False): :size: size of image in bytes :sparse: create sparse logical volume """ - free_space = volume_group_free_space(vg) + vg_info = get_volume_group_info(vg) + free_space = vg_info['free'] def check_size(vg, lv, size): if size > free_space: @@ -232,33 +233,14 @@ def create_lvm_image(vg, lv, size, sparse=False): execute(*cmd, run_as_root=True, attempts=3) -def volume_group_free_space(vg): - """Return available space on volume group in bytes. - - :param vg: volume group name - """ - out, err = execute('vgs', '--noheadings', '--nosuffix', - '--units', 'b', '-o', 'vg_free', vg, - run_as_root=True) - return int(out.strip()) - - -def volume_group_total_space(vg): - """Return total space on volume group in bytes. - - :param vg: volume group name - """ - - out, err = execute('vgs', '--noheadings', '--nosuffix', - '--units', 'b', '-o', 'vg_size', vg, - run_as_root=True) - return int(out.strip()) - - -def volume_group_used_space(vg): - """Return available space on volume group in bytes. +def get_volume_group_info(vg): + """Return free/used/total space info for a volume group in bytes :param vg: volume group name + :returns: A dict containing: + :total: How big the filesystem is (in bytes) + :free: How much space is free (in bytes) + :used: How much space is used (in bytes) """ out, err = execute('vgs', '--noheadings', '--nosuffix', @@ -270,7 +252,9 @@ def volume_group_used_space(vg): if len(info) != 2: raise RuntimeError(_("vg %s must be LVM volume group") % vg) - return int(info[0]) - int(info[1]) + return {'total': int(info[0]), + 'free': int(info[1]), + 'used': int(info[0]) - int(info[1])} def list_logical_volumes(vg): -- cgit