summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/compute/manager.py56
-rw-r--r--nova/db/api.py9
-rw-r--r--nova/db/sqlalchemy/api.py16
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/121_add_indexes_to_bw_usage_cache.py19
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/134_add_counters_to_bw_usage_cache.py60
-rw-r--r--nova/db/sqlalchemy/models.py2
-rw-r--r--nova/tests/test_db_api.py15
-rw-r--r--nova/tests/test_xenapi.py88
-rw-r--r--nova/virt/driver.py4
-rw-r--r--nova/virt/fake.py8
-rw-r--r--nova/virt/xenapi/driver.py28
-rw-r--r--nova/virt/xenapi/vm_utils.py9
-rw-r--r--nova/virt/xenapi/vmops.py41
-rwxr-xr-xplugins/xenserver/xenapi/etc/xapi.d/plugins/bandwidth51
14 files changed, 330 insertions, 76 deletions
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 4d88e2772..0f6653c66 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -2525,9 +2525,8 @@ class ComputeManager(manager.SchedulerDependentManager):
time.time() - start_time))
@manager.periodic_task
- def _poll_bandwidth_usage(self, context, start_time=None, stop_time=None):
- if not start_time:
- start_time = utils.last_completed_audit_period()[1]
+ def _poll_bandwidth_usage(self, context):
+ prev_time, start_time = utils.last_completed_audit_period()
curr_time = time.time()
if (curr_time - self._last_bw_usage_poll >
@@ -2537,8 +2536,7 @@ class ComputeManager(manager.SchedulerDependentManager):
instances = self.db.instance_get_all_by_host(context, self.host)
try:
- bw_usage = self.driver.get_all_bw_usage(instances, start_time,
- stop_time)
+ bw_counters = self.driver.get_all_bw_counters(instances)
except NotImplementedError:
# NOTE(mdragon): Not all hypervisors have bandwidth polling
# implemented yet. If they don't it doesn't break anything,
@@ -2546,12 +2544,52 @@ class ComputeManager(manager.SchedulerDependentManager):
return
refreshed = timeutils.utcnow()
- for usage in bw_usage:
+ for bw_ctr in bw_counters:
+ # Allow switching of greenthreads between queries.
+ greenthread.sleep(0)
+ bw_in = 0
+ bw_out = 0
+ last_ctr_in = None
+ last_ctr_out = None
+ usage = self.db.bw_usage_get(context,
+ bw_ctr['uuid'],
+ start_time,
+ bw_ctr['mac_address'])
+ if usage:
+ bw_in = usage['bw_in']
+ bw_out = usage['bw_out']
+ last_ctr_in = usage['last_ctr_in']
+ last_ctr_out = usage['last_ctr_out']
+ else:
+ usage = self.db.bw_usage_get(context,
+ bw_ctr['uuid'],
+ prev_time,
+ bw_ctr['mac_address'])
+ last_ctr_in = usage['last_ctr_in']
+ last_ctr_out = usage['last_ctr_out']
+
+ if last_ctr_in is not None:
+ if bw_ctr['bw_in'] < last_ctr_in:
+ # counter rollover
+ bw_in += bw_ctr['bw_in']
+ else:
+ bw_in += (bw_ctr['bw_in'] - last_ctr_in)
+
+ if last_ctr_out is not None:
+ if bw_ctr['bw_out'] < last_ctr_out:
+ # counter rollover
+ bw_out += bw_ctr['bw_out']
+ else:
+ bw_out += (bw_ctr['bw_out'] - last_ctr_out)
+
self.db.bw_usage_update(context,
- usage['uuid'],
- usage['mac_address'],
+ bw_ctr['uuid'],
+ bw_ctr['mac_address'],
start_time,
- usage['bw_in'], usage['bw_out'],
+ bw_in,
+ bw_out,
+ bw_ctr['bw_in'],
+ bw_ctr['bw_out'],
last_refreshed=refreshed)
@manager.periodic_task
diff --git a/nova/db/api.py b/nova/db/api.py
index cb651793e..4d91c5db9 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -1543,18 +1543,23 @@ def agent_build_update(context, agent_build_id, values):
####################
+def bw_usage_get(context, uuid, start_period, mac):
+ """Return bw usage for instance and mac in a given audit period."""
+ return IMPL.bw_usage_get(context, uuid, start_period, mac)
+
+
def bw_usage_get_by_uuids(context, uuids, start_period):
"""Return bw usages for instance(s) in a given audit period."""
return IMPL.bw_usage_get_by_uuids(context, uuids, start_period)
def bw_usage_update(context, uuid, mac, start_period, bw_in, bw_out,
- last_refreshed=None):
+ last_ctr_in, last_ctr_out, last_refreshed=None):
"""Update cached bandwidth usage for an instance's network based on mac
address. Creates new record if needed.
"""
return IMPL.bw_usage_update(context, uuid, mac, start_period, bw_in,
- bw_out, last_refreshed=last_refreshed)
+ bw_out, last_ctr_in, last_ctr_out, last_refreshed=last_refreshed)
####################
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 3798cade8..10619c9ed 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -4303,6 +4303,15 @@ def agent_build_update(context, agent_build_id, values):
####################
@require_context
+def bw_usage_get(context, uuid, start_period, mac):
+ return model_query(context, models.BandwidthUsage, read_deleted="yes").\
+ filter_by(start_period=start_period).\
+ filter_by(uuid=uuid).\
+ filter_by(mac=mac).\
+ first()
+
+
+@require_context
def bw_usage_get_by_uuids(context, uuids, start_period):
return model_query(context, models.BandwidthUsage, read_deleted="yes").\
filter(models.BandwidthUsage.uuid.in_(uuids)).\
@@ -4312,7 +4321,8 @@ def bw_usage_get_by_uuids(context, uuids, start_period):
@require_context
def bw_usage_update(context, uuid, mac, start_period, bw_in, bw_out,
- last_refreshed=None, session=None):
+ last_ctr_in, last_ctr_out, last_refreshed=None,
+ session=None):
if not session:
session = get_session()
@@ -4324,6 +4334,8 @@ def bw_usage_update(context, uuid, mac, start_period, bw_in, bw_out,
# records. Fall back to creation when no rows are updated.
with session.begin():
values = {'last_refreshed': last_refreshed,
+ 'last_ctr_in': last_ctr_in,
+ 'last_ctr_out': last_ctr_out,
'bw_in': bw_in,
'bw_out': bw_out}
rows = model_query(context, models.BandwidthUsage,
@@ -4342,6 +4354,8 @@ def bw_usage_update(context, uuid, mac, start_period, bw_in, bw_out,
bwusage.last_refreshed = last_refreshed
bwusage.bw_in = bw_in
bwusage.bw_out = bw_out
+ bwusage.last_ctr_in = last_ctr_in
+ bwusage.last_ctr_out = last_ctr_out
bwusage.save(session=session)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/121_add_indexes_to_bw_usage_cache.py b/nova/db/sqlalchemy/migrate_repo/versions/121_add_indexes_to_bw_usage_cache.py
index fcbe49061..1345e5396 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/121_add_indexes_to_bw_usage_cache.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/121_add_indexes_to_bw_usage_cache.py
@@ -16,7 +16,7 @@
# under the License.
from sqlalchemy import Index, MetaData, Table
-from sqlalchemy.exc import IntegrityError
+from sqlalchemy.exc import IntegrityError, OperationalError
def upgrade(migrate_engine):
@@ -41,4 +41,19 @@ def downgrade(migrate_engine):
t = Table('bw_usage_cache', meta, autoload=True)
i = Index('bw_usage_cache_uuid_start_period_idx',
t.c.uuid, t.c.start_period)
- i.drop(migrate_engine)
+ if migrate_engine.url.get_dialect().name.startswith('sqlite'):
+ try:
+ i.drop(migrate_engine)
+ except OperationalError:
+ # Sqlite is very broken for any kind of table modification.
+ # adding columns creates a new table, then copies the data,
+ # and looses the indexes.
+ # Thus later migrations that add columns will cause the
+ # earlier migration's downgrade unittests to fail on
+ # dropping indexes.
+ # Honestly testing migrations on sqlite is not really a very
+ # valid test (because of above facts), but that is for
+ # another day. (mdragon)
+ pass
+ else:
+ i.drop(migrate_engine)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/134_add_counters_to_bw_usage_cache.py b/nova/db/sqlalchemy/migrate_repo/versions/134_add_counters_to_bw_usage_cache.py
new file mode 100644
index 000000000..985149e91
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/134_add_counters_to_bw_usage_cache.py
@@ -0,0 +1,60 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack LLC.
+#
+# 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.
+
+from sqlalchemy import Boolean, Column, DateTime, BigInteger
+from sqlalchemy import MetaData, Integer, String, Table
+
+from nova.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+def upgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ # add column:
+ bw_usage_cache = Table('bw_usage_cache', meta, autoload=True)
+ last_ctr_in = Column('last_ctr_in', BigInteger())
+ last_ctr_out = Column('last_ctr_out', BigInteger())
+
+ bw_usage_cache.create_column(last_ctr_in)
+ bw_usage_cache.create_column(last_ctr_out)
+
+
+def downgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ # drop column:
+ bw_usage_cache = Table('bw_usage_cache', meta,
+ Column('created_at', DateTime(timezone=False)),
+ Column('updated_at', DateTime(timezone=False)),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted', Boolean(create_constraint=True, name=None)),
+ Column('id', Integer(), primary_key=True, nullable=False),
+ Column('mac', String(255)),
+ Column('uuid', String(36)),
+ Column('start_period', DateTime(timezone=False), nullable=False),
+ Column('last_refreshed', DateTime(timezone=False)),
+ Column('bw_in', BigInteger()),
+ Column('bw_out', BigInteger()),
+ Column('last_ctr_in', BigInteger()),
+ Column('last_ctr_out', BigInteger()),
+ extend_existing=True)
+
+ bw_usage_cache.drop_column('last_ctr_in')
+ bw_usage_cache.drop_column('last_ctr_out')
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 26981114b..0e960940d 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -937,6 +937,8 @@ class BandwidthUsage(BASE, NovaBase):
last_refreshed = Column(DateTime)
bw_in = Column(BigInteger)
bw_out = Column(BigInteger)
+ last_ctr_in = Column(BigInteger)
+ last_ctr_out = Column(BigInteger)
class S3Image(BASE, NovaBase):
diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py
index e434faaad..eb0b8bed8 100644
--- a/nova/tests/test_db_api.py
+++ b/nova/tests/test_db_api.py
@@ -478,18 +478,24 @@ class DbApiTestCase(test.TestCase):
'start_period': start_period,
'bw_in': 100,
'bw_out': 200,
+ 'last_ctr_in': 12345,
+ 'last_ctr_out': 67890,
'last_refreshed': now},
{'uuid': 'fake_uuid2',
'mac': 'fake_mac2',
'start_period': start_period,
'bw_in': 200,
'bw_out': 300,
+ 'last_ctr_in': 22345,
+ 'last_ctr_out': 77890,
'last_refreshed': now},
{'uuid': 'fake_uuid3',
'mac': 'fake_mac3',
'start_period': start_period,
'bw_in': 400,
'bw_out': 500,
+ 'last_ctr_in': 32345,
+ 'last_ctr_out': 87890,
'last_refreshed': uuid3_refreshed}]
def _compare(bw_usage, expected):
@@ -504,18 +510,19 @@ class DbApiTestCase(test.TestCase):
# Add 3 entries
db.bw_usage_update(ctxt, 'fake_uuid1',
'fake_mac1', start_period,
- 100, 200)
+ 100, 200, 12345, 67890)
db.bw_usage_update(ctxt, 'fake_uuid2',
'fake_mac2', start_period,
- 100, 200)
+ 100, 200, 42, 42)
# Test explicit refreshed time
db.bw_usage_update(ctxt, 'fake_uuid3',
'fake_mac3', start_period,
- 400, 500, last_refreshed=uuid3_refreshed)
+ 400, 500, 32345, 87890,
+ last_refreshed=uuid3_refreshed)
# Update 2nd entry
db.bw_usage_update(ctxt, 'fake_uuid2',
'fake_mac2', start_period,
- 200, 300)
+ 200, 300, 22345, 77890)
bw_usages = db.bw_usage_get_by_uuids(ctxt,
['fake_uuid1', 'fake_uuid2', 'fake_uuid3'], start_period)
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index b543f3e0f..18f8f2ea7 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -1442,11 +1442,23 @@ class XenAPIGenerateLocal(stubs.XenAPITestBase):
self.assertCalled(instance)
-class XenAPIBWUsageTestCase(stubs.XenAPITestBase):
+class XenAPIBWCountersTestCase(stubs.XenAPITestBase):
+ FAKE_VMS = {'test1:ref': dict(name_label='test1',
+ other_config=dict(nova_uuid='hash'),
+ domid='12',
+ _vifmap={'0': "a:b:c:d...",
+ '1': "e:f:12:q..."}),
+ 'test2:ref': dict(name_label='test2',
+ other_config=dict(nova_uuid='hash'),
+ domid='42',
+ _vifmap={'0': "a:3:c:d...",
+ '1': "e:f:42:q..."}),
+ }
+
def setUp(self):
- super(XenAPIBWUsageTestCase, self).setUp()
- self.stubs.Set(vm_utils, 'compile_metrics',
- XenAPIBWUsageTestCase._fake_compile_metrics)
+ super(XenAPIBWCountersTestCase, self).setUp()
+ self.stubs.Set(vm_utils, 'list_vms',
+ XenAPIBWCountersTestCase._fake_list_vms)
self.flags(xenapi_connection_url='test_url',
xenapi_connection_password='test_pass',
firewall_driver='nova.virt.xenapi.firewall.'
@@ -1454,21 +1466,75 @@ class XenAPIBWUsageTestCase(stubs.XenAPITestBase):
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
self.conn = xenapi_conn.XenAPIDriver(False)
+ def _fake_get_vif_device_map(vm_rec):
+ return vm_rec['_vifmap']
+
+ self.stubs.Set(self.conn._vmops, "_get_vif_device_map",
+ _fake_get_vif_device_map)
+
@classmethod
- def _fake_compile_metrics(cls, start_time, stop_time=None):
- raise exception.CouldNotFetchMetrics()
+ def _fake_list_vms(cls, session):
+ return cls.FAKE_VMS.iteritems()
- def test_get_all_bw_usage_in_failure_case(self):
- """Test that get_all_bw_usage returns an empty list when metrics
- compilation failed. c.f. bug #910045.
+ @classmethod
+ def _fake_fetch_bandwidth_mt(cls, session):
+ return {}
+
+ @classmethod
+ def _fake_fetch_bandwidth(cls, session):
+ return {'42':
+ {'0': {'bw_in': 21024, 'bw_out': 22048},
+ '1': {'bw_in': 231337, 'bw_out': 221212121}},
+ '12':
+ {'0': {'bw_in': 1024, 'bw_out': 2048},
+ '1': {'bw_in': 31337, 'bw_out': 21212121}},
+ }
+
+ def test_get_all_bw_counters(self):
+ class testinstance(object):
+ def __init__(self, name, uuid):
+ self.name = name
+ self.uuid = uuid
+
+ self.stubs.Set(vm_utils, 'fetch_bandwidth',
+ XenAPIBWCountersTestCase._fake_fetch_bandwidth)
+ result = self.conn.get_all_bw_counters([testinstance(
+ name='test1',
+ uuid='1-2-3'),
+ testinstance(
+ name='test2',
+ uuid='4-5-6')])
+ self.assertEqual(len(result), 4)
+ self.assertIn(dict(uuid='1-2-3',
+ mac_address="a:b:c:d...",
+ bw_in=1024,
+ bw_out=2048), result)
+ self.assertIn(dict(uuid='1-2-3',
+ mac_address="e:f:12:q...",
+ bw_in=31337,
+ bw_out=21212121), result)
+
+ self.assertIn(dict(uuid='4-5-6',
+ mac_address="a:3:c:d...",
+ bw_in=21024,
+ bw_out=22048), result)
+ self.assertIn(dict(uuid='4-5-6',
+ mac_address="e:f:42:q...",
+ bw_in=231337,
+ bw_out=221212121), result)
+
+ def test_get_all_bw_counters_in_failure_case(self):
+ """Test that get_all_bw_conters returns an empty list when
+ no data returned from Xenserver. c.f. bug #910045.
"""
class testinstance(object):
def __init__(self):
self.name = "instance-0001"
self.uuid = "1-2-3-4-5"
- result = self.conn.get_all_bw_usage([testinstance()],
- timeutils.utcnow())
+ self.stubs.Set(vm_utils, 'fetch_bandwidth',
+ XenAPIBWCountersTestCase._fake_fetch_bandwidth_mt)
+ result = self.conn.get_all_bw_counters([testinstance()])
self.assertEqual(result, [])
diff --git a/nova/virt/driver.py b/nova/virt/driver.py
index 41df132fc..d741524b0 100644
--- a/nova/virt/driver.py
+++ b/nova/virt/driver.py
@@ -221,8 +221,8 @@ class ComputeDriver(object):
# TODO(Vek): Need to pass context in for access to auth_token
raise NotImplementedError()
- def get_all_bw_usage(self, instances, start_time, stop_time=None):
- """Return bandwidth usage info for each interface on each
+ def get_all_bw_counters(self, instances):
+ """Return bandwidth usage counters for each interface on each
running VM"""
raise NotImplementedError()
diff --git a/nova/virt/fake.py b/nova/virt/fake.py
index a6476f9d9..959ab174c 100644
--- a/nova/virt/fake.py
+++ b/nova/virt/fake.py
@@ -186,11 +186,11 @@ class FakeDriver(driver.ComputeDriver):
def get_diagnostics(self, instance_name):
return 'FAKE_DIAGNOSTICS'
- def get_all_bw_usage(self, instances, start_time, stop_time=None):
- """Return bandwidth usage info for each interface on each
+ def get_all_bw_counters(self, instances):
+ """Return bandwidth usage counters for each interface on each
running VM"""
- bwusage = []
- return bwusage
+ bw = []
+ return bw
def block_stats(self, instance_name, disk_id):
return [0L, 0L, 0L, 0L, None]
diff --git a/nova/virt/xenapi/driver.py b/nova/virt/xenapi/driver.py
index 3425c64f8..ad2d64a38 100644
--- a/nova/virt/xenapi/driver.py
+++ b/nova/virt/xenapi/driver.py
@@ -298,35 +298,27 @@ class XenAPIDriver(driver.ComputeDriver):
"""Return data about VM diagnostics"""
return self._vmops.get_diagnostics(instance)
- def get_all_bw_usage(self, instances, start_time, stop_time=None):
- """Return bandwidth usage info for each interface on each
+ def get_all_bw_counters(self, instances):
+ """Return bandwidth usage counters for each interface on each
running VM"""
# we only care about VMs that correspond to a nova-managed
# instance:
imap = dict([(inst.name, inst.uuid) for inst in instances])
-
- bwusage = []
- start_time = time.mktime(start_time.timetuple())
- if stop_time:
- stop_time = time.mktime(stop_time.timetuple())
+ bwcounters = []
# get a dictionary of instance names. values are dictionaries
- # of mac addresses with values that are the bw stats:
+ # of mac addresses with values that are the bw counters:
# e.g. {'instance-001' : { 12:34:56:78:90:12 : {'bw_in': 0, ....}}
- iusages = self._vmops.get_all_bw_usage(start_time, stop_time)
- for instance_name in iusages:
+ all_counters = self._vmops.get_all_bw_counters()
+ for instance_name, counters in all_counters.iteritems():
if instance_name in imap:
# yes these are stats for a nova-managed vm
# correlate the stats with the nova instance uuid:
- iusage = iusages[instance_name]
-
- for macaddr, usage in iusage.iteritems():
- bwusage.append(dict(mac_address=macaddr,
- uuid=imap[instance_name],
- bw_in=usage['bw_in'],
- bw_out=usage['bw_out']))
- return bwusage
+ for vif_counter in counters.values():
+ vif_counter['uuid'] = imap[instance_name]
+ bwcounters.append(vif_counter)
+ return bwcounters
def get_console_output(self, instance):
"""Return snapshot of console"""
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index a9adb4575..2dc358f0f 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -1351,9 +1351,16 @@ def compile_diagnostics(record):
return {"Unable to retrieve diagnostics": e}
+def fetch_bandwidth(session):
+ bw = session.call_plugin_serialized('bandwidth', 'fetch_all_bandwidth')
+ return bw
+
+
def compile_metrics(start_time, stop_time=None):
"""Compile bandwidth usage, cpu, and disk metrics for all VMs on
- this host"""
+ this host.
+ Note that some stats, like bandwith, do not seem to be very
+ accurate in some of the data from XenServer (mdragon). """
start_time = int(start_time)
xml = _get_rrd_updates(_get_rrd_server(), start_time)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 52cb9b17b..ad6f0d38c 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -1208,34 +1208,31 @@ class VMOps(object):
vm_rec = self._session.call_xenapi("VM.get_record", vm_ref)
return vm_utils.compile_diagnostics(vm_rec)
- def get_all_bw_usage(self, start_time, stop_time=None):
- """Return bandwidth usage info for each interface on each
+ def _get_vif_device_map(self, vm_rec):
+ vif_map = {}
+ for vif in [self._session.call_xenapi("VIF.get_record", vrec)
+ for vrec in vm_rec['VIFs']]:
+ vif_map[vif['device']] = vif['MAC']
+ return vif_map
+
+ def get_all_bw_counters(self):
+ """Return running bandwidth counter for each interface on each
running VM"""
- try:
- metrics = vm_utils.compile_metrics(start_time, stop_time)
- except exception.CouldNotFetchMetrics:
- LOG.exception(_("Could not get bandwidth info."))
- return {}
+ counters = vm_utils.fetch_bandwidth(self._session)
bw = {}
- for uuid, data in metrics.iteritems():
- vm_ref = self._session.call_xenapi("VM.get_by_uuid", uuid)
- vm_rec = self._session.call_xenapi("VM.get_record", vm_ref)
- vif_map = {}
- for vif in [self._session.call_xenapi("VIF.get_record", vrec)
- for vrec in vm_rec['VIFs']]:
- vif_map[vif['device']] = vif['MAC']
+ for vm_ref, vm_rec in vm_utils.list_vms(self._session):
+ vif_map = self._get_vif_device_map(vm_rec)
name = vm_rec['name_label']
if 'nova_uuid' not in vm_rec['other_config']:
continue
+ dom = vm_rec.get('domid')
+ if dom is None or dom not in counters:
+ continue
vifs_bw = bw.setdefault(name, {})
- for key, val in data.iteritems():
- if key.startswith('vif_'):
- vname = key.split('_')[1]
- vif_bw = vifs_bw.setdefault(vif_map[vname], {})
- if key.endswith('tx'):
- vif_bw['bw_out'] = int(val)
- if key.endswith('rx'):
- vif_bw['bw_in'] = int(val)
+ for vif_num, vif_data in counters[dom].iteritems():
+ mac = vif_map[vif_num]
+ vif_data['mac_address'] = mac
+ vifs_bw[mac] = vif_data
return bw
def get_console_output(self, instance):
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/bandwidth b/plugins/xenserver/xenapi/etc/xapi.d/plugins/bandwidth
new file mode 100755
index 000000000..171011a06
--- /dev/null
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/bandwidth
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2012 OpenStack, LLC
+# All Rights Reserved.
+#
+# 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.
+
+"""Fetch Bandwidth data from VIF network devices."""
+
+import os
+import shutil
+
+import utils
+
+from pluginlib_nova import *
+configure_logging('bandwidth')
+
+
+def _read_proc_net():
+ devs = [l.strip() for l in open('/proc/net/dev', 'r').readlines()]
+ #ignore headers
+ devs = devs[2:]
+ dlist = [d.split(':', 1) for d in devs if d.startswith('vif')]
+ devmap = dict()
+ for name, stats in dlist:
+ slist = stats.split()
+ dom, vifnum = name[3:].split('.', 1)
+ dev = devmap.get(dom, {})
+ # Note, we deliberately swap in and out, as instance traffic
+ # shows up inverted due to going though the bridge. (mdragon)
+ dev[vifnum] = dict(bw_in=int(slist[0]), bw_out=int(slist[8]))
+ devmap[dom] = dev
+ return devmap
+
+
+def fetch_all_bandwidth(session):
+ return _read_proc_net()
+
+
+if __name__ == '__main__':
+ utils.register_plugin_calls(fetch_all_bandwidth)