Merge "Added snmp declarative hardware pollster"
This commit is contained in:
commit
577ffa8595
ceilometer
agent
hardware
tests/unit/hardware
@ -24,6 +24,7 @@ from oslo_context import context
|
||||
from oslo_log import log
|
||||
import oslo_messaging
|
||||
import six
|
||||
from stevedore import extension
|
||||
|
||||
from ceilometer.i18n import _
|
||||
from ceilometer import messaging
|
||||
@ -232,6 +233,33 @@ class PollsterBase(PluginBase):
|
||||
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def build_pollsters(cls):
|
||||
"""Return a list of tuple (name, pollster).
|
||||
|
||||
The name is the meter name which the pollster would return, the
|
||||
pollster is a pollster object instance. The pollster which implements
|
||||
this method should be registered in the namespace of
|
||||
ceilometer.builder.xxx instead of ceilometer.poll.xxx.
|
||||
"""
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def get_pollsters_extensions(cls):
|
||||
"""Return a list of stevedore extensions.
|
||||
|
||||
The returned stevedore extensions wrap the pollster object instances
|
||||
returned by build_pollsters.
|
||||
"""
|
||||
extensions = []
|
||||
try:
|
||||
for name, pollster in cls.build_pollsters():
|
||||
ext = extension.Extension(name, None, cls, pollster)
|
||||
extensions.append(ext)
|
||||
except Exception as err:
|
||||
raise ExtensionLoadError(err)
|
||||
return extensions
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class DiscoveryBase(object):
|
||||
|
@ -25,15 +25,26 @@ import six
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Inspector(object):
|
||||
@abc.abstractmethod
|
||||
def inspect_generic(self, host, identifier, cache, extra_metadata=None):
|
||||
def inspect_generic(self, host, identifier, cache,
|
||||
extra_metadata=None,
|
||||
param=None):
|
||||
"""A generic inspect function.
|
||||
|
||||
:param host: the target host
|
||||
:param identifier: the identifier of the metric
|
||||
:param cache: cache passed from the pollster
|
||||
:param extra_metadata: extra dict to be used as metadata
|
||||
:param param: a dict of inspector specific param
|
||||
:return: an iterator of (value, metadata, extra)
|
||||
:return value: the sample value
|
||||
:return metadata: dict to construct sample's metadata
|
||||
:return extra: dict of extra metadata to help constructing sample
|
||||
"""
|
||||
|
||||
def prepare_params(self, param):
|
||||
"""Parse the params to a format which the inspector itself recognizes.
|
||||
|
||||
:param param: inspector params from meter definition file
|
||||
:return: a dict of param which the inspector recognized
|
||||
"""
|
||||
return {}
|
||||
|
@ -19,6 +19,7 @@
|
||||
# under the License.
|
||||
"""Inspector for collecting data over SNMP"""
|
||||
|
||||
import copy
|
||||
from pysnmp.entity.rfc3413.oneliner import cmdgen
|
||||
|
||||
import six
|
||||
@ -111,7 +112,7 @@ class SNMPInspector(base.Inspector):
|
||||
|
||||
_CACHE_KEY_OID = "snmp_cached_oid"
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
The following mapping define how to construct
|
||||
(value, metadata, extra) returned by inspect_generic
|
||||
@ -166,7 +167,7 @@ class SNMPInspector(base.Inspector):
|
||||
it could be used to add information into extra dict to be returned
|
||||
to construct the pollster how to build final sample, e.g.
|
||||
extra.update('project_id': xy, 'user_id': zw)
|
||||
'''
|
||||
"""
|
||||
|
||||
MAPPING = {
|
||||
'cpu.load.1min': {
|
||||
@ -370,9 +371,14 @@ class SNMPInspector(base.Inspector):
|
||||
new_oids.append(metadata[0])
|
||||
return new_oids
|
||||
|
||||
def inspect_generic(self, host, identifier, cache, extra_metadata=None):
|
||||
def inspect_generic(self, host, identifier, cache,
|
||||
extra_metadata=None,
|
||||
param=None):
|
||||
# the snmp definition for the corresponding meter
|
||||
meter_def = self.MAPPING[identifier]
|
||||
if param:
|
||||
meter_def = param
|
||||
else:
|
||||
meter_def = self.MAPPING[identifier]
|
||||
# collect oids that needs to be queried
|
||||
oids_to_query = self._find_missing_oids(meter_def, cache)
|
||||
# query oids and populate into caches
|
||||
@ -389,7 +395,8 @@ class SNMPInspector(base.Inspector):
|
||||
meter_def['metric_oid'][0],
|
||||
meter_def['matching_type'],
|
||||
False)
|
||||
extra_metadata = extra_metadata or {}
|
||||
input_extra_metadata = extra_metadata
|
||||
|
||||
for oid in oids_for_sample_values:
|
||||
suffix = oid[len(meter_def['metric_oid'][0]):]
|
||||
value = self.get_oid_value(oid_cache,
|
||||
@ -399,6 +406,7 @@ class SNMPInspector(base.Inspector):
|
||||
metadata = self.construct_metadata(oid_cache,
|
||||
meter_def['metadata'],
|
||||
suffix)
|
||||
extra_metadata = copy.deepcopy(input_extra_metadata) or {}
|
||||
# call post_op for special cases
|
||||
if meter_def['post_op']:
|
||||
func = getattr(self, meter_def['post_op'], None)
|
||||
@ -431,6 +439,14 @@ class SNMPInspector(base.Inspector):
|
||||
metadata.update(ip=ip_addr)
|
||||
return value
|
||||
|
||||
def _post_op_disk(self, host, cache, meter_def,
|
||||
value, metadata, extra, suffix):
|
||||
if metadata.get('device'):
|
||||
res_id = extra.get('resource_id') or host.hostname
|
||||
res_id = res_id + ".%s" % metadata.get('device')
|
||||
extra.update(resource_id=res_id)
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def _get_auth_strategy(host):
|
||||
if host.password:
|
||||
@ -438,5 +454,14 @@ class SNMPInspector(base.Inspector):
|
||||
authKey=host.password)
|
||||
else:
|
||||
auth_strategy = cmdgen.CommunityData(host.username or 'public')
|
||||
|
||||
return auth_strategy
|
||||
|
||||
def prepare_params(self, param):
|
||||
processed = {}
|
||||
processed['matching_type'] = param['matching_type']
|
||||
processed['metric_oid'] = (param['oid'], eval(param['type']))
|
||||
processed['post_op'] = param.get('post_op', None)
|
||||
processed['metadata'] = {}
|
||||
for k, v in six.iteritems(param.get('metadata', {})):
|
||||
processed['metadata'][k] = (v['oid'], eval(v['type']))
|
||||
return processed
|
||||
|
181
ceilometer/hardware/pollsters/data/snmp.yaml
Normal file
181
ceilometer/hardware/pollsters/data/snmp.yaml
Normal file
@ -0,0 +1,181 @@
|
||||
---
|
||||
|
||||
metric:
|
||||
# cpu
|
||||
- name: hardware.cpu.load.1min
|
||||
unit: process
|
||||
type: gauge
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.4.1.2021.10.1.3.1"
|
||||
type: "lambda x: float(str(x))"
|
||||
|
||||
- name: hardware.cpu.load.5min
|
||||
unit: process
|
||||
type: gauge
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.4.1.2021.10.1.3.2"
|
||||
type: "lambda x: float(str(x))"
|
||||
|
||||
- name: hardware.cpu.load.15min
|
||||
unit: process
|
||||
type: gauge
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.4.1.2021.10.1.3.3"
|
||||
type: "lambda x: float(str(x))"
|
||||
# disk
|
||||
- name: hardware.disk.size.total
|
||||
unit: KB
|
||||
type: gauge
|
||||
snmp_inspector:
|
||||
matching_type: "type_prefix"
|
||||
oid: "1.3.6.1.4.1.2021.9.1.6"
|
||||
type: "int"
|
||||
metadata: &disk_metadata
|
||||
path:
|
||||
oid: "1.3.6.1.4.1.2021.9.1.2"
|
||||
type: "str"
|
||||
device:
|
||||
oid: "1.3.6.1.4.1.2021.9.1.3"
|
||||
type: "str"
|
||||
post_op: "_post_op_disk"
|
||||
|
||||
- name: hardware.disk.size.used
|
||||
unit: KB
|
||||
type: gauge
|
||||
snmp_inspector:
|
||||
matching_type: "type_prefix"
|
||||
oid: "1.3.6.1.4.1.2021.9.1.8"
|
||||
type: "int"
|
||||
metadata: *disk_metadata
|
||||
post_op: "_post_op_disk"
|
||||
# memory
|
||||
- name: hardware.memory.total
|
||||
unit: KB
|
||||
type: gauge
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.4.1.2021.4.5.0"
|
||||
type: "int"
|
||||
|
||||
- name: hardware.memory.used
|
||||
unit: KB
|
||||
type: gauge
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.4.1.2021.4.6.0"
|
||||
type: "int"
|
||||
post_op: "_post_op_memory_avail_to_used"
|
||||
|
||||
- name: hardware.memory.swap.total
|
||||
unit: KB
|
||||
type: gauge
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.4.1.2021.4.3.0"
|
||||
type: "int"
|
||||
|
||||
- name: hardware.memory.swap.avail
|
||||
unit: KB
|
||||
type: gauge
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.4.1.2021.4.4.0"
|
||||
type: "int"
|
||||
|
||||
- name: hardware.memory.buffer
|
||||
unit: KB
|
||||
type: gauge
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.4.1.2021.4.14.0"
|
||||
type: "int"
|
||||
|
||||
- name: hardware.memory.cached
|
||||
unit: KB
|
||||
type: gauge
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.4.1.2021.4.15.0"
|
||||
type: "int"
|
||||
# network interface
|
||||
- name: hardware.network.incoming.bytes
|
||||
unit: B
|
||||
type: cumulative
|
||||
snmp_inspector:
|
||||
matching_type: "type_prefix"
|
||||
oid: "1.3.6.1.2.1.2.2.1.10"
|
||||
type: "int"
|
||||
metadata: &net_metadata
|
||||
name:
|
||||
oid: "1.3.6.1.2.1.2.2.1.2"
|
||||
type: "str"
|
||||
speed:
|
||||
oid: "1.3.6.1.2.1.2.2.1.5"
|
||||
type: "lambda x: int(x) / 8"
|
||||
mac:
|
||||
oid: "1.3.6.1.2.1.2.2.1.6"
|
||||
type: "lambda x: x.prettyPrint().replace('0x', '')"
|
||||
post_op: "_post_op_net"
|
||||
|
||||
- name: hardware.network.outgoing.bytes
|
||||
unit: B
|
||||
type: cumulative
|
||||
snmp_inspector:
|
||||
matching_type: "type_prefix"
|
||||
oid: "1.3.6.1.2.1.2.2.1.16"
|
||||
type: "int"
|
||||
metadata: *net_metadata
|
||||
post_op: "_post_op_net"
|
||||
|
||||
- name: hardware.network.outgoing.errors
|
||||
unit: packet
|
||||
type: cumulative
|
||||
snmp_inspector:
|
||||
matching_type: "type_prefix"
|
||||
oid: "1.3.6.1.2.1.2.2.1.20"
|
||||
type: "int"
|
||||
metadata: *net_metadata
|
||||
post_op: "_post_op_net"
|
||||
#network aggregate
|
||||
- name: hardware.network.ip.outgoing.datagrams
|
||||
unit: datagrams
|
||||
type: cumulative
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.2.1.4.10.0"
|
||||
type: "int"
|
||||
|
||||
- name: hardware.network.ip.incoming.datagrams
|
||||
unit: datagrams
|
||||
type: cumulative
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.2.1.4.3.0"
|
||||
type: "int"
|
||||
#system stats
|
||||
- name: hardware.system_stats.cpu.idle
|
||||
unit: "%"
|
||||
type: gauge
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.4.1.2021.11.11.0"
|
||||
type: "int"
|
||||
|
||||
- name: hardware.system_stats.io.outgoing.blocks
|
||||
unit: blocks
|
||||
type: cumulative
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.4.1.2021.11.57.0"
|
||||
type: "int"
|
||||
|
||||
- name: hardware.system_stats.io.incoming.blocks
|
||||
unit: blocks
|
||||
type: cumulative
|
||||
snmp_inspector:
|
||||
matching_type: "type_exact"
|
||||
oid: "1.3.6.1.4.1.2021.11.58.0"
|
||||
type: "int"
|
264
ceilometer/hardware/pollsters/generic.py
Normal file
264
ceilometer/hardware/pollsters/generic.py
Normal file
@ -0,0 +1,264 @@
|
||||
#
|
||||
# Copyright 2015 Intel Corp.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import itertools
|
||||
import os
|
||||
import pkg_resources
|
||||
import yaml
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from oslo_utils import netutils
|
||||
import six
|
||||
|
||||
from ceilometer.agent import plugin_base
|
||||
from ceilometer.hardware import inspector as insloader
|
||||
from ceilometer.hardware.pollsters import util
|
||||
from ceilometer.i18n import _LE, _LI, _LW
|
||||
from ceilometer import sample
|
||||
|
||||
OPTS = [
|
||||
cfg.StrOpt('meter_definitions_file',
|
||||
default="snmp.yaml",
|
||||
help="Configuration file for defining hardware snmp meters."
|
||||
),
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(OPTS, group='hardware')
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class MeterDefinitionException(Exception):
|
||||
def __init__(self, message, definition_cfg):
|
||||
super(MeterDefinitionException, self).__init__(message)
|
||||
self.message = message
|
||||
self.definition_cfg = definition_cfg
|
||||
|
||||
def __str__(self):
|
||||
return '%s %s: %s' % (self.__class__.__name__,
|
||||
self.definition_cfg, self.message)
|
||||
|
||||
|
||||
class MeterDefinition(object):
|
||||
required_fields = ['name', 'unit', 'type']
|
||||
|
||||
def __init__(self, definition_cfg):
|
||||
self.cfg = definition_cfg
|
||||
for fname, fval in self.cfg.items():
|
||||
if (isinstance(fname, six.string_types) and
|
||||
(fname in self.required_fields or
|
||||
fname.endswith('_inspector'))):
|
||||
setattr(self, fname, fval)
|
||||
else:
|
||||
LOG.warn(_LW("Ignore unrecognized field %s"), fname)
|
||||
for fname in self.required_fields:
|
||||
if not getattr(self, fname, None):
|
||||
raise MeterDefinitionException(
|
||||
_LE("Missing field %s") % fname, self.cfg)
|
||||
if self.type not in sample.TYPES:
|
||||
raise MeterDefinitionException(
|
||||
_LE("Unrecognized type value %s") % self.type, self.cfg)
|
||||
|
||||
|
||||
class GenericHardwareDeclarativePollster(plugin_base.PollsterBase):
|
||||
CACHE_KEY = 'hardware.generic'
|
||||
mapping = None
|
||||
|
||||
def __init__(self):
|
||||
super(GenericHardwareDeclarativePollster, self).__init__()
|
||||
self.inspectors = {}
|
||||
|
||||
def _update_meter_definition(self, definition):
|
||||
self.meter_definition = definition
|
||||
self.cached_inspector_params = {}
|
||||
|
||||
@property
|
||||
def default_discovery(self):
|
||||
return 'tripleo_overcloud_nodes'
|
||||
|
||||
@staticmethod
|
||||
def _parse_resource(res):
|
||||
"""Parse resource from discovery.
|
||||
|
||||
Either URL can be given or dict. Dict has to contain at least
|
||||
keys 'resource_id' and 'resource_url', all the dict keys will be stored
|
||||
as metadata.
|
||||
|
||||
:param res: URL or dict containing all resource info.
|
||||
:return parsed_url, resource_id, metadata: Returns parsed URL used for
|
||||
SNMP query, unique identifier of the resource and metadata
|
||||
of the resource.
|
||||
"""
|
||||
parsed_url, resource_id, metadata = (None, None, None)
|
||||
if isinstance(res, dict):
|
||||
if 'resource_url' not in res or 'resource_id' not in res:
|
||||
LOG.error(_LE('Passed resource dict must contain keys '
|
||||
'resource_id and resource_url.'))
|
||||
else:
|
||||
metadata = res
|
||||
parsed_url = netutils.urlsplit(res['resource_url'])
|
||||
resource_id = res['resource_id']
|
||||
else:
|
||||
metadata = {}
|
||||
parsed_url = netutils.urlsplit(res)
|
||||
resource_id = res
|
||||
|
||||
return parsed_url, resource_id, metadata
|
||||
|
||||
def _get_inspector(self, parsed_url):
|
||||
if parsed_url.scheme not in self.inspectors:
|
||||
try:
|
||||
driver = insloader.get_inspector(parsed_url)
|
||||
self.inspectors[parsed_url.scheme] = driver
|
||||
except Exception as err:
|
||||
LOG.exception(_LE("Cannot load inspector %(name)s: %(err)s"),
|
||||
dict(name=parsed_url.scheme,
|
||||
err=err))
|
||||
raise err
|
||||
return self.inspectors[parsed_url.scheme]
|
||||
|
||||
def get_samples(self, manager, cache, resources=None):
|
||||
"""Return an iterable of Sample instances from polling the resources.
|
||||
|
||||
:param manager: The service manager invoking the plugin
|
||||
:param cache: A dictionary for passing data between plugins
|
||||
:param resources: end point to poll data from
|
||||
"""
|
||||
resources = resources or []
|
||||
h_cache = cache.setdefault(self.CACHE_KEY, {})
|
||||
sample_iters = []
|
||||
|
||||
# Get the meter identifiers to poll
|
||||
identifier = self.meter_definition.name
|
||||
|
||||
for resource in resources:
|
||||
parsed_url, res, extra_metadata = self._parse_resource(resource)
|
||||
if parsed_url is None:
|
||||
LOG.error(_LE("Skip invalid resource %s"), resource)
|
||||
continue
|
||||
ins = self._get_inspector(parsed_url)
|
||||
try:
|
||||
# Call hardware inspector to poll for the data
|
||||
i_cache = h_cache.setdefault(res, {})
|
||||
|
||||
# Prepare inspector parameters and cache it for performance
|
||||
param_key = parsed_url.scheme + '.' + identifier
|
||||
inspector_param = self.cached_inspector_params.get(param_key)
|
||||
if not inspector_param:
|
||||
param = getattr(self.meter_definition,
|
||||
parsed_url.scheme + '_inspector', {})
|
||||
inspector_param = ins.prepare_params(param)
|
||||
self.cached_inspector_params[param_key] = inspector_param
|
||||
|
||||
if identifier not in i_cache:
|
||||
i_cache[identifier] = list(ins.inspect_generic(
|
||||
parsed_url,
|
||||
identifier,
|
||||
i_cache,
|
||||
extra_metadata,
|
||||
param=inspector_param))
|
||||
# Generate samples
|
||||
if i_cache[identifier]:
|
||||
sample_iters.append(self.generate_samples(
|
||||
parsed_url,
|
||||
i_cache[identifier]))
|
||||
except Exception as err:
|
||||
LOG.exception(_LE('inspector call failed for %(ident)s '
|
||||
'host %(host)s: %(err)s'),
|
||||
dict(ident=identifier,
|
||||
host=parsed_url.hostname,
|
||||
err=err))
|
||||
return itertools.chain(*sample_iters)
|
||||
|
||||
def generate_samples(self, host_url, data):
|
||||
"""Generate a list of Sample from the data returned by inspector
|
||||
|
||||
:param host_url: host url of the endpoint
|
||||
:param data: list of data returned by the corresponding inspector
|
||||
"""
|
||||
samples = []
|
||||
definition = self.meter_definition
|
||||
for (value, metadata, extra) in data:
|
||||
s = util.make_sample_from_host(host_url,
|
||||
name=definition.name,
|
||||
sample_type=definition.type,
|
||||
unit=definition.unit,
|
||||
volume=value,
|
||||
res_metadata=metadata,
|
||||
extra=extra,
|
||||
name_prefix=None)
|
||||
samples.append(s)
|
||||
return samples
|
||||
|
||||
@classmethod
|
||||
def build_pollsters(cls):
|
||||
if not cls.mapping:
|
||||
cls.mapping = load_definition(setup_meters_config())
|
||||
|
||||
pollsters = []
|
||||
for name in cls.mapping:
|
||||
pollster = cls()
|
||||
pollster._update_meter_definition(cls.mapping[name])
|
||||
pollsters.append((name, pollster))
|
||||
return pollsters
|
||||
|
||||
|
||||
def get_config_file():
|
||||
config_file = cfg.CONF.hardware.meter_definitions_file
|
||||
if not os.path.exists(config_file):
|
||||
config_file = cfg.CONF.find_file(config_file)
|
||||
if not config_file:
|
||||
config_file = pkg_resources.resource_filename(
|
||||
__name__, "data/snmp.yaml")
|
||||
return config_file
|
||||
|
||||
|
||||
def setup_meters_config():
|
||||
"""load the meters definitions from yaml config file."""
|
||||
config_file = get_config_file()
|
||||
|
||||
LOG.debug("Hardware snmp meter definition file: %s" % config_file)
|
||||
with open(config_file) as cf:
|
||||
config = cf.read()
|
||||
|
||||
try:
|
||||
meters_config = yaml.safe_load(config)
|
||||
except yaml.YAMLError as err:
|
||||
if hasattr(err, 'problem_mark'):
|
||||
mark = err.problem_mark
|
||||
errmsg = (_LE("Invalid YAML syntax in Meter Definitions file "
|
||||
"%(file)s at line: %(line)s, column: %(column)s.")
|
||||
% dict(file=config_file,
|
||||
line=mark.line + 1,
|
||||
column=mark.column + 1))
|
||||
else:
|
||||
errmsg = (_LE("YAML error reading Meter Definitions file "
|
||||
"%(file)s")
|
||||
% dict(file=config_file))
|
||||
LOG.error(errmsg)
|
||||
raise
|
||||
|
||||
LOG.info(_LI("Meter Definitions: %s") % meters_config)
|
||||
|
||||
return meters_config
|
||||
|
||||
|
||||
def load_definition(config_def):
|
||||
mappings = {}
|
||||
for meter_def in config_def.get('metric', []):
|
||||
meter = MeterDefinition(meter_def)
|
||||
mappings[meter.name] = meter
|
||||
return mappings
|
@ -41,15 +41,18 @@ def make_resource_metadata(res_metadata=None, host_url=None):
|
||||
|
||||
def make_sample_from_host(host_url, name, sample_type, unit, volume,
|
||||
project_id=None, user_id=None, resource_id=None,
|
||||
res_metadata=None, extra=None):
|
||||
res_metadata=None, extra=None,
|
||||
name_prefix='hardware'):
|
||||
|
||||
extra = extra or {}
|
||||
resource_metadata = make_resource_metadata(res_metadata, host_url)
|
||||
resource_metadata.update(extra)
|
||||
|
||||
res_id = resource_id or extra.get('resource_id') or host_url.hostname
|
||||
if name_prefix:
|
||||
name = name_prefix + '.' + name
|
||||
return sample.Sample(
|
||||
name='hardware.' + name,
|
||||
name=name,
|
||||
type=sample_type,
|
||||
unit=unit,
|
||||
volume=volume,
|
||||
|
@ -143,3 +143,38 @@ class TestSNMPInspector(test_base.BaseTestCase):
|
||||
self.assertEqual(8, ret)
|
||||
self.assertIn('ip', metadata)
|
||||
self.assertIn("2", metadata['ip'])
|
||||
|
||||
def test_post_op_disk(self):
|
||||
cache = {}
|
||||
metadata = dict(device='/dev/sda1',
|
||||
path='/')
|
||||
extra = {}
|
||||
ret = self.inspector._post_op_disk(self.host, cache, None,
|
||||
value=8,
|
||||
metadata=metadata,
|
||||
extra=extra,
|
||||
suffix=None)
|
||||
self.assertEqual(8, ret)
|
||||
self.assertIn('resource_id', extra)
|
||||
self.assertEqual("localhost./dev/sda1", extra['resource_id'])
|
||||
|
||||
def test_prepare_params(self):
|
||||
param = {'post_op': '_post_op_disk',
|
||||
'oid': '1.3.6.1.4.1.2021.9.1.6',
|
||||
'type': 'int',
|
||||
'matching_type': 'type_prefix',
|
||||
'metadata': {
|
||||
'device': {'oid': '1.3.6.1.4.1.2021.9.1.3',
|
||||
'type': 'str'},
|
||||
'path': {'oid': '1.3.6.1.4.1.2021.9.1.2',
|
||||
'type': "lambda x: str(x)"}}}
|
||||
processed = self.inspector.prepare_params(param)
|
||||
self.assertEqual('_post_op_disk', processed['post_op'])
|
||||
self.assertEqual('1.3.6.1.4.1.2021.9.1.6', processed['metric_oid'][0])
|
||||
self.assertEqual(int, processed['metric_oid'][1])
|
||||
self.assertEqual(snmp.PREFIX, processed['matching_type'])
|
||||
self.assertEqual(2, len(processed['metadata'].keys()))
|
||||
self.assertEqual('1.3.6.1.4.1.2021.9.1.2',
|
||||
processed['metadata']['path'][0])
|
||||
self.assertEqual("4",
|
||||
processed['metadata']['path'][1](4))
|
||||
|
168
ceilometer/tests/unit/hardware/pollsters/test_generic.py
Normal file
168
ceilometer/tests/unit/hardware/pollsters/test_generic.py
Normal file
@ -0,0 +1,168 @@
|
||||
#
|
||||
# Copyright 2015 Intel Corp.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
import six
|
||||
import yaml
|
||||
|
||||
from oslo_config import fixture as fixture_config
|
||||
from oslo_utils import fileutils
|
||||
from oslotest import mockpatch
|
||||
|
||||
from ceilometer.hardware.inspector import base as inspector_base
|
||||
from ceilometer.hardware.pollsters import generic
|
||||
from ceilometer import sample
|
||||
from ceilometer import service as ceilometer_service
|
||||
from ceilometer.tests import base as test_base
|
||||
|
||||
|
||||
class TestMeterDefinition(test_base.BaseTestCase):
|
||||
def test_config_definition(self):
|
||||
cfg = dict(name='test',
|
||||
type='gauge',
|
||||
unit='B',
|
||||
snmp_inspector={})
|
||||
definition = generic.MeterDefinition(cfg)
|
||||
self.assertEqual('test', definition.name)
|
||||
self.assertEqual('gauge', definition.type)
|
||||
self.assertEqual('B', definition.unit)
|
||||
self.assertEqual({}, definition.snmp_inspector)
|
||||
|
||||
def test_config_missing_field(self):
|
||||
cfg = dict(name='test', type='gauge')
|
||||
try:
|
||||
generic.MeterDefinition(cfg)
|
||||
except generic.MeterDefinitionException as e:
|
||||
self.assertEqual("Missing field unit", e.message)
|
||||
|
||||
def test_config_invalid_field(self):
|
||||
cfg = dict(name='test',
|
||||
type='gauge',
|
||||
unit='B',
|
||||
invalid={})
|
||||
definition = generic.MeterDefinition(cfg)
|
||||
self.assertEqual("foobar", getattr(definition, 'invalid', 'foobar'))
|
||||
|
||||
def test_config_invalid_type_field(self):
|
||||
cfg = dict(name='test',
|
||||
type='invalid',
|
||||
unit='B',
|
||||
snmp_inspector={})
|
||||
try:
|
||||
generic.MeterDefinition(cfg)
|
||||
except generic.MeterDefinitionException as e:
|
||||
self.assertEqual("Unrecognized type value invalid", e.message)
|
||||
|
||||
|
||||
class FakeInspector(inspector_base.Inspector):
|
||||
net_metadata = dict(name='test.teest',
|
||||
mac='001122334455',
|
||||
ip='10.0.0.2',
|
||||
speed=1000)
|
||||
DATA = {
|
||||
'hardware.test1': (0.99, {}, {}),
|
||||
'hardware.test2.abc': (90, net_metadata, {}),
|
||||
}
|
||||
|
||||
def inspect_generic(self, host, identifier, cache,
|
||||
extra_metadata=None, param=None):
|
||||
yield self.DATA[identifier]
|
||||
|
||||
|
||||
class TestGenericPollsters(test_base.BaseTestCase):
|
||||
@staticmethod
|
||||
def faux_get_inspector(url, namespace=None):
|
||||
return FakeInspector()
|
||||
|
||||
def setUp(self):
|
||||
super(TestGenericPollsters, self).setUp()
|
||||
self.conf = self.useFixture(fixture_config.Config()).conf
|
||||
self.resources = ["snmp://test", "snmp://test2"]
|
||||
self.useFixture(mockpatch.Patch(
|
||||
'ceilometer.hardware.inspector.get_inspector',
|
||||
self.faux_get_inspector))
|
||||
ceilometer_service.prepare_service(argv=[], config_files=[])
|
||||
self.pollster = generic.GenericHardwareDeclarativePollster()
|
||||
|
||||
def test_fallback_meter_path(self):
|
||||
self.useFixture(mockpatch.PatchObject(self.conf,
|
||||
'find_file', return_value=None))
|
||||
fall_bak_path = generic.get_config_file()
|
||||
self.assertIn("hardware/pollsters/data/snmp.yaml", fall_bak_path)
|
||||
|
||||
def __setup_meter_def_file(self, cfg):
|
||||
if six.PY3:
|
||||
cfg = cfg.encode('utf-8')
|
||||
meter_cfg_file = fileutils.write_to_tempfile(content=cfg,
|
||||
prefix="snmp",
|
||||
suffix="yaml")
|
||||
self.conf.set_override(
|
||||
'meter_definitions_file',
|
||||
meter_cfg_file, group='hardware')
|
||||
cfg = generic.setup_meters_config()
|
||||
return cfg
|
||||
|
||||
@mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock())
|
||||
def _check_get_samples(self, name, definition,
|
||||
expected_value, expected_type, expected_unit=None):
|
||||
self.pollster._update_meter_definition(definition)
|
||||
cache = {}
|
||||
samples = list(self.pollster.get_samples(None, cache,
|
||||
self.resources))
|
||||
self.assertTrue(samples)
|
||||
self.assertIn(self.pollster.CACHE_KEY, cache)
|
||||
for resource in self.resources:
|
||||
self.assertIn(resource, cache[self.pollster.CACHE_KEY])
|
||||
|
||||
self.assertEqual(set([name]),
|
||||
set([s.name for s in samples]))
|
||||
match = [s for s in samples if s.name == name]
|
||||
self.assertEqual(expected_value, match[0].volume)
|
||||
self.assertEqual(expected_type, match[0].type)
|
||||
if expected_unit:
|
||||
self.assertEqual(expected_unit, match[0].unit)
|
||||
|
||||
def test_get_samples(self):
|
||||
param = dict(matching_type='type_exact',
|
||||
oid='1.3.6.1.4.1.2021.10.1.3.1',
|
||||
type='lambda x: float(str(x))')
|
||||
meter_def = generic.MeterDefinition(dict(type='gauge',
|
||||
name='hardware.test1',
|
||||
unit='process',
|
||||
snmp_inspector=param))
|
||||
self._check_get_samples('hardware.test1',
|
||||
meter_def,
|
||||
0.99, sample.TYPE_GAUGE,
|
||||
expected_unit='process')
|
||||
|
||||
def test_get_pollsters_extensions(self):
|
||||
param = dict(matching_type='type_exact',
|
||||
oid='1.3.6.1.4.1.2021.10.1.3.1',
|
||||
type='lambda x: float(str(x))')
|
||||
meter_cfg = yaml.dump(
|
||||
{'metric': [dict(type='gauge',
|
||||
name='hardware.test1',
|
||||
unit='process',
|
||||
snmp_inspector=param),
|
||||
dict(type='gauge',
|
||||
name='hardware.test2.abc',
|
||||
unit='process',
|
||||
snmp_inspector=param)]})
|
||||
self.__setup_meter_def_file(meter_cfg)
|
||||
pollster = generic.GenericHardwareDeclarativePollster
|
||||
exts = pollster.get_pollsters_extensions()
|
||||
self.assertEqual(2, len(exts))
|
||||
self.assertIn(exts[0].name, ['hardware.test1', 'hardware.test2.abc'])
|
||||
self.assertIn(exts[1].name, ['hardware.test1', 'hardware.test2.abc'])
|
Loading…
x
Reference in New Issue
Block a user