Allow monitor plugins to set own metric object

The values reported by a monitor depends on the type of value which
will not be uniform for all the monitors. For example, the memory
bandwidth monitor will return a dict of ints while the cpu monitor
returns a normal int value. The monitor metric objects offer
different fields to be set depending on the monitor in question.

This patchset allows the monitors to decide what metric field
it would like to populate as over a general implementation of
creating the monitor metric object inside the base module.
This would also help in exchanging versioned objects between
the monitors and the compute drivers.

Eventually this work will lead up to making the metrics.update
notification as versioned.

Related to blueprint memory-bw
Co-Authored-By: Ahilan Rajadeva <rajadeva@us.ibm.com>

Change-Id: Ieaf3d44bbe73e71ad877199bbbc779afa4218b51
This commit is contained in:
Sudipta Biswas 2016-01-20 23:14:41 +05:30
parent a074c661e4
commit 2a53063679
6 changed files with 36 additions and 34 deletions

View File

@ -14,7 +14,6 @@ import abc
import six
from nova import objects
from nova.objects import fields
@ -44,9 +43,10 @@ class MonitorBase(object):
raise NotImplementedError('get_metric_names')
@abc.abstractmethod
def get_metrics(self):
"""Returns a list of tuples containing information for all metrics
tracked by the monitor.
def populate_metrics(self, metric_list):
"""Monitors are responsible for populating this metric_list object
with nova.objects.MonitorMetric objects with values collected via
the respective compute drivers.
Note that if the monitor class is responsible for tracking a *related*
set of metrics -- e.g. a set of percentages of CPU time allocated to
@ -54,26 +54,9 @@ class MonitorBase(object):
implementation to do a single sampling call to the underlying monitor
to ensure that related metric values make logical sense.
:returns: list of (metric_name, value, timestamp) tuples
:param metric_list: A mutable reference of the metric list object
"""
raise NotImplementedError('get_metrics')
def add_metrics_to_list(self, metrics_list):
"""Adds metric objects to a supplied list object.
:param metric_list: nova.objects.MonitorMetricList that the monitor
plugin should append nova.objects.MonitorMetric
objects to.
"""
metric_data = self.get_metrics()
metrics = []
for (name, value, timestamp) in metric_data:
metric = objects.MonitorMetric(name=name,
value=value,
timestamp=timestamp,
source=self.source)
metrics.append(metric)
metrics_list.objects.extend(metrics)
raise NotImplementedError('populate_metrics')
class CPUMonitorBase(MonitorBase):

View File

@ -24,6 +24,7 @@ from nova.compute.monitors import base
import nova.conf
from nova import exception
from nova.i18n import _LE
from nova import objects
CONF = nova.conf.CONF
LOG = logging.getLogger(__name__)
@ -39,12 +40,15 @@ class Monitor(base.CPUMonitorBase):
self._data = {}
self._cpu_stats = {}
def get_metrics(self):
metrics = []
def populate_metrics(self, metric_list):
self._update_data()
for name in self.get_metric_names():
metrics.append((name, self._data[name], self._data["timestamp"]))
return metrics
metric_object = objects.MonitorMetric()
metric_object.name = name
metric_object.value = self._data[name]
metric_object.timestamp = self._data["timestamp"]
metric_object.source = self.source
metric_list.objects.append(metric_object)
def _update_data(self):
self._data = {}

View File

@ -471,7 +471,7 @@ class ResourceTracker(object):
metrics_info = {}
for monitor in self.monitors:
try:
monitor.add_metrics_to_list(metrics)
monitor.populate_metrics(metrics)
except Exception as exc:
LOG.warning(_LW("Cannot get the metrics from %(mon)s; "
"error: %(exc)s"),

View File

@ -52,10 +52,10 @@ class VirtDriverCPUMonitorTestCase(test.NoDBTestCase):
self.assertIn("cpu.iowait.percent", names)
self.assertIn("cpu.percent", names)
def test_get_metrics(self):
def test_populate_metrics(self):
metrics = objects.MonitorMetricList()
monitor = virt_driver.Monitor(FakeResourceTracker())
monitor.add_metrics_to_list(metrics)
monitor.populate_metrics(metrics)
names = monitor.get_metric_names()
for metric in metrics.objects:
self.assertIn(metric.name, names)
@ -82,5 +82,5 @@ class VirtDriverCPUMonitorTestCase(test.NoDBTestCase):
monitor = virt_driver.Monitor(FakeResourceTracker())
with mock.patch.object(FakeDriver, 'get_host_cpu_stats') as mocked:
monitor.add_metrics_to_list(metrics)
monitor.populate_metrics(metrics)
mocked.assert_called_once_with()

View File

@ -638,7 +638,7 @@ class ComputeMonitorTestCase(BaseTestCase):
@mock.patch.object(resource_tracker.LOG, 'warning')
def test_get_host_metrics_exception(self, mock_LOG_warning):
monitor = mock.MagicMock()
monitor.add_metrics_to_list.side_effect = Exception
monitor.populate_metrics.side_effect = Exception
self.tracker.monitors = [monitor]
metrics = self.tracker._get_host_metrics(self.context,
self.node_name)
@ -658,8 +658,13 @@ class ComputeMonitorTestCase(BaseTestCase):
def get_metric_names(self):
return set(["cpu.frequency"])
def get_metrics(self):
return [("cpu.frequency", 100, self.NOW_TS)]
def populate_metrics(self, monitor_list):
metric_object = objects.MonitorMetric()
metric_object.name = 'cpu.frequency'
metric_object.value = 100
metric_object.timestamp = self.NOW_TS
metric_object.source = self.source
monitor_list.objects.append(metric_object)
self.tracker.monitors = [FakeCPUMonitor(None)]
mock_notifier = mock.Mock()

View File

@ -0,0 +1,10 @@
---
upgrade:
- The get_metrics API has been replaced by
populate_metrics in nova.compute.monitors.base
module. This change is introduced to allow each
monitor plugin to have the flexibility of setting
it's own metric value types. The in-tree metrics
plugins are modified as a part of this change.
However, the out-of-tree plugins would have to
adapt to the new API in order to work with nova.