diff --git a/ceilometer/compute/libvirt.py b/ceilometer/compute/libvirt.py index 70bff8fc..295f33e1 100644 --- a/ceilometer/compute/libvirt.py +++ b/ceilometer/compute/libvirt.py @@ -16,6 +16,8 @@ # License for the specific language governing permissions and limitations # under the License. +import copy + from lxml import etree from nova import flags @@ -61,6 +63,24 @@ def make_counter_from_instance(instance, name, type, volume): ) +def make_vnic_counter(instance, name, type, volume, vnic_data): + resource_metadata = copy.copy(vnic_data) + resource_metadata['instance_id'] = instance.id + + return counter.Counter( + source='?', + name=name, + type=type, + volume=volume, + user_id=instance.user_id, + project_id=instance.project_id, + resource_id=vnic_data['fref'], + timestamp=timeutils.isotime(), + duration=None, + resource_metadata=resource_metadata + ) + + class DiskIOPollster(plugin.ComputePollster): LOG = log.getLogger(__name__ + '.diskio') @@ -134,3 +154,54 @@ class CPUPollster(plugin.ComputePollster): self.LOG.error('could not get CPU time for %s: %s', instance.uuid, err) self.LOG.exception(err) + + +class NetPollster(plugin.ComputePollster): + + LOG = log.getLogger(__name__ + '.net') + + NET_USAGE_MESSAGE = ' '.join(["NETWORK USAGE:", "%s %s:", "read-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) + 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 + + def get_counters(self, manager, instance): + conn = get_libvirt_connection() + self.LOG.info('checking instance %s', instance.uuid) + try: + vnics = self._get_vnics(conn, instance) + 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, _, _, _, tx, _, _, _ = domain.interfaceStats(vnic['name']) + self.LOG.info(self.NET_USAGE_MESSAGE, instance.name, + vnic['name'], rx, tx) + yield make_vnic_counter(instance, + name='net_in_int', + type='cumulative', + volume=rx, + vnic_data=vnic + ) + yield make_vnic_counter(instance, + name='net_out_int', + type='cumulative', + volume=tx, + vnic_data=vnic + ) diff --git a/setup.py b/setup.py index 6643a127..b50aa235 100755 --- a/setup.py +++ b/setup.py @@ -48,6 +48,7 @@ setuptools.setup( [ceilometer.poll.compute] libvirt_diskio = ceilometer.compute.libvirt:DiskIOPollster libvirt_cpu = ceilometer.compute.libvirt:CPUPollster + libvirt_net = ceilometer.compute.libvirt:NetPollster [ceilometer.poll.central] network_floatingip = ceilometer.network.floatingip:FloatingIPPollster diff --git a/tests/compute/test_libvirt.py b/tests/compute/test_libvirt.py index eabba08f..e1b64d62 100644 --- a/tests/compute/test_libvirt.py +++ b/tests/compute/test_libvirt.py @@ -34,8 +34,12 @@ from nova import 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 + class TestDiskIOPollster(unittest.TestCase): @@ -72,3 +76,106 @@ class TestDiskIOPollster(unittest.TestCase): instance.id = 999 counters = list(self.pollster.get_counters(self.manager, instance)) assert not counters + + +class TestNetPollster(test_base.TestCase): + + def setUp(self): + self.manager = manager.AgentManager() + self.pollster = libvirt.NetPollster() + super(TestNetPollster, self).setUp() + self.instance = mock.MagicMock() + self.instance.name = 'instance-00000001' + self.instance.id = 1 + + def test_get_vnics(self): + dom_xml = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """ + + 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'} + ] + + ignore = mox.IgnoreArg() + conn = self.mox.CreateMockAnything() + conn._conn = self.mox.CreateMockAnything() + domain = self.mox.CreateMockAnything() + self.mox.StubOutWithMock(libvirt, 'get_libvirt_connection') + libvirt.get_libvirt_connection().AndReturn(conn) + 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])