Virt inspector directly layered over hypervisor API
Addresses BP nova-independent-virt Add the concept of a pluggable virt inspector that allows polling of the hypervisor layer without relying on any nova internals. Note that the test-requires dependency on the nova master tarball remains in place for the moment, as it's required by the nova notifier tests. Also we must leave the dependency on the libvirt-python RPM or python-libvirt Debian package to the distros which end up packaging ceilometer. Change-Id: I20700320dd7e3196507173c780ab598b479e4021
This commit is contained in:
		| @@ -1,8 +1,10 @@ | |||||||
| # -*- encoding: utf-8 -*- | # -*- encoding: utf-8 -*- | ||||||
| # | # | ||||||
| # Copyright © 2012 eNovance <licensing@enovance.com> | # Copyright © 2012 eNovance <licensing@enovance.com> | ||||||
|  | # Copyright © 2012 Red Hat, Inc | ||||||
| # | # | ||||||
| # Author: Julien Danjou <julien@danjou.info> | # Author: Julien Danjou <julien@danjou.info> | ||||||
|  | # Author: Eoghan Glynn <eglynn@redhat.com> | ||||||
| # | # | ||||||
| # Licensed under the Apache License, Version 2.0 (the "License"); you may | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||||||
| # not use this file except in compliance with the License. You may obtain | # not use this file except in compliance with the License. You may obtain | ||||||
| @@ -19,58 +21,44 @@ | |||||||
| import copy | import copy | ||||||
| import datetime | import datetime | ||||||
| 
 | 
 | ||||||
| from lxml import etree | from stevedore import driver | ||||||
| 
 | 
 | ||||||
| try: |  | ||||||
|     from nova import config as nova_config |  | ||||||
| except ImportError: |  | ||||||
|     # NOTE(dhellmann): We want to try to maintain compatibility |  | ||||||
|     # with folsom for the time being, so set the name nova_config |  | ||||||
|     # to a sentinal we can use to trigger different behavior |  | ||||||
|     # when we try to set up the configuration object. |  | ||||||
|     from nova import flags |  | ||||||
|     nova_config = False |  | ||||||
|     # Import this to register compute_driver flag in Folsom |  | ||||||
|     import nova.compute.manager |  | ||||||
| from nova.virt import driver |  | ||||||
| from ceilometer import counter | from ceilometer import counter | ||||||
| from ceilometer.compute import plugin | from ceilometer.compute import plugin | ||||||
| from ceilometer.compute import instance as compute_instance | from ceilometer.compute import instance as compute_instance | ||||||
|  | from ceilometer.compute.virt import inspector as virt_inspector | ||||||
|  | from ceilometer.openstack.common import cfg | ||||||
| from ceilometer.openstack.common import importutils | from ceilometer.openstack.common import importutils | ||||||
| from ceilometer.openstack.common import log | from ceilometer.openstack.common import log | ||||||
| from ceilometer.openstack.common import timeutils | from ceilometer.openstack.common import timeutils | ||||||
| 
 | 
 | ||||||
|  | OPTS = [ | ||||||
|  |     cfg.StrOpt('hypervisor_inspector', | ||||||
|  |                help='Inspector to use for inspecting the hypervisor layer', | ||||||
|  |                default='libvirt') | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | CONF = cfg.CONF | ||||||
|  | CONF.register_opts(OPTS) | ||||||
|  | 
 | ||||||
|  | LOG = log.getLogger(__name__) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| def _instance_name(instance): | def _instance_name(instance): | ||||||
|     """Shortcut to get instance name""" |     """Shortcut to get instance name""" | ||||||
|     return getattr(instance, 'OS-EXT-SRV-ATTR:instance_name', None) |     return getattr(instance, 'OS-EXT-SRV-ATTR:instance_name', None) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_compute_driver(): | def get_hypervisor_inspector(): | ||||||
|     # FIXME(jd) This function is made to be destroyed by an abstraction |  | ||||||
|     # layer in Nova providing an hypervisor agnostic API. |  | ||||||
|     # XXX(jd) Folsom compat |  | ||||||
|     if not nova_config: |  | ||||||
|         flags.parse_args([]) |  | ||||||
|         return flags.FLAGS.compute_driver |  | ||||||
|     nova_config.parse_args([]) |  | ||||||
|     return nova_config.cfg.CONF.compute_driver or "" |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def get_libvirt_connection(): |  | ||||||
|     """Return an open connection for talking to libvirt.""" |  | ||||||
|     # The direct-import implementation only works with Folsom because |  | ||||||
|     # the configuration setting changed. |  | ||||||
|     try: |     try: | ||||||
|         try: |         namespace = 'ceilometer.compute.virt' | ||||||
|             return driver.load_compute_driver(None) |         mgr = driver.DriverManager(namespace, | ||||||
|         except AttributeError: |                                    CONF.hypervisor_inspector, | ||||||
|             return importutils.import_object_ns('nova.virt', |                                    invoke_on_load=True) | ||||||
|                                                 get_compute_driver()) |         return mgr.driver | ||||||
|     except ImportError: |     except ImportError as e: | ||||||
|         # Fall back to the way it was done in Essex. |         LOG.error("Unable to load the hypervisor inspector: %s" % (e)) | ||||||
|         import nova.virt.connection |         return virt_inspector.Inspector() | ||||||
|         return nova.virt.connection.get_connection(read_only=True) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def make_counter_from_instance(instance, name, type, volume): | def make_counter_from_instance(instance, name, type, volume): | ||||||
| @@ -86,18 +74,16 @@ def make_counter_from_instance(instance, name, type, volume): | |||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class LibVirtPollster(plugin.ComputePollster): | class HypervisorPollster(plugin.ComputePollster): | ||||||
| 
 | 
 | ||||||
|     def is_enabled(self): |     inspector = None | ||||||
|         # XXX(llu): Keeps Folsom compatibility | 
 | ||||||
|         try: |     def __init__(self): | ||||||
|             return driver.compute_driver_matches("LibvirtDriver") |         if not HypervisorPollster.inspector: | ||||||
|         except AttributeError: |             HypervisorPollster.inspector = get_hypervisor_inspector() | ||||||
|             # Use a fairly liberal substring check. |  | ||||||
|             return 'libvirt' in get_compute_driver().lower() |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class InstancePollster(LibVirtPollster): | class InstancePollster(HypervisorPollster): | ||||||
| 
 | 
 | ||||||
|     def get_counters(self, manager, instance): |     def get_counters(self, manager, instance): | ||||||
|         yield make_counter_from_instance(instance, |         yield make_counter_from_instance(instance, | ||||||
| @@ -113,7 +99,7 @@ class InstancePollster(LibVirtPollster): | |||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class DiskIOPollster(LibVirtPollster): | class DiskIOPollster(HypervisorPollster): | ||||||
| 
 | 
 | ||||||
|     LOG = log.getLogger(__name__ + '.diskio') |     LOG = log.getLogger(__name__ + '.diskio') | ||||||
| 
 | 
 | ||||||
| @@ -127,28 +113,21 @@ class DiskIOPollster(LibVirtPollster): | |||||||
|                                      ]) |                                      ]) | ||||||
| 
 | 
 | ||||||
|     def get_counters(self, manager, instance): |     def get_counters(self, manager, instance): | ||||||
|         conn = get_libvirt_connection() |  | ||||||
|         instance_name = _instance_name(instance) |         instance_name = _instance_name(instance) | ||||||
|         try: |         try: | ||||||
|             disks = conn.get_disks(instance_name) |  | ||||||
|         except Exception as err: |  | ||||||
|             self.LOG.warning('Ignoring instance %s: %s', |  | ||||||
|                              instance_name, err) |  | ||||||
|             self.LOG.exception(err) |  | ||||||
|         else: |  | ||||||
|             r_bytes = 0 |             r_bytes = 0 | ||||||
|             r_requests = 0 |             r_requests = 0 | ||||||
|             w_bytes = 0 |             w_bytes = 0 | ||||||
|             w_requests = 0 |             w_requests = 0 | ||||||
|             for disk in disks: |             for disk, info in self.inspector.inspect_disks(instance_name): | ||||||
|                 stats = conn.block_stats(instance_name, disk) |  | ||||||
|                 self.LOG.info(self.DISKIO_USAGE_MESSAGE, |                 self.LOG.info(self.DISKIO_USAGE_MESSAGE, | ||||||
|                               instance, disk, stats[0], stats[1], |                               instance, disk.device, info.read_requests, | ||||||
|                               stats[2], stats[3], stats[4]) |                               info.read_bytes, info.write_requests, | ||||||
|                 r_bytes += stats[0] |                               info.write_bytes, info.errors) | ||||||
|                 r_requests += stats[1] |                 r_bytes += info.read_bytes | ||||||
|                 w_bytes += stats[3] |                 r_requests += info.read_requests | ||||||
|                 w_requests += stats[2] |                 w_bytes += info.write_bytes | ||||||
|  |                 w_requests += info.write_requests | ||||||
|             yield make_counter_from_instance(instance, |             yield make_counter_from_instance(instance, | ||||||
|                                              name='disk.read.requests', |                                              name='disk.read.requests', | ||||||
|                                              type=counter.TYPE_CUMULATIVE, |                                              type=counter.TYPE_CUMULATIVE, | ||||||
| @@ -169,9 +148,13 @@ class DiskIOPollster(LibVirtPollster): | |||||||
|                                              type=counter.TYPE_CUMULATIVE, |                                              type=counter.TYPE_CUMULATIVE, | ||||||
|                                              volume=w_bytes, |                                              volume=w_bytes, | ||||||
|                                              ) |                                              ) | ||||||
|  |         except Exception as err: | ||||||
|  |             self.LOG.warning('Ignoring instance %s: %s', | ||||||
|  |                              instance_name, err) | ||||||
|  |             self.LOG.exception(err) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CPUPollster(LibVirtPollster): | class CPUPollster(HypervisorPollster): | ||||||
| 
 | 
 | ||||||
|     LOG = log.getLogger(__name__ + '.cpu') |     LOG = log.getLogger(__name__ + '.cpu') | ||||||
| 
 | 
 | ||||||
| @@ -179,7 +162,7 @@ class CPUPollster(LibVirtPollster): | |||||||
| 
 | 
 | ||||||
|     def get_cpu_util(self, instance, cpu_info): |     def get_cpu_util(self, instance, cpu_info): | ||||||
|         prev_times = self.utilization_map.get(instance.id) |         prev_times = self.utilization_map.get(instance.id) | ||||||
|         self.utilization_map[instance.id] = (cpu_info['cpu_time'], |         self.utilization_map[instance.id] = (cpu_info.time, | ||||||
|                                              datetime.datetime.now()) |                                              datetime.datetime.now()) | ||||||
|         cpu_util = 0.0 |         cpu_util = 0.0 | ||||||
|         if prev_times: |         if prev_times: | ||||||
| @@ -187,21 +170,20 @@ class CPUPollster(LibVirtPollster): | |||||||
|             prev_timestamp = prev_times[1] |             prev_timestamp = prev_times[1] | ||||||
|             delta = self.utilization_map[instance.id][1] - prev_timestamp |             delta = self.utilization_map[instance.id][1] - prev_timestamp | ||||||
|             elapsed = (delta.seconds * (10 ** 6) + delta.microseconds) * 1000 |             elapsed = (delta.seconds * (10 ** 6) + delta.microseconds) * 1000 | ||||||
|             cores_fraction = 1.0 / cpu_info['num_cpu'] |             cores_fraction = 1.0 / cpu_info.number | ||||||
|             # account for cpu_time being reset when the instance is restarted |             # account for cpu_time being reset when the instance is restarted | ||||||
|             time_used = (cpu_info['cpu_time'] - prev_cpu |             time_used = (cpu_info.time - prev_cpu | ||||||
|                          if prev_cpu <= cpu_info['cpu_time'] else |                          if prev_cpu <= cpu_info.time else cpu_info.time) | ||||||
|                          cpu_info['cpu_time']) |  | ||||||
|             cpu_util = 100 * cores_fraction * time_used / elapsed |             cpu_util = 100 * cores_fraction * time_used / elapsed | ||||||
|         return cpu_util |         return cpu_util | ||||||
| 
 | 
 | ||||||
|     def get_counters(self, manager, instance): |     def get_counters(self, manager, instance): | ||||||
|         conn = get_libvirt_connection() |  | ||||||
|         self.LOG.info('checking instance %s', instance.id) |         self.LOG.info('checking instance %s', instance.id) | ||||||
|  |         instance_name = _instance_name(instance) | ||||||
|         try: |         try: | ||||||
|             cpu_info = conn.get_info({'name': _instance_name(instance)}) |             cpu_info = self.inspector.inspect_cpus(instance_name) | ||||||
|             self.LOG.info("CPUTIME USAGE: %s %d", |             self.LOG.info("CPUTIME USAGE: %s %d", | ||||||
|                           instance.__dict__, cpu_info['cpu_time']) |                           instance.__dict__, cpu_info.time) | ||||||
|             cpu_util = self.get_cpu_util(instance, cpu_info) |             cpu_util = self.get_cpu_util(instance, cpu_info) | ||||||
|             self.LOG.info("CPU UTILIZATION %%: %s %0.2f", |             self.LOG.info("CPU UTILIZATION %%: %s %0.2f", | ||||||
|                           instance.__dict__, cpu_util) |                           instance.__dict__, cpu_util) | ||||||
| @@ -218,7 +200,7 @@ class CPUPollster(LibVirtPollster): | |||||||
|             yield make_counter_from_instance(instance, |             yield make_counter_from_instance(instance, | ||||||
|                                              name='cpu', |                                              name='cpu', | ||||||
|                                              type=counter.TYPE_CUMULATIVE, |                                              type=counter.TYPE_CUMULATIVE, | ||||||
|                                              volume=cpu_info['cpu_time'], |                                              volume=cpu_info.time, | ||||||
|                                              ) |                                              ) | ||||||
|         except Exception as err: |         except Exception as err: | ||||||
|             self.LOG.error('could not get CPU time for %s: %s', |             self.LOG.error('could not get CPU time for %s: %s', | ||||||
| @@ -226,31 +208,17 @@ class CPUPollster(LibVirtPollster): | |||||||
|             self.LOG.exception(err) |             self.LOG.exception(err) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class NetPollster(LibVirtPollster): | class NetPollster(HypervisorPollster): | ||||||
| 
 | 
 | ||||||
|     LOG = log.getLogger(__name__ + '.net') |     LOG = log.getLogger(__name__ + '.net') | ||||||
| 
 | 
 | ||||||
|     NET_USAGE_MESSAGE = ' '.join(["NETWORK USAGE:", "%s %s:", "read-bytes=%d", |     NET_USAGE_MESSAGE = ' '.join(["NETWORK USAGE:", "%s %s:", "read-bytes=%d", | ||||||
|                                   "write-bytes=%d"]) |                                   "write-bytes=%d"]) | ||||||
| 
 | 
 | ||||||
|     def _get_vnics(self, conn, instance): |  | ||||||
|         """Get disks of an instance, only used to bypass bug#998089.""" |  | ||||||
|         domain = conn._conn.lookupByName(_instance_name(instance)) |  | ||||||
|         tree = etree.fromstring(domain.XMLDesc(0)) |  | ||||||
|         vnics = [] |  | ||||||
|         for interface in tree.findall('devices/interface'): |  | ||||||
|             vnic = {} |  | ||||||
|             vnic['name'] = interface.find('target').get('dev') |  | ||||||
|             vnic['mac'] = interface.find('mac').get('address') |  | ||||||
|             vnic['fref'] = interface.find('filterref').get('filter') |  | ||||||
|             for param in interface.findall('filterref/parameter'): |  | ||||||
|                 vnic[param.get('name').lower()] = param.get('value') |  | ||||||
|             vnics.append(vnic) |  | ||||||
|         return vnics |  | ||||||
| 
 |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def make_vnic_counter(instance, name, type, volume, vnic_data): |     def make_vnic_counter(instance, name, type, volume, vnic_data): | ||||||
|         resource_metadata = copy.copy(vnic_data) |         metadata = copy.copy(vnic_data) | ||||||
|  |         resource_metadata = dict(zip(metadata._fields, metadata)) | ||||||
|         resource_metadata['instance_id'] = instance.id |         resource_metadata['instance_id'] = instance.id | ||||||
| 
 | 
 | ||||||
|         return counter.Counter( |         return counter.Counter( | ||||||
| @@ -259,50 +227,43 @@ class NetPollster(LibVirtPollster): | |||||||
|             volume=volume, |             volume=volume, | ||||||
|             user_id=instance.user_id, |             user_id=instance.user_id, | ||||||
|             project_id=instance.tenant_id, |             project_id=instance.tenant_id, | ||||||
|             resource_id=vnic_data['fref'], |             resource_id=vnic_data.fref, | ||||||
|             timestamp=timeutils.isotime(), |             timestamp=timeutils.isotime(), | ||||||
|             resource_metadata=resource_metadata |             resource_metadata=resource_metadata | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     def get_counters(self, manager, instance): |     def get_counters(self, manager, instance): | ||||||
|         conn = get_libvirt_connection() |  | ||||||
|         instance_name = _instance_name(instance) |         instance_name = _instance_name(instance) | ||||||
|         self.LOG.info('checking instance %s', instance.id) |         self.LOG.info('checking instance %s', instance.id) | ||||||
|         try: |         try: | ||||||
|             vnics = self._get_vnics(conn, instance) |             for vnic, info in self.inspector.inspect_vnics(instance_name): | ||||||
|         except Exception as err: |  | ||||||
|             self.LOG.warning('Ignoring instance %s: %s', |  | ||||||
|                              instance_name, err) |  | ||||||
|             self.LOG.exception(err) |  | ||||||
|         else: |  | ||||||
|             domain = conn._conn.lookupByName(instance_name) |  | ||||||
|             for vnic in vnics: |  | ||||||
|                 rx_bytes, rx_packets, _, _, \ |  | ||||||
|                     tx_bytes, tx_packets, _, _ = \ |  | ||||||
|                     domain.interfaceStats(vnic['name']) |  | ||||||
|                 self.LOG.info(self.NET_USAGE_MESSAGE, instance_name, |                 self.LOG.info(self.NET_USAGE_MESSAGE, instance_name, | ||||||
|                               vnic['name'], rx_bytes, tx_bytes) |                               vnic.name, info.rx_bytes, info.tx_bytes) | ||||||
|                 yield self.make_vnic_counter(instance, |                 yield self.make_vnic_counter(instance, | ||||||
|                                              name='network.incoming.bytes', |                                              name='network.incoming.bytes', | ||||||
|                                              type=counter.TYPE_CUMULATIVE, |                                              type=counter.TYPE_CUMULATIVE, | ||||||
|                                              volume=rx_bytes, |                                              volume=info.rx_bytes, | ||||||
|                                              vnic_data=vnic, |                                              vnic_data=vnic, | ||||||
|                                              ) |                                              ) | ||||||
|                 yield self.make_vnic_counter(instance, |                 yield self.make_vnic_counter(instance, | ||||||
|                                              name='network.outgoing.bytes', |                                              name='network.outgoing.bytes', | ||||||
|                                              type=counter.TYPE_CUMULATIVE, |                                              type=counter.TYPE_CUMULATIVE, | ||||||
|                                              volume=tx_bytes, |                                              volume=info.tx_bytes, | ||||||
|                                              vnic_data=vnic, |                                              vnic_data=vnic, | ||||||
|                                              ) |                                              ) | ||||||
|                 yield self.make_vnic_counter(instance, |                 yield self.make_vnic_counter(instance, | ||||||
|                                              name='network.incoming.packets', |                                              name='network.incoming.packets', | ||||||
|                                              type=counter.TYPE_CUMULATIVE, |                                              type=counter.TYPE_CUMULATIVE, | ||||||
|                                              volume=rx_packets, |                                              volume=info.rx_packets, | ||||||
|                                              vnic_data=vnic, |                                              vnic_data=vnic, | ||||||
|                                              ) |                                              ) | ||||||
|                 yield self.make_vnic_counter(instance, |                 yield self.make_vnic_counter(instance, | ||||||
|                                              name='network.outgoing.packets', |                                              name='network.outgoing.packets', | ||||||
|                                              type=counter.TYPE_CUMULATIVE, |                                              type=counter.TYPE_CUMULATIVE, | ||||||
|                                              volume=tx_packets, |                                              volume=info.tx_packets, | ||||||
|                                              vnic_data=vnic, |                                              vnic_data=vnic, | ||||||
|                                              ) |                                              ) | ||||||
|  |         except Exception as err: | ||||||
|  |             self.LOG.warning('Ignoring instance %s: %s', | ||||||
|  |                              instance_name, err) | ||||||
|  |             self.LOG.exception(err) | ||||||
							
								
								
									
										0
									
								
								ceilometer/compute/virt/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								ceilometer/compute/virt/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										130
									
								
								ceilometer/compute/virt/inspector.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								ceilometer/compute/virt/inspector.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | |||||||
|  | # -*- encoding: utf-8 -*- | ||||||
|  | # | ||||||
|  | # Copyright © 2012 Red Hat, Inc | ||||||
|  | # | ||||||
|  | # Author: Eoghan Glynn <eglynn@redhat.com> | ||||||
|  | # | ||||||
|  | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||||||
|  | # not use this file except in compliance with the License. You may obtain | ||||||
|  | # a copy of the License at | ||||||
|  | # | ||||||
|  | #      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | # | ||||||
|  | # Unless required by applicable law or agreed to in writing, software | ||||||
|  | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||||
|  | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||||
|  | # License for the specific language governing permissions and limitations | ||||||
|  | # under the License. | ||||||
|  | """Inspector abstraction for read-only access to hypervisors""" | ||||||
|  |  | ||||||
|  | import collections | ||||||
|  |  | ||||||
|  | # Named tuple representing instances. | ||||||
|  | # | ||||||
|  | # name: the name of the instance | ||||||
|  | # uuid: the UUID associated with the instance | ||||||
|  | # | ||||||
|  | Instance = collections.namedtuple('Instance', ['name', 'UUID']) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Named tuple representing CPU statistics. | ||||||
|  | # | ||||||
|  | # number: number of CPUs | ||||||
|  | # time: cumulative CPU time | ||||||
|  | # | ||||||
|  | CPUStats = collections.namedtuple('CPUStats', ['number', 'time']) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Named tuple representing vNICs. | ||||||
|  | # | ||||||
|  | # name: the name of the vNIC | ||||||
|  | # mac: the MAC address | ||||||
|  | # fref: the filter ref | ||||||
|  | # parameters: miscellaneous parameters | ||||||
|  | # | ||||||
|  | Interface = collections.namedtuple('Interface', ['name', 'mac', | ||||||
|  |                                                  'fref', 'parameters']) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Named tuple representing vNIC statistics. | ||||||
|  | # | ||||||
|  | # rx_bytes: number of received bytes | ||||||
|  | # rx_packets: number of received packets | ||||||
|  | # tx_bytes: number of transmitted bytes | ||||||
|  | # tx_packets: number of transmitted packets | ||||||
|  | # | ||||||
|  | InterfaceStats = collections.namedtuple('InterfaceStats', | ||||||
|  |                                         ['rx_bytes', 'rx_packets', | ||||||
|  |                                          'tx_bytes', 'tx_packets']) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Named tuple representing disks. | ||||||
|  | # | ||||||
|  | # device: the device name for the disk | ||||||
|  | # | ||||||
|  | Disk = collections.namedtuple('Disk', ['device']) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Named tuple representing disk statistics. | ||||||
|  | # | ||||||
|  | # read_bytes: number of bytes read | ||||||
|  | # read_requests: number of read operations | ||||||
|  | # write_bytes: number of bytes written | ||||||
|  | # write_requests: number of write operations | ||||||
|  | # errors: number of errors | ||||||
|  | # | ||||||
|  | DiskStats = collections.namedtuple('DiskStats', | ||||||
|  |                                    ['read_bytes', 'read_requests', | ||||||
|  |                                     'write_bytes', 'write_requests', | ||||||
|  |                                     'errors']) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Exception types | ||||||
|  | # | ||||||
|  | class InspectorException(Exception): | ||||||
|  |     def __init__(self, message=None): | ||||||
|  |         super(InspectorException, self).__init__(message) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class InstanceNotFoundException(InspectorException): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Main virt inspector abstraction layering over the hypervisor API. | ||||||
|  | # | ||||||
|  | class Inspector(object): | ||||||
|  |  | ||||||
|  |     def inspect_instances(self): | ||||||
|  |         """ | ||||||
|  |         List the instances on the current host. | ||||||
|  |         """ | ||||||
|  |         raise NotImplementedError() | ||||||
|  |  | ||||||
|  |     def inspect_cpus(self, instance_name): | ||||||
|  |         """ | ||||||
|  |         Inspect the CPU statistics for an instance. | ||||||
|  |  | ||||||
|  |         :param instance_name: the name of the target instance | ||||||
|  |         :return: the number of CPUs and cumulative CPU time | ||||||
|  |         """ | ||||||
|  |         raise NotImplementedError() | ||||||
|  |  | ||||||
|  |     def inspect_vnics(self, instance_name): | ||||||
|  |         """ | ||||||
|  |         Inspect the vNIC statistics for an instance. | ||||||
|  |  | ||||||
|  |         :param instance_name: the name of the target instance | ||||||
|  |         :return: for each vNIC, the number of bytes & packets | ||||||
|  |                  received and transmitted | ||||||
|  |         """ | ||||||
|  |         raise NotImplementedError() | ||||||
|  |  | ||||||
|  |     def inspect_disks(self, instance_name): | ||||||
|  |         """ | ||||||
|  |         Inspect the disk statistics for an instance. | ||||||
|  |  | ||||||
|  |         :param instance_name: the name of the target instance | ||||||
|  |         :return: for each disk, the number of bytes & operations | ||||||
|  |                  read and written, and the error count | ||||||
|  |         """ | ||||||
|  |         raise NotImplementedError() | ||||||
							
								
								
									
										0
									
								
								ceilometer/compute/virt/libvirt/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								ceilometer/compute/virt/libvirt/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										139
									
								
								ceilometer/compute/virt/libvirt/inspector.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								ceilometer/compute/virt/libvirt/inspector.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | |||||||
|  | # -*- encoding: utf-8 -*- | ||||||
|  | # | ||||||
|  | # Copyright © 2012 Red Hat, Inc | ||||||
|  | # | ||||||
|  | # Author: Eoghan Glynn <eglynn@redhat.com> | ||||||
|  | # | ||||||
|  | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||||||
|  | # not use this file except in compliance with the License. You may obtain | ||||||
|  | # a copy of the License at | ||||||
|  | # | ||||||
|  | #      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | # | ||||||
|  | # Unless required by applicable law or agreed to in writing, software | ||||||
|  | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||||
|  | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||||
|  | # License for the specific language governing permissions and limitations | ||||||
|  | # under the License. | ||||||
|  | """Implementation of Inspector abstraction for libvirt""" | ||||||
|  |  | ||||||
|  | from lxml import etree | ||||||
|  |  | ||||||
|  | from ceilometer.compute.virt import inspector as virt_inspector | ||||||
|  | from ceilometer.openstack.common import cfg | ||||||
|  | from ceilometer.openstack.common import log as logging | ||||||
|  |  | ||||||
|  | libvirt = None | ||||||
|  |  | ||||||
|  | LOG = logging.getLogger(__name__) | ||||||
|  |  | ||||||
|  | libvirt_opts = [ | ||||||
|  |     cfg.StrOpt('libvirt_type', | ||||||
|  |                default='kvm', | ||||||
|  |                help='Libvirt domain type (valid options are: ' | ||||||
|  |                     'kvm, lxc, qemu, uml, xen)'), | ||||||
|  |     cfg.StrOpt('libvirt_uri', | ||||||
|  |                default='', | ||||||
|  |                help='Override the default libvirt URI ' | ||||||
|  |                     '(which is dependent on libvirt_type)'), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  | CONF = cfg.CONF | ||||||
|  | CONF.register_opts(libvirt_opts) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LibvirtInspector(virt_inspector.Inspector): | ||||||
|  |  | ||||||
|  |     per_type_uris = dict(uml='uml:///system', xen='xen:///', lxc='lxc:///') | ||||||
|  |  | ||||||
|  |     def __init__(self): | ||||||
|  |         self.uri = self._get_uri() | ||||||
|  |         self.connection = None | ||||||
|  |  | ||||||
|  |     def _get_uri(self): | ||||||
|  |         return CONF.libvirt_uri or self.per_type_uris.get(CONF.libvirt_type, | ||||||
|  |                                                           'qemu:///system') | ||||||
|  |  | ||||||
|  |     def _get_connection(self): | ||||||
|  |         if not self.connection or not self._test_connection(): | ||||||
|  |             global libvirt | ||||||
|  |             if libvirt is None: | ||||||
|  |                 libvirt = __import__('libvirt') | ||||||
|  |  | ||||||
|  |             LOG.debug('Connecting to libvirt: %s', self.uri) | ||||||
|  |             self.connection = libvirt.openReadOnly(self.uri) | ||||||
|  |  | ||||||
|  |         return self.connection | ||||||
|  |  | ||||||
|  |     def _test_connection(self): | ||||||
|  |         try: | ||||||
|  |             self.connection.getCapabilities() | ||||||
|  |             return True | ||||||
|  |         except libvirt.libvirtError as e: | ||||||
|  |             if (e.get_error_code() == libvirt.VIR_ERR_SYSTEM_ERROR and | ||||||
|  |                 e.get_error_domain() in (libvirt.VIR_FROM_REMOTE, | ||||||
|  |                                          libvirt.VIR_FROM_RPC)): | ||||||
|  |                 LOG.debug('Connection to libvirt broke') | ||||||
|  |                 return False | ||||||
|  |             raise | ||||||
|  |  | ||||||
|  |     def _lookup_by_name(self, instance_name): | ||||||
|  |         try: | ||||||
|  |             return self._get_connection().lookupByName(instance_name) | ||||||
|  |         except Exception as ex: | ||||||
|  |             error_code = ex.get_error_code() if libvirt else 'unknown' | ||||||
|  |             msg = ("Error from libvirt while looking up %(instance_name)s: " | ||||||
|  |                    "[Error Code %(error_code)s] %(ex)s" % locals()) | ||||||
|  |             raise virt_inspector.InstanceNotFoundException(msg) | ||||||
|  |  | ||||||
|  |     def inspect_instances(self): | ||||||
|  |         if self._get_connection().numOfDomains() > 0: | ||||||
|  |             for domain_id in self._get_connection().listDomainsID(): | ||||||
|  |                 try: | ||||||
|  |                     # We skip domains with ID 0 (hypervisors). | ||||||
|  |                     if domain_id != 0: | ||||||
|  |                         domain = self._get_connection().lookupByID(domain_id) | ||||||
|  |                         yield virt_inspector.Instance(name=domain.name(), | ||||||
|  |                                                       uuid=domain.UUIDString()) | ||||||
|  |                 except libvirt.libvirtError: | ||||||
|  |                     # Instance was deleted while listing... ignore it | ||||||
|  |                     pass | ||||||
|  |  | ||||||
|  |     def inspect_cpus(self, instance_name): | ||||||
|  |         domain = self._lookup_by_name(instance_name) | ||||||
|  |         (_, _, _, num_cpu, cpu_time) = domain.info() | ||||||
|  |         return virt_inspector.CPUStats(number=num_cpu, time=cpu_time) | ||||||
|  |  | ||||||
|  |     def inspect_vnics(self, instance_name): | ||||||
|  |         domain = self._lookup_by_name(instance_name) | ||||||
|  |         tree = etree.fromstring(domain.XMLDesc(0)) | ||||||
|  |         for iface in tree.findall('devices/interface'): | ||||||
|  |             name = iface.find('target').get('dev') | ||||||
|  |             mac = iface.find('mac').get('address') | ||||||
|  |             fref = iface.find('filterref').get('filter') | ||||||
|  |             params = dict((p.get('name').lower(), p.get('value')) | ||||||
|  |                           for p in iface.findall('filterref/parameter')) | ||||||
|  |             interface = virt_inspector.Interface(name=name, mac=mac, | ||||||
|  |                                                  fref=fref, parameters=params) | ||||||
|  |             rx_bytes, rx_packets, _, _, \ | ||||||
|  |                 tx_bytes, tx_packets, _, _ = domain.interfaceStats(name) | ||||||
|  |             stats = virt_inspector.InterfaceStats(rx_bytes=rx_bytes, | ||||||
|  |                                                   rx_packets=rx_packets, | ||||||
|  |                                                   tx_bytes=tx_bytes, | ||||||
|  |                                                   tx_packets=tx_packets) | ||||||
|  |             yield (interface, stats) | ||||||
|  |  | ||||||
|  |     def inspect_disks(self, instance_name): | ||||||
|  |         domain = self._lookup_by_name(instance_name) | ||||||
|  |         tree = etree.fromstring(domain.XMLDesc(0)) | ||||||
|  |         for device in filter(bool, | ||||||
|  |                       [target.get("dev") | ||||||
|  |                        for target in tree.findall('devices/disk/target')]): | ||||||
|  |             disk = virt_inspector.Disk(device=device) | ||||||
|  |             block_stats = domain.blockStats(device) | ||||||
|  |             stats = virt_inspector.DiskStats(read_requests=block_stats[0], | ||||||
|  |                                              read_bytes=block_stats[1], | ||||||
|  |                                              write_requests=block_stats[2], | ||||||
|  |                                              write_bytes=block_stats[3], | ||||||
|  |                                              errors=block_stats[4]) | ||||||
|  |             yield (disk, stats) | ||||||
| @@ -51,8 +51,8 @@ information and send them to the collector. As stated above, an agent | |||||||
| will automatically activate all plugins of a given class. For example, | will automatically activate all plugins of a given class. For example, | ||||||
| the compute agent will load all plugins of class | the compute agent will load all plugins of class | ||||||
| ``ceilometer.poll.compute``.  This will load, among others, the | ``ceilometer.poll.compute``.  This will load, among others, the | ||||||
| :class:`ceilometer.compute.libvirt.CPUPollster`, which is defined in | :class:`ceilometer.compute.pollsters.CPUPollster`, which is defined in | ||||||
| the file ``ceilometer/compute/libvirt.py`` as well as the | the file ``ceilometer/compute/pollsters.py`` as well as the | ||||||
| :class:`ceilometer.compute.notifications.InstanceNotifications` plugin | :class:`ceilometer.compute.notifications.InstanceNotifications` plugin | ||||||
| which is defined in the file ``ceilometer/compute/notifications.py`` | which is defined in the file ``ceilometer/compute/notifications.py`` | ||||||
|  |  | ||||||
| @@ -73,7 +73,7 @@ sequence of ``Counter`` objects as defined in the | |||||||
|  |  | ||||||
| In the ``CPUPollster`` plugin, the ``get_counters`` method is implemented as a loop | In the ``CPUPollster`` plugin, the ``get_counters`` method is implemented as a loop | ||||||
| which, for each instances running on the local host, retrieves the cpu_time | which, for each instances running on the local host, retrieves the cpu_time | ||||||
| from libvirt and send back two ``Counter`` objects.  The first one, named | from the hypervisor and sends back two ``Counter`` objects.  The first one, named | ||||||
| "cpu", is of type "cumulative", meaning that between two polls, its value is | "cpu", is of type "cumulative", meaning that between two polls, its value is | ||||||
| not reset, or in other word that the cpu value is always provided as a duration | not reset, or in other word that the cpu value is always provided as a duration | ||||||
| that continuously increases since the creation of the instance. The second one, | that continuously increases since the creation of the instance. The second one, | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								setup.py
									
									
									
									
									
								
							| @@ -111,10 +111,10 @@ setuptools.setup( | |||||||
|     floatingip = ceilometer.network.notifications:FloatingIP |     floatingip = ceilometer.network.notifications:FloatingIP | ||||||
|  |  | ||||||
|     [ceilometer.poll.compute] |     [ceilometer.poll.compute] | ||||||
|     libvirt_diskio = ceilometer.compute.libvirt:DiskIOPollster |     diskio = ceilometer.compute.pollsters:DiskIOPollster | ||||||
|     libvirt_cpu = ceilometer.compute.libvirt:CPUPollster |     cpu = ceilometer.compute.pollsters:CPUPollster | ||||||
|     libvirt_net = ceilometer.compute.libvirt:NetPollster |     net = ceilometer.compute.pollsters:NetPollster | ||||||
|     libvirt_instance = ceilometer.compute.libvirt:InstancePollster |     instance = ceilometer.compute.pollsters:InstancePollster | ||||||
|  |  | ||||||
|     [ceilometer.poll.central] |     [ceilometer.poll.central] | ||||||
|     network_floatingip = ceilometer.network.floatingip:FloatingIPPollster |     network_floatingip = ceilometer.network.floatingip:FloatingIPPollster | ||||||
| @@ -129,5 +129,8 @@ setuptools.setup( | |||||||
|     postgresql = ceilometer.storage.impl_sqlalchemy:SQLAlchemyStorage |     postgresql = ceilometer.storage.impl_sqlalchemy:SQLAlchemyStorage | ||||||
|     sqlite = ceilometer.storage.impl_sqlalchemy:SQLAlchemyStorage |     sqlite = ceilometer.storage.impl_sqlalchemy:SQLAlchemyStorage | ||||||
|     test = ceilometer.storage.impl_test:TestDBStorage |     test = ceilometer.storage.impl_test:TestDBStorage | ||||||
|  |  | ||||||
|  |     [ceilometer.compute.virt] | ||||||
|  |     libvirt = ceilometer.compute.virt.libvirt.inspector:LibvirtInspector | ||||||
|     """), |     """), | ||||||
|     ) |     ) | ||||||
|   | |||||||
| @@ -1,249 +0,0 @@ | |||||||
| #!/usr/bin/env python |  | ||||||
| # -*- encoding: utf-8 -*- |  | ||||||
| # |  | ||||||
| # Copyright © 2012 eNovance <licensing@enovance.com> |  | ||||||
| # |  | ||||||
| # Author: Julien Danjou <julien@danjou.info> |  | ||||||
| # |  | ||||||
| # Licensed under the Apache License, Version 2.0 (the "License"); you may |  | ||||||
| # not use this file except in compliance with the License. You may obtain |  | ||||||
| # a copy of the License at |  | ||||||
| # |  | ||||||
| #      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
| # |  | ||||||
| # Unless required by applicable law or agreed to in writing, software |  | ||||||
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |  | ||||||
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |  | ||||||
| # License for the specific language governing permissions and limitations |  | ||||||
| # under the License. |  | ||||||
| """Tests for manager. |  | ||||||
| """ |  | ||||||
|  |  | ||||||
| try: |  | ||||||
|     import libvirt as ignored_libvirt |  | ||||||
| except ImportError: |  | ||||||
|     libvirt_missing = True |  | ||||||
| else: |  | ||||||
|     libvirt_missing = False |  | ||||||
|  |  | ||||||
| import mock |  | ||||||
| import time |  | ||||||
|  |  | ||||||
| try: |  | ||||||
|     from nova import config |  | ||||||
|     nova_CONF = config.cfg.CONF |  | ||||||
| except ImportError: |  | ||||||
|     # XXX Folsom compat |  | ||||||
|     from nova import flags |  | ||||||
|     nova_CONF = flags.FLAGS |  | ||||||
|  |  | ||||||
| from ceilometer.compute import libvirt |  | ||||||
| from ceilometer.compute import manager |  | ||||||
| from ceilometer.tests import base as test_base |  | ||||||
| from ceilometer.tests import skip |  | ||||||
|  |  | ||||||
| import mox |  | ||||||
| import re |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def fake_libvirt_conn(moxobj, count=1): |  | ||||||
|     conn = moxobj.CreateMockAnything() |  | ||||||
|     conn._conn = moxobj.CreateMockAnything() |  | ||||||
|     moxobj.StubOutWithMock(libvirt, 'get_libvirt_connection') |  | ||||||
|     for _ in xrange(count): |  | ||||||
|         libvirt.get_libvirt_connection().AndReturn(conn) |  | ||||||
|     return conn |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TestLibvirtBase(test_base.TestCase): |  | ||||||
|  |  | ||||||
|     def setUp(self): |  | ||||||
|         super(TestLibvirtBase, self).setUp() |  | ||||||
|         self.manager = manager.AgentManager() |  | ||||||
|         self.instance = mock.MagicMock() |  | ||||||
|         self.instance.name = 'instance-00000001' |  | ||||||
|         setattr(self.instance, 'OS-EXT-SRV-ATTR:instance_name', |  | ||||||
|                 self.instance.name) |  | ||||||
|         self.instance.id = 1 |  | ||||||
|         self.instance.flavor = {'name': 'm1.small', 'id': 2} |  | ||||||
|         nova_CONF.compute_driver = 'libvirt.LibvirtDriver' |  | ||||||
|         nova_CONF.connection_type = 'libvirt' |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TestInstancePollster(TestLibvirtBase): |  | ||||||
|  |  | ||||||
|     @skip.skip_if(libvirt_missing, 'Test requires libvirt') |  | ||||||
|     def setUp(self): |  | ||||||
|         super(TestInstancePollster, self).setUp() |  | ||||||
|         self.pollster = libvirt.InstancePollster() |  | ||||||
|  |  | ||||||
|     def test_get_counter(self): |  | ||||||
|         counters = list(self.pollster.get_counters(self.manager, |  | ||||||
|                                                    self.instance)) |  | ||||||
|         self.assertEquals(len(counters), 2) |  | ||||||
|         self.assertEqual(counters[0].name, 'instance') |  | ||||||
|         self.assertEqual(counters[1].name, 'instance:m1.small') |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TestDiskIOPollster(TestLibvirtBase): |  | ||||||
|  |  | ||||||
|     def setUp(self): |  | ||||||
|         super(TestDiskIOPollster, self).setUp() |  | ||||||
|         self.pollster = libvirt.DiskIOPollster() |  | ||||||
|  |  | ||||||
|     @skip.skip_if(libvirt_missing, 'Test requires libvirt') |  | ||||||
|     def test_fetch_diskio(self): |  | ||||||
|         nova_CONF.compute_driver = 'fake.FakeDriver' |  | ||||||
|         list(self.pollster.get_counters(self.manager, self.instance)) |  | ||||||
|         #assert counters |  | ||||||
|         # FIXME(dhellmann): The CI environment doesn't produce |  | ||||||
|         # a response when the fake driver asks for the disks, so |  | ||||||
|         # we do not get any counters in response. |  | ||||||
|  |  | ||||||
|     @skip.skip_if(libvirt_missing, 'Test requires libvirt') |  | ||||||
|     def test_fetch_diskio_not_libvirt(self): |  | ||||||
|         nova_CONF.compute_driver = 'fake.FakeDriver' |  | ||||||
|         nova_CONF.connection_type = 'fake' |  | ||||||
|         counters = list(self.pollster.get_counters(self.manager, |  | ||||||
|                                                    self.instance)) |  | ||||||
|         assert not counters |  | ||||||
|  |  | ||||||
|     @skip.skip_if(libvirt_missing, 'Test requires libvirt') |  | ||||||
|     def test_fetch_diskio_with_libvirt_non_existent_instance(self): |  | ||||||
|         nova_CONF.compute_driver = 'fake.FakeDriver' |  | ||||||
|         instance = mock.MagicMock() |  | ||||||
|         instance.name = 'instance-00000999' |  | ||||||
|         instance.id = 999 |  | ||||||
|         counters = list(self.pollster.get_counters(self.manager, instance)) |  | ||||||
|         assert not counters |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TestNetPollster(TestLibvirtBase): |  | ||||||
|  |  | ||||||
|     def setUp(self): |  | ||||||
|         super(TestNetPollster, self).setUp() |  | ||||||
|         self.pollster = libvirt.NetPollster() |  | ||||||
|  |  | ||||||
|     def test_get_vnics(self): |  | ||||||
|         dom_xml = """ |  | ||||||
|              <domain type='kvm'> |  | ||||||
|                  <devices> |  | ||||||
|                     <interface type='bridge'> |  | ||||||
|                        <mac address='fa:16:3e:71:ec:6d'/> |  | ||||||
|                        <source bridge='br100'/> |  | ||||||
|                        <target dev='vnet0'/> |  | ||||||
|                        <filterref filter= |  | ||||||
|                         'nova-instance-instance-00000001-fa163e71ec6d'> |  | ||||||
|                          <parameter name='DHCPSERVER' value='10.0.0.1'/> |  | ||||||
|                          <parameter name='IP' value='10.0.0.2'/> |  | ||||||
|                          <parameter name='PROJMASK' value='255.255.255.0'/> |  | ||||||
|                          <parameter name='PROJNET' value='10.0.0.0'/> |  | ||||||
|                        </filterref> |  | ||||||
|                        <alias name='net0'/> |  | ||||||
|                      </interface> |  | ||||||
|                      <interface type='bridge'> |  | ||||||
|                        <mac address='fa:16:3e:71:ec:6e'/> |  | ||||||
|                        <source bridge='br100'/> |  | ||||||
|                        <target dev='vnet1'/> |  | ||||||
|                        <filterref filter= |  | ||||||
|                         'nova-instance-instance-00000001-fa163e71ec6e'> |  | ||||||
|                          <parameter name='DHCPSERVER' value='192.168.0.1'/> |  | ||||||
|                          <parameter name='IP' value='192.168.0.2'/> |  | ||||||
|                          <parameter name='PROJMASK' value='255.255.255.0'/> |  | ||||||
|                          <parameter name='PROJNET' value='192.168.0.0'/> |  | ||||||
|                        </filterref> |  | ||||||
|                        <alias name='net1'/> |  | ||||||
|                      </interface> |  | ||||||
|                  </devices> |  | ||||||
|              </domain> |  | ||||||
|         """ |  | ||||||
|  |  | ||||||
|         ignore = mox.IgnoreArg() |  | ||||||
|         conn = self.mox.CreateMockAnything() |  | ||||||
|         domain = self.mox.CreateMockAnything() |  | ||||||
|         conn._conn = self.mox.CreateMockAnything() |  | ||||||
|         self.mox.StubOutWithMock(conn._conn, 'lookupByName') |  | ||||||
|         conn._conn.lookupByName(self.instance.name).AndReturn(domain) |  | ||||||
|         self.mox.StubOutWithMock(domain, 'XMLDesc') |  | ||||||
|         domain.XMLDesc(0).AndReturn(dom_xml) |  | ||||||
|         self.mox.ReplayAll() |  | ||||||
|         interfaces = self.pollster._get_vnics(conn, self.instance) |  | ||||||
|         self.assertTrue('vnet1' in [x['name'] for x in interfaces]) |  | ||||||
|         self.assertTrue('fa:16:3e:71:ec:6d', [x['mac'] for x in interfaces]) |  | ||||||
|         self.assertTrue([x['dhcpserver'] for x in interfaces]) |  | ||||||
|  |  | ||||||
|     def test_get_counters(self): |  | ||||||
|         interface_stats1 = (3876L, 15L, 0L, 0L, 15830L, 0L, 0L, 0L) |  | ||||||
|         interface_stats2 = (9999L, 99L, 0L, 0L, 88888L, 0L, 0L, 0L) |  | ||||||
|         vnics = [ |  | ||||||
|                  {'name': 'vnet0', |  | ||||||
|                   'ip': '10.0.0.2', |  | ||||||
|                   'projmask': '255.255.255.0', |  | ||||||
|                   'projnet': 'proj1', |  | ||||||
|                   'fref': 'nova-instance-instance-00000001-fa163e71ec6e', |  | ||||||
|                   'bridge': 'br100', |  | ||||||
|                   'dhcp_server': '10.0.0.1', |  | ||||||
|                   'alias': 'net0', |  | ||||||
|                   'mac': 'fa:16:3e:71:ec:6d'}, |  | ||||||
|                  {'name': 'vnet1', |  | ||||||
|                   'ip': '192.168.0.3', |  | ||||||
|                   'projmask': '255.255.255.0', |  | ||||||
|                   'projnet': 'proj2', |  | ||||||
|                   'fref': 'nova-instance-instance-00000001-fa163e71ec6f', |  | ||||||
|                   'bridge': 'br100', |  | ||||||
|                   'dhcp_server': '192.168.0.1', |  | ||||||
|                   'fref': '00:00:00:01:1e', |  | ||||||
|                   'alias': 'net1', |  | ||||||
|                   'mac': 'fa:16:3e:71:ec:6e'} |  | ||||||
|                 ] |  | ||||||
|  |  | ||||||
|         conn = fake_libvirt_conn(self.mox) |  | ||||||
|         ignore = mox.IgnoreArg() |  | ||||||
|         domain = self.mox.CreateMockAnything() |  | ||||||
|         self.mox.StubOutWithMock(self.pollster, '_get_vnics') |  | ||||||
|         self.pollster._get_vnics(ignore, ignore).AndReturn(vnics) |  | ||||||
|         self.mox.StubOutWithMock(conn._conn, 'lookupByName') |  | ||||||
|         conn._conn.lookupByName(self.instance.name).AndReturn(domain) |  | ||||||
|         self.mox.StubOutWithMock(domain, 'interfaceStats') |  | ||||||
|         domain.interfaceStats('vnet0').AndReturn(interface_stats1) |  | ||||||
|         domain.interfaceStats('vnet1').AndReturn(interface_stats2) |  | ||||||
|         self.mox.ReplayAll() |  | ||||||
|  |  | ||||||
|         results = list(self.pollster.get_counters(self.manager, self.instance)) |  | ||||||
|         self.assertTrue([countr.resource_metadata['ip'] for countr in results]) |  | ||||||
|         self.assertTrue([countr.resource_id for countr in results]) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TestCPUPollster(TestLibvirtBase): |  | ||||||
|  |  | ||||||
|     def setUp(self): |  | ||||||
|         super(TestCPUPollster, self).setUp() |  | ||||||
|         self.pollster = libvirt.CPUPollster() |  | ||||||
|  |  | ||||||
|     def test_get_counter(self): |  | ||||||
|         conn = fake_libvirt_conn(self.mox, 3) |  | ||||||
|         self.mox.StubOutWithMock(conn, 'get_info') |  | ||||||
|         conn.get_info({'name': self.instance.name}).AndReturn( |  | ||||||
|             {'cpu_time': 1 * (10 ** 6), 'num_cpu': 2}) |  | ||||||
|         conn.get_info({'name': self.instance.name}).AndReturn( |  | ||||||
|             {'cpu_time': 3 * (10 ** 6), 'num_cpu': 2}) |  | ||||||
|         # cpu_time resets on instance restart |  | ||||||
|         conn.get_info({'name': self.instance.name}).AndReturn( |  | ||||||
|             {'cpu_time': 2 * (10 ** 6), 'num_cpu': 2}) |  | ||||||
|         self.mox.ReplayAll() |  | ||||||
|  |  | ||||||
|         def _verify_cpu_metering(zero, expected_time): |  | ||||||
|             counters = list(self.pollster.get_counters(self.manager, |  | ||||||
|                                                        self.instance)) |  | ||||||
|             self.assertEquals(len(counters), 2) |  | ||||||
|             assert counters[0].name == 'cpu_util' |  | ||||||
|             assert (counters[0].volume == 0.0 if zero else |  | ||||||
|                     counters[0].volume > 0.0) |  | ||||||
|             assert counters[1].name == 'cpu' |  | ||||||
|             assert counters[1].volume == expected_time |  | ||||||
|             # ensure elapsed time between polling cycles is non-zero |  | ||||||
|             time.sleep(0.001) |  | ||||||
|  |  | ||||||
|         _verify_cpu_metering(True, 1 * (10 ** 6)) |  | ||||||
|         _verify_cpu_metering(False, 3 * (10 ** 6)) |  | ||||||
|         _verify_cpu_metering(False, 2 * (10 ** 6)) |  | ||||||
							
								
								
									
										179
									
								
								tests/compute/test_pollsters.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								tests/compute/test_pollsters.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | |||||||
|  | # -*- encoding: utf-8 -*- | ||||||
|  | # | ||||||
|  | # Copyright © 2012 eNovance <licensing@enovance.com> | ||||||
|  | # Copyright © 2012 Red Hat, Inc | ||||||
|  | # | ||||||
|  | # Author: Julien Danjou <julien@danjou.info> | ||||||
|  | # Author: Eoghan Glynn <eglynn@redhat.com> | ||||||
|  | # | ||||||
|  | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||||||
|  | # not use this file except in compliance with the License. You may obtain | ||||||
|  | # a copy of the License at | ||||||
|  | # | ||||||
|  | #      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | # | ||||||
|  | # Unless required by applicable law or agreed to in writing, software | ||||||
|  | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||||
|  | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||||
|  | # License for the specific language governing permissions and limitations | ||||||
|  | # under the License. | ||||||
|  | """Tests for the compute pollsters. | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | import mock | ||||||
|  | import time | ||||||
|  |  | ||||||
|  | from ceilometer.compute import pollsters | ||||||
|  | from ceilometer.compute import manager | ||||||
|  | from ceilometer.compute.virt import inspector as virt_inspector | ||||||
|  | from ceilometer.tests import base as test_base | ||||||
|  |  | ||||||
|  | import mox | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestPollsterBase(test_base.TestCase): | ||||||
|  |  | ||||||
|  |     def setUp(self): | ||||||
|  |         super(TestPollsterBase, self).setUp() | ||||||
|  |         self.manager = manager.AgentManager() | ||||||
|  |         self.instance = mock.MagicMock() | ||||||
|  |         self.instance.name = 'instance-00000001' | ||||||
|  |         setattr(self.instance, 'OS-EXT-SRV-ATTR:instance_name', | ||||||
|  |                 self.instance.name) | ||||||
|  |         self.instance.id = 1 | ||||||
|  |         self.instance.flavor = {'name': 'm1.small', 'id': 2} | ||||||
|  |         self.mox.StubOutWithMock(pollsters, 'get_hypervisor_inspector') | ||||||
|  |         self.inspector = self.mox.CreateMock(virt_inspector.Inspector) | ||||||
|  |         pollsters.get_hypervisor_inspector().AndReturn(self.inspector) | ||||||
|  |         pollsters.HypervisorPollster.inspector = None | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestInstancePollster(TestPollsterBase): | ||||||
|  |  | ||||||
|  |     def setUp(self): | ||||||
|  |         super(TestInstancePollster, self).setUp() | ||||||
|  |  | ||||||
|  |     def test_get_counters(self): | ||||||
|  |         self.mox.ReplayAll() | ||||||
|  |  | ||||||
|  |         pollster = pollsters.InstancePollster() | ||||||
|  |         counters = list(pollster.get_counters(self.manager, | ||||||
|  |                                               self.instance)) | ||||||
|  |         self.assertEquals(len(counters), 2) | ||||||
|  |         self.assertEqual(counters[0].name, 'instance') | ||||||
|  |         self.assertEqual(counters[1].name, 'instance:m1.small') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestDiskIOPollster(TestPollsterBase): | ||||||
|  |  | ||||||
|  |     def setUp(self): | ||||||
|  |         super(TestDiskIOPollster, self).setUp() | ||||||
|  |  | ||||||
|  |     def test_get_counters(self): | ||||||
|  |         disks = [ | ||||||
|  |                  (virt_inspector.Disk(device='vda'), | ||||||
|  |                   virt_inspector.DiskStats(read_bytes=1L, read_requests=2L, | ||||||
|  |                                            write_bytes=3L, write_requests=4L, | ||||||
|  |                                            errors=-1L)) | ||||||
|  |                 ] | ||||||
|  |         self.inspector.inspect_disks(self.instance.name).AndReturn(disks) | ||||||
|  |         self.mox.ReplayAll() | ||||||
|  |  | ||||||
|  |         pollster = pollsters.DiskIOPollster() | ||||||
|  |         counters = list(pollster.get_counters(self.manager, self.instance)) | ||||||
|  |         assert counters | ||||||
|  |  | ||||||
|  |         def _verify_disk_metering(name, expected_volume): | ||||||
|  |             match = [c for c in counters if c.name == name] | ||||||
|  |             self.assertEquals(len(match), 1, 'missing counter %s' % name) | ||||||
|  |             self.assertEquals(match[0].volume, expected_volume) | ||||||
|  |             self.assertEquals(match[0].type, 'cumulative') | ||||||
|  |  | ||||||
|  |         _verify_disk_metering('disk.read.requests', 2L) | ||||||
|  |         _verify_disk_metering('disk.read.bytes', 1L) | ||||||
|  |         _verify_disk_metering('disk.write.requests', 4L) | ||||||
|  |         _verify_disk_metering('disk.write.bytes', 3L) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestNetPollster(TestPollsterBase): | ||||||
|  |  | ||||||
|  |     def setUp(self): | ||||||
|  |         super(TestNetPollster, self).setUp() | ||||||
|  |  | ||||||
|  |     def test_get_counters(self): | ||||||
|  |         vnic0 = virt_inspector.Interface(name='vnet0', | ||||||
|  |                                          fref='fa163e71ec6e', | ||||||
|  |                                          mac='fa:16:3e:71:ec:6d', | ||||||
|  |                                          parameters=dict(ip='10.0.0.2', | ||||||
|  |                                                      projmask='255.255.255.0', | ||||||
|  |                                                      projnet='proj1', | ||||||
|  |                                                      dhcp_server='10.0.0.1')) | ||||||
|  |         stats0 = virt_inspector.InterfaceStats(rx_bytes=1L, rx_packets=2L, | ||||||
|  |                                                tx_bytes=3L, tx_packets=4L) | ||||||
|  |         vnic1 = virt_inspector.Interface(name='vnet1', | ||||||
|  |                                          fref='fa163e71ec6f', | ||||||
|  |                                          mac='fa:16:3e:71:ec:6e', | ||||||
|  |                                          parameters=dict(ip='192.168.0.3', | ||||||
|  |                                                      projmask='255.255.255.0', | ||||||
|  |                                                      projnet='proj2', | ||||||
|  |                                                      dhcp_server='10.0.0.2')) | ||||||
|  |         stats1 = virt_inspector.InterfaceStats(rx_bytes=5L, rx_packets=6L, | ||||||
|  |                                                tx_bytes=7L, tx_packets=8L) | ||||||
|  |         vnics = [(vnic0, stats0), (vnic1, stats1)] | ||||||
|  |  | ||||||
|  |         self.inspector.inspect_vnics(self.instance.name).AndReturn(vnics) | ||||||
|  |         self.mox.ReplayAll() | ||||||
|  |  | ||||||
|  |         pollster = pollsters.NetPollster() | ||||||
|  |         counters = list(pollster.get_counters(self.manager, self.instance)) | ||||||
|  |         assert counters | ||||||
|  |  | ||||||
|  |         def _verify_vnic_metering(name, ip, expected_volume): | ||||||
|  |             match = [c for c in counters if c.name == name and | ||||||
|  |                      c.resource_metadata['parameters']['ip'] == ip] | ||||||
|  |             self.assertEquals(len(match), 1, 'missing counter %s' % name) | ||||||
|  |             self.assertEquals(match[0].volume, expected_volume) | ||||||
|  |             self.assertEquals(match[0].type, 'cumulative') | ||||||
|  |  | ||||||
|  |         _verify_vnic_metering('network.incoming.bytes', '10.0.0.2', 1L) | ||||||
|  |         _verify_vnic_metering('network.incoming.bytes', '192.168.0.3', 5L) | ||||||
|  |         _verify_vnic_metering('network.outgoing.bytes', '10.0.0.2', 3L) | ||||||
|  |         _verify_vnic_metering('network.outgoing.bytes', '192.168.0.3', 7L) | ||||||
|  |         _verify_vnic_metering('network.incoming.packets', '10.0.0.2', 2L) | ||||||
|  |         _verify_vnic_metering('network.incoming.packets', '192.168.0.3', 6L) | ||||||
|  |         _verify_vnic_metering('network.outgoing.packets', '10.0.0.2', 4L) | ||||||
|  |         _verify_vnic_metering('network.outgoing.packets', '192.168.0.3', 8L) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestCPUPollster(TestPollsterBase): | ||||||
|  |  | ||||||
|  |     def setUp(self): | ||||||
|  |         super(TestCPUPollster, self).setUp() | ||||||
|  |  | ||||||
|  |     def test_get_counters(self): | ||||||
|  |         self.inspector.inspect_cpus(self.instance.name).AndReturn( | ||||||
|  |             virt_inspector.CPUStats(time=1 * (10 ** 6), number=2)) | ||||||
|  |         self.inspector.inspect_cpus(self.instance.name).AndReturn( | ||||||
|  |             virt_inspector.CPUStats(time=3 * (10 ** 6), number=2)) | ||||||
|  |         # cpu_time resets on instance restart | ||||||
|  |         self.inspector.inspect_cpus(self.instance.name).AndReturn( | ||||||
|  |             virt_inspector.CPUStats(time=2 * (10 ** 6), number=2)) | ||||||
|  |         self.mox.ReplayAll() | ||||||
|  |  | ||||||
|  |         pollster = pollsters.CPUPollster() | ||||||
|  |  | ||||||
|  |         def _verify_cpu_metering(zero, expected_time): | ||||||
|  |             counters = list(pollster.get_counters(self.manager, | ||||||
|  |                                                   self.instance)) | ||||||
|  |             self.assertEquals(len(counters), 2) | ||||||
|  |             assert counters[0].name == 'cpu_util' | ||||||
|  |             assert (counters[0].volume == 0.0 if zero else | ||||||
|  |                     counters[0].volume > 0.0) | ||||||
|  |             assert counters[1].name == 'cpu' | ||||||
|  |             assert counters[1].volume == expected_time | ||||||
|  |             # ensure elapsed time between polling cycles is non-zero | ||||||
|  |             time.sleep(0.001) | ||||||
|  |  | ||||||
|  |         _verify_cpu_metering(True, 1 * (10 ** 6)) | ||||||
|  |         _verify_cpu_metering(False, 3 * (10 ** 6)) | ||||||
|  |         _verify_cpu_metering(False, 2 * (10 ** 6)) | ||||||
							
								
								
									
										0
									
								
								tests/compute/virt/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/compute/virt/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								tests/compute/virt/libvirt/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/compute/virt/libvirt/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										151
									
								
								tests/compute/virt/libvirt/test_inspector.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								tests/compute/virt/libvirt/test_inspector.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,151 @@ | |||||||
|  | #!/usr/bin/env python | ||||||
|  | # -*- encoding: utf-8 -*- | ||||||
|  | # | ||||||
|  | # Copyright © 2012 Red Hat, Inc | ||||||
|  | # | ||||||
|  | # Author: Eoghan Glynn <eglynn@redhat.com> | ||||||
|  | # | ||||||
|  | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||||||
|  | # not use this file except in compliance with the License. You may obtain | ||||||
|  | # a copy of the License at | ||||||
|  | # | ||||||
|  | #      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | # | ||||||
|  | # Unless required by applicable law or agreed to in writing, software | ||||||
|  | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||||
|  | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||||
|  | # License for the specific language governing permissions and limitations | ||||||
|  | # under the License. | ||||||
|  | """Tests for libvirt inspector. | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | import time | ||||||
|  |  | ||||||
|  | from ceilometer.compute.virt.libvirt import inspector as libvirt_inspector | ||||||
|  | from ceilometer.tests import base as test_base | ||||||
|  |  | ||||||
|  | import mox | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestLibvirtInspection(test_base.TestCase): | ||||||
|  |  | ||||||
|  |     def setUp(self): | ||||||
|  |         super(TestLibvirtInspection, self).setUp() | ||||||
|  |         self.instance_name = 'instance-00000001' | ||||||
|  |         self.inspector = libvirt_inspector.LibvirtInspector() | ||||||
|  |         self.inspector.connection = self.mox.CreateMockAnything() | ||||||
|  |         self.inspector.connection.getCapabilities() | ||||||
|  |         self.domain = self.mox.CreateMockAnything() | ||||||
|  |         self.inspector.connection.lookupByName(self.instance_name).AndReturn( | ||||||
|  |                                                                  self.domain) | ||||||
|  |  | ||||||
|  |     def test_inspect_cpus(self): | ||||||
|  |         self.domain.info().AndReturn((0L, 0L, 0L, 2L, 999999L)) | ||||||
|  |         self.mox.ReplayAll() | ||||||
|  |  | ||||||
|  |         cpu_info = self.inspector.inspect_cpus(self.instance_name) | ||||||
|  |         self.assertEqual(cpu_info.number, 2L) | ||||||
|  |         self.assertEqual(cpu_info.time, 999999L) | ||||||
|  |  | ||||||
|  |     def test_inspect_vnics(self): | ||||||
|  |         dom_xml = """ | ||||||
|  |              <domain type='kvm'> | ||||||
|  |                  <devices> | ||||||
|  |                     <interface type='bridge'> | ||||||
|  |                        <mac address='fa:16:3e:71:ec:6d'/> | ||||||
|  |                        <source bridge='br100'/> | ||||||
|  |                        <target dev='vnet0'/> | ||||||
|  |                        <filterref filter= | ||||||
|  |                         'nova-instance-00000001-fa163e71ec6d'> | ||||||
|  |                          <parameter name='DHCPSERVER' value='10.0.0.1'/> | ||||||
|  |                          <parameter name='IP' value='10.0.0.2'/> | ||||||
|  |                          <parameter name='PROJMASK' value='255.255.255.0'/> | ||||||
|  |                          <parameter name='PROJNET' value='10.0.0.0'/> | ||||||
|  |                        </filterref> | ||||||
|  |                        <alias name='net0'/> | ||||||
|  |                      </interface> | ||||||
|  |                      <interface type='bridge'> | ||||||
|  |                        <mac address='fa:16:3e:71:ec:6e'/> | ||||||
|  |                        <source bridge='br100'/> | ||||||
|  |                        <target dev='vnet1'/> | ||||||
|  |                        <filterref filter= | ||||||
|  |                         'nova-instance-00000001-fa163e71ec6e'> | ||||||
|  |                          <parameter name='DHCPSERVER' value='192.168.0.1'/> | ||||||
|  |                          <parameter name='IP' value='192.168.0.2'/> | ||||||
|  |                          <parameter name='PROJMASK' value='255.255.255.0'/> | ||||||
|  |                          <parameter name='PROJNET' value='192.168.0.0'/> | ||||||
|  |                        </filterref> | ||||||
|  |                        <alias name='net1'/> | ||||||
|  |                      </interface> | ||||||
|  |                  </devices> | ||||||
|  |              </domain> | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         self.domain.XMLDesc(0).AndReturn(dom_xml) | ||||||
|  |         self.domain.interfaceStats('vnet0').AndReturn((1L, 2L, 0L, 0L, | ||||||
|  |                                                        3L, 4L, 0L, 0L)) | ||||||
|  |         self.domain.interfaceStats('vnet1').AndReturn((5L, 6L, 0L, 0L, | ||||||
|  |                                                        7L, 8L, 0L, 0L)) | ||||||
|  |         self.mox.ReplayAll() | ||||||
|  |  | ||||||
|  |         interfaces = list(self.inspector.inspect_vnics(self.instance_name)) | ||||||
|  |  | ||||||
|  |         self.assertEquals(len(interfaces), 2) | ||||||
|  |         vnic0, info0 = interfaces[0] | ||||||
|  |         self.assertEqual(vnic0.name, 'vnet0') | ||||||
|  |         self.assertEqual(vnic0.mac, 'fa:16:3e:71:ec:6d') | ||||||
|  |         self.assertEqual(vnic0.fref, | ||||||
|  |                          'nova-instance-00000001-fa163e71ec6d') | ||||||
|  |         self.assertEqual(vnic0.parameters.get('projmask'), '255.255.255.0') | ||||||
|  |         self.assertEqual(vnic0.parameters.get('ip'), '10.0.0.2') | ||||||
|  |         self.assertEqual(vnic0.parameters.get('projnet'), '10.0.0.0') | ||||||
|  |         self.assertEqual(vnic0.parameters.get('dhcpserver'), '10.0.0.1') | ||||||
|  |         self.assertEqual(info0.rx_bytes, 1L) | ||||||
|  |         self.assertEqual(info0.rx_packets, 2L) | ||||||
|  |         self.assertEqual(info0.tx_bytes, 3L) | ||||||
|  |         self.assertEqual(info0.tx_packets, 4L) | ||||||
|  |  | ||||||
|  |         vnic1, info1 = interfaces[1] | ||||||
|  |         self.assertEqual(vnic1.name, 'vnet1') | ||||||
|  |         self.assertEqual(vnic1.mac, 'fa:16:3e:71:ec:6e') | ||||||
|  |         self.assertEqual(vnic1.fref, | ||||||
|  |                          'nova-instance-00000001-fa163e71ec6e') | ||||||
|  |         self.assertEqual(vnic1.parameters.get('projmask'), '255.255.255.0') | ||||||
|  |         self.assertEqual(vnic1.parameters.get('ip'), '192.168.0.2') | ||||||
|  |         self.assertEqual(vnic1.parameters.get('projnet'), '192.168.0.0') | ||||||
|  |         self.assertEqual(vnic1.parameters.get('dhcpserver'), '192.168.0.1') | ||||||
|  |         self.assertEqual(info1.rx_bytes, 5L) | ||||||
|  |         self.assertEqual(info1.rx_packets, 6L) | ||||||
|  |         self.assertEqual(info1.tx_bytes, 7L) | ||||||
|  |         self.assertEqual(info1.tx_packets, 8L) | ||||||
|  |  | ||||||
|  |     def test_inspect_disks(self): | ||||||
|  |         dom_xml = """ | ||||||
|  |              <domain type='kvm'> | ||||||
|  |                  <devices> | ||||||
|  |                      <disk type='file' device='disk'> | ||||||
|  |                          <driver name='qemu' type='qcow2' cache='none'/> | ||||||
|  |                          <source file='/path/instance-00000001/disk'/> | ||||||
|  |                          <target dev='vda' bus='virtio'/> | ||||||
|  |                          <alias name='virtio-disk0'/> | ||||||
|  |                          <address type='pci' domain='0x0000' bus='0x00' | ||||||
|  |                                   slot='0x04' function='0x0'/> | ||||||
|  |                      </disk> | ||||||
|  |                  </devices> | ||||||
|  |              </domain> | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         self.domain.XMLDesc(0).AndReturn(dom_xml) | ||||||
|  |  | ||||||
|  |         self.domain.blockStats('vda').AndReturn((1L, 2L, 3L, 4L, -1)) | ||||||
|  |         self.mox.ReplayAll() | ||||||
|  |  | ||||||
|  |         disks = list(self.inspector.inspect_disks(self.instance_name)) | ||||||
|  |  | ||||||
|  |         self.assertEquals(len(disks), 1) | ||||||
|  |         disk0, info0 = disks[0] | ||||||
|  |         self.assertEqual(disk0.device, 'vda') | ||||||
|  |         self.assertEqual(info0.read_requests, 1L) | ||||||
|  |         self.assertEqual(info0.read_bytes, 2L) | ||||||
|  |         self.assertEqual(info0.write_requests, 3L) | ||||||
|  |         self.assertEqual(info0.write_bytes, 4L) | ||||||
| @@ -16,3 +16,4 @@ python-novaclient>=2.6.10 | |||||||
| python-keystoneclient>=0.2,<0.3 | python-keystoneclient>=0.2,<0.3 | ||||||
| python-swiftclient | python-swiftclient | ||||||
| pecan | pecan | ||||||
|  | lxml | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Eoghan Glynn
					Eoghan Glynn