202 lines
7.1 KiB
Python
202 lines
7.1 KiB
Python
# Copyright 2013 Intel Corporation.
|
|
# 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.
|
|
|
|
"""
|
|
Resource monitor API specification.
|
|
|
|
ResourceMonitorBase provides the definition of minimum set of methods
|
|
that needs to be implemented by Resource Monitor.
|
|
"""
|
|
|
|
import types
|
|
|
|
import six
|
|
|
|
from oslo.config import cfg
|
|
|
|
from nova import loadables
|
|
from nova.openstack.common.gettextutils import _
|
|
from nova.openstack.common import log as logging
|
|
from nova.openstack.common import timeutils
|
|
|
|
compute_monitors_opts = [
|
|
cfg.MultiStrOpt('compute_available_monitors',
|
|
default=['nova.compute.monitors.all_monitors'],
|
|
help='Monitor classes available to the compute which may '
|
|
'be specified more than once.'),
|
|
cfg.ListOpt('compute_monitors',
|
|
default=[],
|
|
help='A list of monitors that can be used for getting '
|
|
'compute metrics.'),
|
|
]
|
|
|
|
CONF = cfg.CONF
|
|
CONF.register_opts(compute_monitors_opts)
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class ResourceMonitorMeta(type):
|
|
def __init__(cls, names, bases, dict_):
|
|
"""Metaclass that allows us to create a function map and call it later
|
|
to get the metric names and their values.
|
|
"""
|
|
super(ResourceMonitorMeta, cls).__init__(names, bases, dict_)
|
|
|
|
prefix = '_get_'
|
|
prefix_len = len(prefix)
|
|
cls.metric_map = {}
|
|
for name, value in cls.__dict__.iteritems():
|
|
if (len(name) > prefix_len
|
|
and name[:prefix_len] == prefix
|
|
and isinstance(value, types.FunctionType)):
|
|
metric_name = name[prefix_len:].replace('_', '.')
|
|
cls.metric_map[metric_name] = value
|
|
|
|
|
|
@six.add_metaclass(ResourceMonitorMeta)
|
|
class ResourceMonitorBase(object):
|
|
"""Base class for resource monitors
|
|
"""
|
|
|
|
def __init__(self, parent):
|
|
self.compute_manager = parent
|
|
self.source = None
|
|
self._data = {}
|
|
|
|
@classmethod
|
|
def add_timestamp(arg, func):
|
|
"""Decorator to indicate that a method needs to add a timestamp.
|
|
|
|
When a function returning a value is decorated by the decorator,
|
|
which means a timestamp should be added into the returned value.
|
|
That is, a tuple (value, timestamp) is returned.
|
|
|
|
The timestamp is the time when we update the value in the _data.
|
|
|
|
If users hope to define how the timestamp is got by themselves,
|
|
they should not use this decorator in their own classes.
|
|
"""
|
|
def wrapper(cls, **kwargs):
|
|
return func(cls, **kwargs), cls._data.get("timestamp", None)
|
|
return wrapper
|
|
|
|
def _update_data(self):
|
|
"""Method to update the metrics data.
|
|
|
|
Each subclass can implement this method to update metrics
|
|
into _data. It will be called in get_metrics.
|
|
"""
|
|
pass
|
|
|
|
def get_metric_names(self):
|
|
"""Get available metric names.
|
|
|
|
Get available metric names, which are represented by a set of keys
|
|
that can be used to check conflicts and duplications
|
|
:returns: a set of keys representing metrics names
|
|
"""
|
|
return self.metric_map.keys()
|
|
|
|
def get_metrics(self, **kwargs):
|
|
"""Get metrics.
|
|
|
|
Get metrics, which are represented by a list of dictionaries
|
|
[{'name': metric name,
|
|
'value': metric value,
|
|
'timestamp': the time when the value is retrieved,
|
|
'source': what the value is got by}, ...]
|
|
:param kwargs: extra arguments that might be present
|
|
:returns: a list to tell the current metrics
|
|
"""
|
|
data = []
|
|
self._update_data()
|
|
for name, func in self.metric_map.iteritems():
|
|
ret = func(self, **kwargs)
|
|
data.append(self._populate(name, ret[0], ret[1]))
|
|
return data
|
|
|
|
def _populate(self, metric_name, metric_value, timestamp=None):
|
|
"""Populate the format what we want from metric name and metric value
|
|
"""
|
|
result = {}
|
|
result['name'] = metric_name
|
|
result['value'] = metric_value
|
|
result['timestamp'] = timestamp or timeutils.utcnow()
|
|
result['source'] = self.source
|
|
|
|
return result
|
|
|
|
|
|
class ResourceMonitorHandler(loadables.BaseLoader):
|
|
"""Base class to handle loading monitor classes.
|
|
"""
|
|
def __init__(self):
|
|
super(ResourceMonitorHandler, self).__init__(ResourceMonitorBase)
|
|
|
|
def choose_monitors(self, manager):
|
|
"""This function checks the monitor names and metrics names against a
|
|
predefined set of acceptable monitors.
|
|
"""
|
|
monitor_classes = self.get_matching_classes(
|
|
CONF.compute_available_monitors)
|
|
monitor_class_map = dict((cls.__name__, cls)
|
|
for cls in monitor_classes)
|
|
monitor_cls_names = CONF.compute_monitors
|
|
good_monitors = []
|
|
bad_monitors = []
|
|
metric_names = set()
|
|
for monitor_name in monitor_cls_names:
|
|
if monitor_name not in monitor_class_map:
|
|
bad_monitors.append(monitor_name)
|
|
continue
|
|
|
|
try:
|
|
# make sure different monitors do not have the same
|
|
# metric name
|
|
monitor = monitor_class_map[monitor_name](manager)
|
|
metric_names_tmp = set(monitor.get_metric_names())
|
|
overlap = metric_names & metric_names_tmp
|
|
if not overlap:
|
|
metric_names = metric_names | metric_names_tmp
|
|
good_monitors.append(monitor)
|
|
else:
|
|
msg = (_("Excluding monitor %(monitor_name)s due to "
|
|
"metric name overlap; overlapping "
|
|
"metrics: %(overlap)s") %
|
|
{'monitor_name': monitor_name,
|
|
'overlap': ', '.join(overlap)})
|
|
LOG.warn(msg)
|
|
bad_monitors.append(monitor_name)
|
|
except Exception as ex:
|
|
msg = (_("Monitor %(monitor_name)s cannot be used: %(ex)s") %
|
|
{'monitor_name': monitor_name, 'ex': ex})
|
|
LOG.warn(msg)
|
|
bad_monitors.append(monitor_name)
|
|
|
|
if bad_monitors:
|
|
LOG.warn(_("The following monitors have been disabled: %s"),
|
|
', '.join(bad_monitors))
|
|
|
|
return good_monitors
|
|
|
|
|
|
def all_monitors():
|
|
"""Return a list of monitor classes found in this directory.
|
|
|
|
This method is used as the default for available monitors
|
|
and should return a list of all monitor classes available.
|
|
"""
|
|
return ResourceMonitorHandler().get_all_classes()
|