Remove GenericHardwareDeclarativePollster
... and discovery/inspector plugins depending on the pollster. These were implemented to gather metrics via SNMP daemon in TripleO-managed deployment but these are no longer valid since Telemetry services and Nova were removed from undercloud. Change-Id: If9a6b695ba799c766314a88328ea8a779407acc0
This commit is contained in:
parent
41ac16b5ed
commit
a28cef7036
@ -1,144 +0,0 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# 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 warnings
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
from oslo_log import log
|
|
||||||
from oslo_utils import timeutils
|
|
||||||
|
|
||||||
from ceilometer import nova_client
|
|
||||||
from ceilometer.polling import plugin_base
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
|
||||||
|
|
||||||
OPTS = [
|
|
||||||
cfg.StrOpt('url_scheme',
|
|
||||||
default='snmp://',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='URL scheme to use for hardware nodes.'),
|
|
||||||
cfg.StrOpt('readonly_user_name',
|
|
||||||
default='ro_snmp_user',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='SNMPd user name of all nodes running in the cloud.'),
|
|
||||||
cfg.StrOpt('readonly_user_password',
|
|
||||||
default='password',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='SNMPd v3 authentication password of all the nodes '
|
|
||||||
'running in the cloud.',
|
|
||||||
secret=True),
|
|
||||||
cfg.StrOpt('readonly_user_auth_proto',
|
|
||||||
choices=['md5', 'sha'],
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='SNMPd v3 authentication algorithm of all the nodes '
|
|
||||||
'running in the cloud'),
|
|
||||||
cfg.StrOpt('readonly_user_priv_proto',
|
|
||||||
choices=['des', 'aes128', '3des', 'aes192', 'aes256'],
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='SNMPd v3 encryption algorithm of all the nodes '
|
|
||||||
'running in the cloud'),
|
|
||||||
cfg.StrOpt('readonly_user_priv_password',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='SNMPd v3 encryption password of all the nodes '
|
|
||||||
'running in the cloud.',
|
|
||||||
secret=True),
|
|
||||||
cfg.StrOpt('tripleo_network_name',
|
|
||||||
default='ctlplane',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help='Name of the control plane Tripleo network')
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class NodesDiscoveryTripleO(plugin_base.DiscoveryBase):
|
|
||||||
def __init__(self, conf):
|
|
||||||
super(NodesDiscoveryTripleO, self).__init__(conf)
|
|
||||||
self.nova_cli = nova_client.Client(conf)
|
|
||||||
self.last_run = None
|
|
||||||
self.instances = {}
|
|
||||||
|
|
||||||
warnings.warn('GenericHardwareDeclarativePollster has been deprecated '
|
|
||||||
'and will be removed in a future release.',
|
|
||||||
category=DeprecationWarning, stacklevel=3)
|
|
||||||
|
|
||||||
def _make_resource_url(self, ip):
|
|
||||||
hwconf = self.conf.hardware
|
|
||||||
url = hwconf.url_scheme
|
|
||||||
username = hwconf.readonly_user_name
|
|
||||||
password = hwconf.readonly_user_password
|
|
||||||
if username:
|
|
||||||
url += username
|
|
||||||
if password:
|
|
||||||
url += ':' + password
|
|
||||||
if username or password:
|
|
||||||
url += '@'
|
|
||||||
url += ip
|
|
||||||
|
|
||||||
opts = ['auth_proto', 'priv_proto', 'priv_password']
|
|
||||||
query = "&".join(opt + "=" + hwconf['readonly_user_%s' % opt]
|
|
||||||
for opt in opts
|
|
||||||
if hwconf['readonly_user_%s' % opt])
|
|
||||||
if query:
|
|
||||||
url += '?' + query
|
|
||||||
|
|
||||||
return url
|
|
||||||
|
|
||||||
def discover(self, manager, param=None):
|
|
||||||
"""Discover resources to monitor.
|
|
||||||
|
|
||||||
instance_get_all will return all instances if last_run is None,
|
|
||||||
and will return only the instances changed since the last_run time.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
instances = self.nova_cli.instance_get_all(self.last_run)
|
|
||||||
except Exception:
|
|
||||||
# NOTE(zqfan): instance_get_all is wrapped and will log exception
|
|
||||||
# when there is any error. It is no need to raise it again and
|
|
||||||
# print one more time.
|
|
||||||
return []
|
|
||||||
|
|
||||||
for instance in instances:
|
|
||||||
if getattr(instance, 'OS-EXT-STS:vm_state', None) in ['deleted',
|
|
||||||
'error']:
|
|
||||||
self.instances.pop(instance.id, None)
|
|
||||||
else:
|
|
||||||
self.instances[instance.id] = instance
|
|
||||||
self.last_run = timeutils.utcnow(True).isoformat()
|
|
||||||
|
|
||||||
resources = []
|
|
||||||
for instance in self.instances.values():
|
|
||||||
addresses = instance.addresses.get(
|
|
||||||
self.conf.hardware.tripleo_network_name)
|
|
||||||
if addresses is None:
|
|
||||||
# NOTE(sileht): This is not a tripleo undercloud instance, this
|
|
||||||
# is a cheap detection if ironic node deployed by tripleo, but
|
|
||||||
# nova don't expose anything more useful and we must not log a
|
|
||||||
# ERROR when the instance is not a tripleo undercloud one.
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
ip_address = addresses[0].get('addr')
|
|
||||||
final_address = self._make_resource_url(ip_address)
|
|
||||||
resource = {
|
|
||||||
'resource_id': instance.id,
|
|
||||||
'resource_url': final_address,
|
|
||||||
'mac_addr': addresses[0].get('OS-EXT-IPS-MAC:mac_addr'),
|
|
||||||
'image_id': instance.image['id'],
|
|
||||||
'flavor_id': instance.flavor['id']
|
|
||||||
}
|
|
||||||
|
|
||||||
resources.append(resource)
|
|
||||||
except KeyError:
|
|
||||||
LOG.error("Couldn't obtain IP address of "
|
|
||||||
"instance %s" % instance.id)
|
|
||||||
|
|
||||||
return resources
|
|
@ -1,26 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright 2014 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.
|
|
||||||
|
|
||||||
from stevedore import driver
|
|
||||||
|
|
||||||
|
|
||||||
def get_inspector(parsed_url, namespace='ceilometer.hardware.inspectors'):
|
|
||||||
"""Get inspector driver and load it.
|
|
||||||
|
|
||||||
:param parsed_url: urlparse.SplitResult object for the inspector
|
|
||||||
:param namespace: Namespace to use to look for drivers.
|
|
||||||
"""
|
|
||||||
loaded_driver = driver.DriverManager(namespace, parsed_url.scheme)
|
|
||||||
return loaded_driver.driver()
|
|
@ -1,40 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright 2014 ZHAW SoE
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
"""Inspector abstraction for read-only access to hardware components"""
|
|
||||||
|
|
||||||
import abc
|
|
||||||
|
|
||||||
|
|
||||||
class Inspector(object, metaclass=abc.ABCMeta):
|
|
||||||
@abc.abstractmethod
|
|
||||||
def inspect_generic(self, host, cache, extra_metadata, param):
|
|
||||||
"""A generic inspect function.
|
|
||||||
|
|
||||||
:param host: the target host
|
|
||||||
: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) containing the sample
|
|
||||||
value, metadata dict to construct sample's metadata, and
|
|
||||||
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 {}
|
|
@ -1,346 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright 2014 ZHAW SoE
|
|
||||||
# Copyright 2014 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.
|
|
||||||
"""Inspector for collecting data over SNMP"""
|
|
||||||
|
|
||||||
import copy
|
|
||||||
|
|
||||||
from oslo_log import log
|
|
||||||
from pysnmp.entity.rfc3413.oneliner import cmdgen
|
|
||||||
from pysnmp.proto import rfc1905
|
|
||||||
|
|
||||||
from urllib import parse as urlparse
|
|
||||||
|
|
||||||
from ceilometer.hardware.inspector import base
|
|
||||||
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class SNMPException(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def parse_snmp_return(ret, is_bulk=False):
|
|
||||||
"""Check the return value of snmp operations
|
|
||||||
|
|
||||||
:param ret: a tuple of (errorIndication, errorStatus, errorIndex, data)
|
|
||||||
returned by pysnmp
|
|
||||||
:param is_bulk: True if the ret value is from GetBulkRequest
|
|
||||||
:return: a tuple of (err, data)
|
|
||||||
err: True if error found, or False if no error found
|
|
||||||
data: a string of error description if error found, or the
|
|
||||||
actual return data of the snmp operation
|
|
||||||
"""
|
|
||||||
err = True
|
|
||||||
(errIndication, errStatus, errIdx, varBinds) = ret
|
|
||||||
if errIndication:
|
|
||||||
data = errIndication
|
|
||||||
elif errStatus:
|
|
||||||
if is_bulk:
|
|
||||||
varBinds = varBinds[-1]
|
|
||||||
data = "%s at %s" % (errStatus.prettyPrint(),
|
|
||||||
errIdx and varBinds[int(errIdx) - 1] or "?")
|
|
||||||
else:
|
|
||||||
err = False
|
|
||||||
data = varBinds
|
|
||||||
return err, data
|
|
||||||
|
|
||||||
|
|
||||||
EXACT = 'type_exact'
|
|
||||||
PREFIX = 'type_prefix'
|
|
||||||
|
|
||||||
_auth_proto_mapping = {
|
|
||||||
'md5': cmdgen.usmHMACMD5AuthProtocol,
|
|
||||||
'sha': cmdgen.usmHMACSHAAuthProtocol,
|
|
||||||
}
|
|
||||||
_priv_proto_mapping = {
|
|
||||||
'des': cmdgen.usmDESPrivProtocol,
|
|
||||||
'aes128': cmdgen.usmAesCfb128Protocol,
|
|
||||||
'3des': cmdgen.usm3DESEDEPrivProtocol,
|
|
||||||
'aes192': cmdgen.usmAesCfb192Protocol,
|
|
||||||
'aes256': cmdgen.usmAesCfb256Protocol,
|
|
||||||
}
|
|
||||||
_usm_proto_mapping = {
|
|
||||||
'auth_proto': ('authProtocol', _auth_proto_mapping),
|
|
||||||
'priv_proto': ('privProtocol', _priv_proto_mapping),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class SNMPInspector(base.Inspector):
|
|
||||||
# Default port
|
|
||||||
_port = 161
|
|
||||||
|
|
||||||
_CACHE_KEY_OID = "snmp_cached_oid"
|
|
||||||
|
|
||||||
# NOTE: The following mapping has been moved to the yaml file identified
|
|
||||||
# by the config options hardware.meter_definitions_file. However, we still
|
|
||||||
# keep the description here for code reading purpose.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
The following mapping define how to construct
|
|
||||||
(value, metadata, extra) returned by inspect_generic
|
|
||||||
MAPPING = {
|
|
||||||
'identifier: {
|
|
||||||
'matching_type': EXACT or PREFIX,
|
|
||||||
'metric_oid': (oid, value_converter)
|
|
||||||
'metadata': {
|
|
||||||
metadata_name1: (oid1, value_converter),
|
|
||||||
metadata_name2: (oid2, value_converter),
|
|
||||||
},
|
|
||||||
'post_op': special func to modify the return data,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
For matching_type of EXACT, each item in the above mapping will
|
|
||||||
return exact one (value, metadata, extra) tuple. The value would be
|
|
||||||
returned from SNMP request GetRequest for oid of 'metric_oid', the
|
|
||||||
metadata dict would be constructed based on the returning from SNMP
|
|
||||||
GetRequest for oids of 'metadata'.
|
|
||||||
|
|
||||||
For matching_type of PREFIX, SNMP request GetBulkRequest
|
|
||||||
would be sent to get values for oids of 'metric_oid' and
|
|
||||||
'metadata' of each item in the above mapping. And each item might
|
|
||||||
return multiple (value, metadata, extra) tuples, e.g.
|
|
||||||
Suppose we have the following mapping:
|
|
||||||
MAPPING = {
|
|
||||||
'disk.size.total': {
|
|
||||||
'matching_type': PREFIX,
|
|
||||||
'metric_oid': ("1.3.6.1.4.1.2021.9.1.6", int)
|
|
||||||
'metadata': {
|
|
||||||
'device': ("1.3.6.1.4.1.2021.9.1.3", str),
|
|
||||||
'path': ("1.3.6.1.4.1.2021.9.1.2", str),
|
|
||||||
},
|
|
||||||
'post_op': None,
|
|
||||||
},
|
|
||||||
and the SNMP have the following oid/value(s):
|
|
||||||
{
|
|
||||||
'1.3.6.1.4.1.2021.9.1.6.1': 19222656,
|
|
||||||
'1.3.6.1.4.1.2021.9.1.3.1': "/dev/sda2",
|
|
||||||
'1.3.6.1.4.1.2021.9.1.2.1': "/"
|
|
||||||
'1.3.6.1.4.1.2021.9.1.6.2': 808112,
|
|
||||||
'1.3.6.1.4.1.2021.9.1.3.2': "tmpfs",
|
|
||||||
'1.3.6.1.4.1.2021.9.1.2.2': "/run",
|
|
||||||
}
|
|
||||||
So here we'll return 2 instances of (value, metadata, extra):
|
|
||||||
(19222656, {'device': "/dev/sda2", 'path': "/"}, None)
|
|
||||||
(808112, {'device': "tmpfs", 'path': "/run"}, None)
|
|
||||||
|
|
||||||
The post_op is assumed to be implemented by new metric developer. It
|
|
||||||
could be used to add additional special metadata(e.g. ip address), or
|
|
||||||
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)
|
|
||||||
"""
|
|
||||||
|
|
||||||
def _query_oids(self, host, oids, cache, is_bulk):
|
|
||||||
# send GetRequest or GetBulkRequest to get oids values and
|
|
||||||
# populate the values into cache
|
|
||||||
authData = self._get_auth_strategy(host)
|
|
||||||
transport = cmdgen.UdpTransportTarget((host.hostname,
|
|
||||||
host.port or self._port))
|
|
||||||
oid_cache = cache.setdefault(self._CACHE_KEY_OID, {})
|
|
||||||
|
|
||||||
cmd_runner = cmdgen.CommandGenerator()
|
|
||||||
if is_bulk:
|
|
||||||
ret = cmd_runner.bulkCmd(authData, transport, 0, 100, *oids,
|
|
||||||
lookupValues=True)
|
|
||||||
else:
|
|
||||||
ret = cmd_runner.getCmd(authData, transport, *oids,
|
|
||||||
lookupValues=True)
|
|
||||||
(error, data) = parse_snmp_return(ret, is_bulk)
|
|
||||||
if error:
|
|
||||||
raise SNMPException("An error occurred, oids %(oid)s, "
|
|
||||||
"host %(host)s, %(err)s" %
|
|
||||||
dict(oid=oids,
|
|
||||||
host=host.hostname,
|
|
||||||
err=data))
|
|
||||||
# save result into cache
|
|
||||||
if is_bulk:
|
|
||||||
for var_bind_table_row in data:
|
|
||||||
for name, val in var_bind_table_row:
|
|
||||||
oid_cache[str(name)] = val
|
|
||||||
else:
|
|
||||||
for name, val in data:
|
|
||||||
oid_cache[str(name)] = val
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def find_matching_oids(oid_cache, oid, match_type, find_one=True):
|
|
||||||
matched = []
|
|
||||||
if match_type == PREFIX:
|
|
||||||
for key in oid_cache.keys():
|
|
||||||
if key.startswith(oid):
|
|
||||||
matched.append(key)
|
|
||||||
if find_one:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if oid in oid_cache:
|
|
||||||
matched.append(oid)
|
|
||||||
return matched
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_oid_value(oid_cache, oid_def, suffix='', host=None):
|
|
||||||
oid, converter = oid_def
|
|
||||||
value = oid_cache[oid + suffix]
|
|
||||||
if isinstance(value, (rfc1905.NoSuchObject, rfc1905.NoSuchInstance)):
|
|
||||||
LOG.debug("OID %s%s has no value" % (
|
|
||||||
oid, " on %s" % host.hostname if host else ""))
|
|
||||||
return None
|
|
||||||
if converter:
|
|
||||||
value = converter(value)
|
|
||||||
return value
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def construct_metadata(cls, oid_cache, meta_defs, suffix='', host=None):
|
|
||||||
metadata = {}
|
|
||||||
for key, oid_def in meta_defs.items():
|
|
||||||
metadata[key] = cls.get_oid_value(oid_cache, oid_def, suffix, host)
|
|
||||||
return metadata
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _find_missing_oids(cls, meter_def, cache):
|
|
||||||
# find oids have not been queried and cached
|
|
||||||
new_oids = []
|
|
||||||
oid_cache = cache.setdefault(cls._CACHE_KEY_OID, {})
|
|
||||||
# check metric_oid
|
|
||||||
if not cls.find_matching_oids(oid_cache,
|
|
||||||
meter_def['metric_oid'][0],
|
|
||||||
meter_def['matching_type']):
|
|
||||||
new_oids.append(meter_def['metric_oid'][0])
|
|
||||||
for metadata in meter_def['metadata'].values():
|
|
||||||
if not cls.find_matching_oids(oid_cache,
|
|
||||||
metadata[0],
|
|
||||||
meter_def['matching_type']):
|
|
||||||
new_oids.append(metadata[0])
|
|
||||||
return new_oids
|
|
||||||
|
|
||||||
def inspect_generic(self, host, cache, extra_metadata, param):
|
|
||||||
# the snmp definition for the corresponding meter
|
|
||||||
meter_def = param
|
|
||||||
# collect oids that needs to be queried
|
|
||||||
oids_to_query = self._find_missing_oids(meter_def, cache)
|
|
||||||
# query oids and populate into caches
|
|
||||||
if oids_to_query:
|
|
||||||
self._query_oids(host, oids_to_query, cache,
|
|
||||||
meter_def['matching_type'] == PREFIX)
|
|
||||||
# construct (value, metadata, extra)
|
|
||||||
oid_cache = cache[self._CACHE_KEY_OID]
|
|
||||||
# find all oids which needed to construct final sample values
|
|
||||||
# for matching type of EXACT, only 1 sample would be generated
|
|
||||||
# for matching type of PREFIX, multiple samples could be generated
|
|
||||||
oids_for_sample_values = self.find_matching_oids(
|
|
||||||
oid_cache,
|
|
||||||
meter_def['metric_oid'][0],
|
|
||||||
meter_def['matching_type'],
|
|
||||||
False)
|
|
||||||
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,
|
|
||||||
meter_def['metric_oid'],
|
|
||||||
suffix, host)
|
|
||||||
# get the metadata for this sample value
|
|
||||||
metadata = self.construct_metadata(oid_cache,
|
|
||||||
meter_def['metadata'],
|
|
||||||
suffix, host)
|
|
||||||
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)
|
|
||||||
if func:
|
|
||||||
value = func(host, cache, meter_def,
|
|
||||||
value, metadata, extra_metadata,
|
|
||||||
suffix)
|
|
||||||
yield (value, metadata, extra_metadata)
|
|
||||||
|
|
||||||
def _post_op_memory_avail_to_used(self, host, cache, meter_def,
|
|
||||||
value, metadata, extra, suffix):
|
|
||||||
_memory_total_oid = "1.3.6.1.4.1.2021.4.5.0"
|
|
||||||
if _memory_total_oid not in cache[self._CACHE_KEY_OID]:
|
|
||||||
self._query_oids(host, [_memory_total_oid], cache, False)
|
|
||||||
|
|
||||||
total_value = self.get_oid_value(cache[self._CACHE_KEY_OID],
|
|
||||||
(_memory_total_oid, int))
|
|
||||||
if total_value is None:
|
|
||||||
return None
|
|
||||||
return total_value - value
|
|
||||||
|
|
||||||
def _post_op_net(self, host, cache, meter_def,
|
|
||||||
value, metadata, extra, suffix):
|
|
||||||
# add ip address into metadata
|
|
||||||
_interface_ip_oid = "1.3.6.1.2.1.4.20.1.2"
|
|
||||||
oid_cache = cache.setdefault(self._CACHE_KEY_OID, {})
|
|
||||||
if not self.find_matching_oids(oid_cache,
|
|
||||||
_interface_ip_oid,
|
|
||||||
PREFIX):
|
|
||||||
# populate the oid into cache
|
|
||||||
self._query_oids(host, [_interface_ip_oid], cache, True)
|
|
||||||
ip_addr = ''
|
|
||||||
for k, v in oid_cache.items():
|
|
||||||
if k.startswith(_interface_ip_oid) and v == int(suffix[1:]):
|
|
||||||
ip_addr = k.replace(_interface_ip_oid + ".", "")
|
|
||||||
metadata.update(ip=ip_addr)
|
|
||||||
# update resource_id for each nic interface
|
|
||||||
self._suffix_resource_id(host, metadata, 'name', extra)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def _post_op_disk(self, host, cache, meter_def,
|
|
||||||
value, metadata, extra, suffix):
|
|
||||||
self._suffix_resource_id(host, metadata, 'device', extra)
|
|
||||||
return value
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _suffix_resource_id(host, metadata, key, extra):
|
|
||||||
prefix = metadata.get(key)
|
|
||||||
if prefix:
|
|
||||||
res_id = extra.get('resource_id') or host.hostname
|
|
||||||
res_id = res_id + ".%s" % metadata.get(key)
|
|
||||||
extra.update(resource_id=res_id)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_auth_strategy(host):
|
|
||||||
options = urlparse.parse_qs(host.query)
|
|
||||||
kwargs = {}
|
|
||||||
|
|
||||||
for key in _usm_proto_mapping:
|
|
||||||
opt = options.get(key, [None])[-1]
|
|
||||||
value = _usm_proto_mapping[key][1].get(opt)
|
|
||||||
if value:
|
|
||||||
kwargs[_usm_proto_mapping[key][0]] = value
|
|
||||||
|
|
||||||
priv_pass = options.get('priv_password', [None])[-1]
|
|
||||||
if priv_pass:
|
|
||||||
kwargs['privKey'] = priv_pass
|
|
||||||
if host.password:
|
|
||||||
kwargs['authKey'] = host.password
|
|
||||||
|
|
||||||
if kwargs:
|
|
||||||
auth_strategy = cmdgen.UsmUserData(host.username,
|
|
||||||
**kwargs)
|
|
||||||
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 param.get('metadata', {}).items():
|
|
||||||
processed['metadata'][k] = (v['oid'], eval(v['type']))
|
|
||||||
return processed
|
|
@ -1,287 +0,0 @@
|
|||||||
---
|
|
||||||
# see http://www.circitor.fr/Mibs/Html/U/UCD-SNMP-MIB.php for reference.
|
|
||||||
# http://www.circitor.fr/Mibs/Html/U/UCD-DISKIO-MIB.php for disk metrics
|
|
||||||
|
|
||||||
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))"
|
|
||||||
|
|
||||||
# hardware.cpu.util is deprecated
|
|
||||||
- name: hardware.cpu.util
|
|
||||||
unit: "%"
|
|
||||||
type: gauge
|
|
||||||
snmp_inspector:
|
|
||||||
matching_type: "type_exact"
|
|
||||||
oid: "1.3.6.1.4.1.2021.11.9.0"
|
|
||||||
type: "int"
|
|
||||||
|
|
||||||
- name: hardware.cpu.user
|
|
||||||
unit: tick
|
|
||||||
type: gauge
|
|
||||||
snmp_inspector:
|
|
||||||
matching_type: "type_exact"
|
|
||||||
oid: "1.3.6.1.4.1.2021.11.50.0"
|
|
||||||
type: "int"
|
|
||||||
- name: hardware.cpu.nice
|
|
||||||
unit: tick
|
|
||||||
type: gauge
|
|
||||||
snmp_inspector:
|
|
||||||
matching_type: "type_exact"
|
|
||||||
oid: "1.3.6.1.4.1.2021.11.51.0"
|
|
||||||
type: "int"
|
|
||||||
- name: hardware.cpu.system
|
|
||||||
unit: tick
|
|
||||||
type: gauge
|
|
||||||
snmp_inspector:
|
|
||||||
matching_type: "type_exact"
|
|
||||||
oid: "1.3.6.1.4.1.2021.11.52.0"
|
|
||||||
type: "int"
|
|
||||||
- name: hardware.cpu.idle
|
|
||||||
unit: tick
|
|
||||||
type: gauge
|
|
||||||
snmp_inspector:
|
|
||||||
matching_type: "type_exact"
|
|
||||||
oid: "1.3.6.1.4.1.2021.11.53.0"
|
|
||||||
type: "int"
|
|
||||||
- name: hardware.cpu.wait
|
|
||||||
unit: tick
|
|
||||||
type: gauge
|
|
||||||
snmp_inspector:
|
|
||||||
matching_type: "type_exact"
|
|
||||||
oid: "1.3.6.1.4.1.2021.11.54.0"
|
|
||||||
type: "int"
|
|
||||||
- name: hardware.cpu.kernel
|
|
||||||
unit: tick
|
|
||||||
type: gauge
|
|
||||||
snmp_inspector:
|
|
||||||
matching_type: "type_exact"
|
|
||||||
oid: "1.3.6.1.4.1.2021.11.55.0"
|
|
||||||
type: "int"
|
|
||||||
- name: hardware.cpu.interrupt
|
|
||||||
unit: tick
|
|
||||||
type: gauge
|
|
||||||
snmp_inspector:
|
|
||||||
matching_type: "type_exact"
|
|
||||||
oid: "1.3.6.1.4.1.2021.11.56.0"
|
|
||||||
type: "int"
|
|
||||||
|
|
||||||
# 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"
|
|
||||||
|
|
||||||
- name: hardware.disk.read.bytes
|
|
||||||
unit: B
|
|
||||||
type: gauge
|
|
||||||
snmp_inspector:
|
|
||||||
matching_type: "type_prefix"
|
|
||||||
oid: "1.3.6.1.4.1.2021.13.15.1.1.3"
|
|
||||||
type: "int"
|
|
||||||
metadata: &diskio_metadata
|
|
||||||
device:
|
|
||||||
oid: "1.3.6.1.4.1.2021.13.15.1.1.2"
|
|
||||||
post_op: "_post_op_disk"
|
|
||||||
|
|
||||||
- name: hardware.disk.write.bytes
|
|
||||||
unit: B
|
|
||||||
type: gauge
|
|
||||||
snmp_inspector:
|
|
||||||
matching_type: "type_prefix"
|
|
||||||
oid: "1.3.6.1.4.1.2021.13.15.1.1.4"
|
|
||||||
type: "int"
|
|
||||||
<<: *diskio_metadata
|
|
||||||
post_op: "_post_op_disk"
|
|
||||||
|
|
||||||
- name: hardware.disk.read.requests
|
|
||||||
unit: requests
|
|
||||||
type: gauge
|
|
||||||
snmp_inspector:
|
|
||||||
matching_type: "type_prefix"
|
|
||||||
oid: "1.3.6.1.4.1.2021.13.15.1.1.5"
|
|
||||||
type: "int"
|
|
||||||
<<: *diskio_metadata
|
|
||||||
post_op: "_post_op_disk"
|
|
||||||
|
|
||||||
- name: hardware.disk.write.requests
|
|
||||||
unit: requests
|
|
||||||
type: gauge
|
|
||||||
snmp_inspector:
|
|
||||||
matching_type: "type_prefix"
|
|
||||||
oid: "1.3.6.1.4.1.2021.13.15.1.1.6"
|
|
||||||
type: "int"
|
|
||||||
<<: *diskio_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
|
|
||||||
# hardware.system_stats.cpu.idle is deprecated
|
|
||||||
- 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"
|
|
@ -1,225 +0,0 @@
|
|||||||
#
|
|
||||||
# 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 pkg_resources
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
from oslo_log import log
|
|
||||||
from oslo_utils import netutils
|
|
||||||
|
|
||||||
from ceilometer import declarative
|
|
||||||
from ceilometer.hardware import inspector as insloader
|
|
||||||
from ceilometer.hardware.pollsters import util
|
|
||||||
from ceilometer.i18n import _
|
|
||||||
from ceilometer.polling import plugin_base
|
|
||||||
from ceilometer import sample
|
|
||||||
|
|
||||||
OPTS = [
|
|
||||||
cfg.StrOpt('meter_definitions_file',
|
|
||||||
default="snmp.yaml",
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
help="Configuration file for defining hardware snmp meters."
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
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, str) and
|
|
||||||
(fname in self.required_fields or
|
|
||||||
fname.endswith('_inspector'))):
|
|
||||||
setattr(self, fname, fval)
|
|
||||||
else:
|
|
||||||
LOG.warning("Ignore unrecognized field %s", fname)
|
|
||||||
for fname in self.required_fields:
|
|
||||||
if not getattr(self, fname, None):
|
|
||||||
raise declarative.MeterDefinitionException(
|
|
||||||
_("Missing field %s") % fname, self.cfg)
|
|
||||||
if self.type not in sample.TYPES:
|
|
||||||
raise declarative.MeterDefinitionException(
|
|
||||||
_("Unrecognized type value %s") % self.type, self.cfg)
|
|
||||||
|
|
||||||
|
|
||||||
class GenericHardwareDeclarativePollster(plugin_base.PollsterBase):
|
|
||||||
CACHE_KEY = 'hardware.generic'
|
|
||||||
mapping = None
|
|
||||||
|
|
||||||
def __init__(self, conf):
|
|
||||||
super(GenericHardwareDeclarativePollster, self).__init__(conf)
|
|
||||||
self.inspectors = {}
|
|
||||||
|
|
||||||
warnings.warn('GenericHardwareDeclarativePollster has been deprecated '
|
|
||||||
'and will be removed in a future release.',
|
|
||||||
category=DeprecationWarning, stacklevel=3)
|
|
||||||
|
|
||||||
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('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("Cannot load inspector %(name)s: %(err)s",
|
|
||||||
dict(name=parsed_url.scheme,
|
|
||||||
err=err))
|
|
||||||
raise
|
|
||||||
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("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(
|
|
||||||
host=parsed_url,
|
|
||||||
cache=i_cache,
|
|
||||||
extra_metadata=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:
|
|
||||||
msg = ('inspector call failed for %(ident)s '
|
|
||||||
'host %(host)s: %(err)s' %
|
|
||||||
dict(ident=identifier,
|
|
||||||
host=parsed_url.hostname,
|
|
||||||
err=err))
|
|
||||||
if "timeout" in str(err):
|
|
||||||
LOG.warning(msg)
|
|
||||||
else:
|
|
||||||
LOG.exception(msg)
|
|
||||||
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, conf):
|
|
||||||
if not cls.mapping:
|
|
||||||
definition_cfg = declarative.load_definitions(
|
|
||||||
conf, {}, conf.hardware.meter_definitions_file,
|
|
||||||
pkg_resources.resource_filename(__name__, "data/snmp.yaml"))
|
|
||||||
cls.mapping = load_definition(definition_cfg)
|
|
||||||
|
|
||||||
pollsters = []
|
|
||||||
for name in cls.mapping:
|
|
||||||
pollster = cls(conf)
|
|
||||||
pollster._update_meter_definition(cls.mapping[name])
|
|
||||||
pollsters.append((name, pollster))
|
|
||||||
return pollsters
|
|
||||||
|
|
||||||
|
|
||||||
def load_definition(config_def):
|
|
||||||
mappings = {}
|
|
||||||
for meter_def in config_def.get('metric', []):
|
|
||||||
try:
|
|
||||||
meter = MeterDefinition(meter_def)
|
|
||||||
mappings[meter.name] = meter
|
|
||||||
except declarative.DefinitionException as e:
|
|
||||||
errmsg = "Error loading meter definition: %s"
|
|
||||||
LOG.error(errmsg, e.brief_message)
|
|
||||||
return mappings
|
|
@ -1,59 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright 2013 ZHAW SoE
|
|
||||||
# Copyright 2014 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 copy
|
|
||||||
|
|
||||||
from urllib import parse as urlparse
|
|
||||||
|
|
||||||
from ceilometer import sample
|
|
||||||
|
|
||||||
|
|
||||||
def get_metadata_from_host(host_url):
|
|
||||||
return {'resource_url': urlparse.urlunsplit(host_url)}
|
|
||||||
|
|
||||||
|
|
||||||
def make_resource_metadata(res_metadata=None, host_url=None):
|
|
||||||
resource_metadata = dict()
|
|
||||||
if res_metadata is not None:
|
|
||||||
metadata = copy.copy(res_metadata)
|
|
||||||
resource_metadata.update(metadata)
|
|
||||||
resource_metadata.update(get_metadata_from_host(host_url))
|
|
||||||
return resource_metadata
|
|
||||||
|
|
||||||
|
|
||||||
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,
|
|
||||||
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=name,
|
|
||||||
type=sample_type,
|
|
||||||
unit=unit,
|
|
||||||
volume=volume,
|
|
||||||
user_id=user_id or extra.get('user_id'),
|
|
||||||
project_id=project_id or extra.get('project_id'),
|
|
||||||
resource_id=res_id,
|
|
||||||
resource_metadata=resource_metadata,
|
|
||||||
source='hardware',
|
|
||||||
)
|
|
@ -22,8 +22,6 @@ import ceilometer.compute.virt.inspector
|
|||||||
import ceilometer.compute.virt.libvirt.utils
|
import ceilometer.compute.virt.libvirt.utils
|
||||||
import ceilometer.compute.virt.vmware.inspector
|
import ceilometer.compute.virt.vmware.inspector
|
||||||
import ceilometer.event.converter
|
import ceilometer.event.converter
|
||||||
import ceilometer.hardware.discovery
|
|
||||||
import ceilometer.hardware.pollsters.generic
|
|
||||||
import ceilometer.image.discovery
|
import ceilometer.image.discovery
|
||||||
import ceilometer.ipmi.platform.intel_node_manager
|
import ceilometer.ipmi.platform.intel_node_manager
|
||||||
import ceilometer.ipmi.pollsters
|
import ceilometer.ipmi.pollsters
|
||||||
@ -95,9 +93,6 @@ def list_opts():
|
|||||||
'membership has changed'),
|
'membership has changed'),
|
||||||
]),
|
]),
|
||||||
('event', ceilometer.event.converter.OPTS),
|
('event', ceilometer.event.converter.OPTS),
|
||||||
('hardware', itertools.chain(
|
|
||||||
ceilometer.hardware.discovery.OPTS,
|
|
||||||
ceilometer.hardware.pollsters.generic.OPTS)),
|
|
||||||
('ipmi',
|
('ipmi',
|
||||||
itertools.chain(ceilometer.ipmi.platform.intel_node_manager.OPTS,
|
itertools.chain(ceilometer.ipmi.platform.intel_node_manager.OPTS,
|
||||||
ceilometer.ipmi.pollsters.OPTS)),
|
ceilometer.ipmi.pollsters.OPTS)),
|
||||||
|
@ -275,61 +275,6 @@ resources:
|
|||||||
attributes:
|
attributes:
|
||||||
provider: resource_metadata.provider
|
provider: resource_metadata.provider
|
||||||
|
|
||||||
- resource_type: host
|
|
||||||
metrics:
|
|
||||||
hardware.cpu.load.1min:
|
|
||||||
hardware.cpu.load.5min:
|
|
||||||
hardware.cpu.load.15min:
|
|
||||||
hardware.cpu.util:
|
|
||||||
hardware.cpu.user:
|
|
||||||
archive_policy_name: ceilometer-low-rate
|
|
||||||
hardware.cpu.nice:
|
|
||||||
archive_policy_name: ceilometer-low-rate
|
|
||||||
hardware.cpu.system:
|
|
||||||
archive_policy_name: ceilometer-low-rate
|
|
||||||
hardware.cpu.idle:
|
|
||||||
archive_policy_name: ceilometer-low-rate
|
|
||||||
hardware.cpu.wait:
|
|
||||||
archive_policy_name: ceilometer-low-rate
|
|
||||||
hardware.cpu.kernel:
|
|
||||||
archive_policy_name: ceilometer-low-rate
|
|
||||||
hardware.cpu.interrupt:
|
|
||||||
archive_policy_name: ceilometer-low-rate
|
|
||||||
hardware.memory.total:
|
|
||||||
hardware.memory.used:
|
|
||||||
hardware.memory.swap.total:
|
|
||||||
hardware.memory.swap.avail:
|
|
||||||
hardware.memory.buffer:
|
|
||||||
hardware.memory.cached:
|
|
||||||
hardware.network.ip.outgoing.datagrams:
|
|
||||||
hardware.network.ip.incoming.datagrams:
|
|
||||||
hardware.system_stats.cpu.idle:
|
|
||||||
hardware.system_stats.io.outgoing.blocks:
|
|
||||||
hardware.system_stats.io.incoming.blocks:
|
|
||||||
attributes:
|
|
||||||
host_name: resource_metadata.resource_url
|
|
||||||
|
|
||||||
- resource_type: host_disk
|
|
||||||
metrics:
|
|
||||||
hardware.disk.size.total:
|
|
||||||
hardware.disk.size.used:
|
|
||||||
hardware.disk.read.bytes:
|
|
||||||
hardware.disk.write.bytes:
|
|
||||||
hardware.disk.read.requests:
|
|
||||||
hardware.disk.write.requests:
|
|
||||||
attributes:
|
|
||||||
host_name: resource_metadata.resource_url
|
|
||||||
device_name: resource_metadata.device
|
|
||||||
|
|
||||||
- resource_type: host_network_interface
|
|
||||||
metrics:
|
|
||||||
hardware.network.incoming.bytes:
|
|
||||||
hardware.network.outgoing.bytes:
|
|
||||||
hardware.network.outgoing.errors:
|
|
||||||
attributes:
|
|
||||||
host_name: resource_metadata.resource_url
|
|
||||||
device_name: resource_metadata.name
|
|
||||||
|
|
||||||
- resource_type: nova_compute
|
- resource_type: nova_compute
|
||||||
metrics:
|
metrics:
|
||||||
compute.node.cpu.frequency:
|
compute.node.cpu.frequency:
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright 2014 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.
|
|
||||||
from oslo_utils import netutils
|
|
||||||
|
|
||||||
from ceilometer.hardware import inspector
|
|
||||||
from ceilometer.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class TestHardwareInspector(base.BaseTestCase):
|
|
||||||
def test_get_inspector(self):
|
|
||||||
url = netutils.urlsplit("snmp://")
|
|
||||||
driver = inspector.get_inspector(url)
|
|
||||||
self.assertTrue(driver)
|
|
||||||
|
|
||||||
def test_get_inspector_illegal(self):
|
|
||||||
url = netutils.urlsplit("illegal://")
|
|
||||||
self.assertRaises(RuntimeError,
|
|
||||||
inspector.get_inspector,
|
|
||||||
url)
|
|
@ -1,271 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright 2013 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.
|
|
||||||
"""Tests for ceilometer/hardware/inspector/snmp/inspector.py"""
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
import fixtures
|
|
||||||
from oslo_utils import netutils
|
|
||||||
from pysnmp.proto import rfc1905
|
|
||||||
|
|
||||||
from ceilometer.hardware.inspector import snmp
|
|
||||||
from ceilometer.tests import base as test_base
|
|
||||||
|
|
||||||
ins = snmp.SNMPInspector
|
|
||||||
|
|
||||||
|
|
||||||
class FakeObjectName(object):
|
|
||||||
def __init__(self, name):
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return str(self.name)
|
|
||||||
|
|
||||||
|
|
||||||
class FakeCommandGenerator(object):
|
|
||||||
def getCmd(self, authData, transportTarget, *oids, **kwargs):
|
|
||||||
emptyOIDs = {
|
|
||||||
'1.3.6.1.4.1.2021.4.14.0': rfc1905.noSuchObject,
|
|
||||||
'1.3.6.1.4.1.2021.4.14.1': rfc1905.noSuchInstance,
|
|
||||||
}
|
|
||||||
varBinds = [
|
|
||||||
(FakeObjectName(oid), int(oid.split('.')[-1]))
|
|
||||||
for oid in oids
|
|
||||||
if oid not in emptyOIDs
|
|
||||||
]
|
|
||||||
for emptyOID, exc in emptyOIDs.items():
|
|
||||||
if emptyOID in oids:
|
|
||||||
varBinds += [(FakeObjectName(emptyOID), exc)]
|
|
||||||
return (None, None, 0, varBinds)
|
|
||||||
|
|
||||||
def bulkCmd(authData, transportTarget, nonRepeaters, maxRepetitions,
|
|
||||||
*oids, **kwargs):
|
|
||||||
varBindTable = [
|
|
||||||
[(FakeObjectName("%s.%d" % (oid, i)), i) for i in range(1, 3)]
|
|
||||||
for oid in oids
|
|
||||||
]
|
|
||||||
return (None, None, 0, varBindTable)
|
|
||||||
|
|
||||||
|
|
||||||
class TestSNMPInspector(test_base.BaseTestCase):
|
|
||||||
mapping = {
|
|
||||||
'test_exact': {
|
|
||||||
'matching_type': snmp.EXACT,
|
|
||||||
'metric_oid': ('1.3.6.1.4.1.2021.10.1.3.1', int),
|
|
||||||
'metadata': {
|
|
||||||
'meta': ('1.3.6.1.4.1.2021.10.1.3.8', int)
|
|
||||||
},
|
|
||||||
'post_op': '_fake_post_op',
|
|
||||||
},
|
|
||||||
'test_prefix': {
|
|
||||||
'matching_type': snmp.PREFIX,
|
|
||||||
'metric_oid': ('1.3.6.1.4.1.2021.9.1.8', int),
|
|
||||||
'metadata': {
|
|
||||||
'meta': ('1.3.6.1.4.1.2021.9.1.3', int)
|
|
||||||
},
|
|
||||||
'post_op': None,
|
|
||||||
},
|
|
||||||
'test_nosuch': {
|
|
||||||
'matching_type': snmp.EXACT,
|
|
||||||
'metric_oid': ('1.3.6.1.4.1.2021.4.14.0', int),
|
|
||||||
'metadata': {},
|
|
||||||
'post_op': None,
|
|
||||||
},
|
|
||||||
'test_nosuch_instance': {
|
|
||||||
'matching_type': snmp.EXACT,
|
|
||||||
'metric_oid': ('1.3.6.1.4.1.2021.4.14.1', int),
|
|
||||||
'metadata': {},
|
|
||||||
'post_op': None,
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestSNMPInspector, self).setUp()
|
|
||||||
self.inspector = snmp.SNMPInspector()
|
|
||||||
self.host = netutils.urlsplit("snmp://localhost")
|
|
||||||
self.useFixture(fixtures.MockPatchObject(
|
|
||||||
snmp.cmdgen, 'CommandGenerator',
|
|
||||||
return_value=FakeCommandGenerator()))
|
|
||||||
|
|
||||||
def test_snmp_error(self):
|
|
||||||
def get_list(func, *args, **kwargs):
|
|
||||||
return list(func(*args, **kwargs))
|
|
||||||
|
|
||||||
def faux_parse(ret, is_bulk):
|
|
||||||
return (True, 'forced error')
|
|
||||||
|
|
||||||
self.useFixture(fixtures.MockPatchObject(
|
|
||||||
snmp, 'parse_snmp_return', new=faux_parse))
|
|
||||||
|
|
||||||
self.assertRaises(snmp.SNMPException,
|
|
||||||
get_list,
|
|
||||||
self.inspector.inspect_generic,
|
|
||||||
host=self.host,
|
|
||||||
cache={},
|
|
||||||
extra_metadata={},
|
|
||||||
param=self.mapping['test_exact'])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _fake_post_op(host, cache, meter_def, value, metadata, extra, suffix):
|
|
||||||
metadata.update(post_op_meta=4)
|
|
||||||
extra.update(project_id=2)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def test_inspect_no_such_object(self):
|
|
||||||
cache = {}
|
|
||||||
try:
|
|
||||||
# inspect_generic() is a generator, so we explicitly need to
|
|
||||||
# iterate through it in order to trigger the exception.
|
|
||||||
list(self.inspector.inspect_generic(self.host,
|
|
||||||
cache,
|
|
||||||
{},
|
|
||||||
self.mapping['test_nosuch']))
|
|
||||||
except ValueError:
|
|
||||||
self.fail("got ValueError when interpreting NoSuchObject return")
|
|
||||||
|
|
||||||
def test_inspect_no_such_instance(self):
|
|
||||||
cache = {}
|
|
||||||
try:
|
|
||||||
# inspect_generic() is a generator, so we explicitly need to
|
|
||||||
# iterate through it in order to trigger the exception.
|
|
||||||
list(self.inspector.inspect_generic(self.host,
|
|
||||||
cache,
|
|
||||||
{},
|
|
||||||
self.mapping['test_nosuch']))
|
|
||||||
except ValueError:
|
|
||||||
self.fail("got ValueError when interpreting NoSuchInstance return")
|
|
||||||
|
|
||||||
def test_inspect_generic_exact(self):
|
|
||||||
self.inspector._fake_post_op = self._fake_post_op
|
|
||||||
cache = {}
|
|
||||||
ret = list(self.inspector.inspect_generic(self.host,
|
|
||||||
cache,
|
|
||||||
{},
|
|
||||||
self.mapping['test_exact']))
|
|
||||||
keys = cache[ins._CACHE_KEY_OID].keys()
|
|
||||||
self.assertIn('1.3.6.1.4.1.2021.10.1.3.1', keys)
|
|
||||||
self.assertIn('1.3.6.1.4.1.2021.10.1.3.8', keys)
|
|
||||||
self.assertEqual(1, len(ret))
|
|
||||||
self.assertEqual(1, ret[0][0])
|
|
||||||
self.assertEqual(8, ret[0][1]['meta'])
|
|
||||||
self.assertEqual(4, ret[0][1]['post_op_meta'])
|
|
||||||
self.assertEqual(2, ret[0][2]['project_id'])
|
|
||||||
|
|
||||||
def test_inspect_generic_prefix(self):
|
|
||||||
cache = {}
|
|
||||||
ret = list(self.inspector.inspect_generic(self.host,
|
|
||||||
cache,
|
|
||||||
{},
|
|
||||||
self.mapping['test_prefix']))
|
|
||||||
keys = cache[ins._CACHE_KEY_OID].keys()
|
|
||||||
self.assertIn('1.3.6.1.4.1.2021.9.1.8' + '.1', keys)
|
|
||||||
self.assertIn('1.3.6.1.4.1.2021.9.1.8' + '.2', keys)
|
|
||||||
self.assertIn('1.3.6.1.4.1.2021.9.1.3' + '.1', keys)
|
|
||||||
self.assertIn('1.3.6.1.4.1.2021.9.1.3' + '.2', keys)
|
|
||||||
self.assertEqual(2, len(ret))
|
|
||||||
self.assertIn(ret[0][0], (1, 2))
|
|
||||||
self.assertEqual(ret[0][0], ret[0][1]['meta'])
|
|
||||||
|
|
||||||
def test_post_op_net(self):
|
|
||||||
cache = {}
|
|
||||||
metadata = dict(name='lo',
|
|
||||||
speed=0,
|
|
||||||
mac='ba21e43302fe')
|
|
||||||
extra = {}
|
|
||||||
ret = self.inspector._post_op_net(self.host, cache, None,
|
|
||||||
value=8,
|
|
||||||
metadata=metadata,
|
|
||||||
extra=extra,
|
|
||||||
suffix=".2")
|
|
||||||
self.assertEqual(8, ret)
|
|
||||||
self.assertIn('ip', metadata)
|
|
||||||
self.assertIn("2", metadata['ip'])
|
|
||||||
self.assertIn('resource_id', extra)
|
|
||||||
self.assertEqual("localhost.lo", extra['resource_id'])
|
|
||||||
|
|
||||||
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))
|
|
||||||
|
|
||||||
def test_pysnmp_ver43(self):
|
|
||||||
# Test pysnmp version >=4.3 compatibility of ObjectIdentifier
|
|
||||||
from distutils import version
|
|
||||||
import pysnmp
|
|
||||||
|
|
||||||
has43 = (version.StrictVersion(pysnmp.__version__) >=
|
|
||||||
version.StrictVersion('4.3.0'))
|
|
||||||
oid = '1.3.6.4.1.2021.11.57.0'
|
|
||||||
|
|
||||||
if has43:
|
|
||||||
from pysnmp.entity import engine
|
|
||||||
from pysnmp.smi import rfc1902
|
|
||||||
from pysnmp.smi import view
|
|
||||||
snmp_engine = engine.SnmpEngine()
|
|
||||||
mvc = view.MibViewController(snmp_engine.getMibBuilder())
|
|
||||||
name = rfc1902.ObjectIdentity(oid)
|
|
||||||
name.resolveWithMib(mvc)
|
|
||||||
else:
|
|
||||||
from pysnmp.proto import rfc1902
|
|
||||||
name = rfc1902.ObjectName(oid)
|
|
||||||
|
|
||||||
self.assertEqual(oid, str(name))
|
|
||||||
|
|
||||||
@mock.patch.object(snmp.cmdgen, 'UsmUserData')
|
|
||||||
def test_auth_strategy(self, mock_method):
|
|
||||||
host = ''.join(['snmp://a:b@foo?auth_proto=sha',
|
|
||||||
'&priv_password=pass&priv_proto=aes256'])
|
|
||||||
host = netutils.urlsplit(host)
|
|
||||||
self.inspector._get_auth_strategy(host)
|
|
||||||
mock_method.assert_called_with(
|
|
||||||
'a', authKey='b',
|
|
||||||
authProtocol=snmp.cmdgen.usmHMACSHAAuthProtocol,
|
|
||||||
privProtocol=snmp.cmdgen.usmAesCfb256Protocol,
|
|
||||||
privKey='pass')
|
|
||||||
|
|
||||||
host2 = 'snmp://a:b@foo?&priv_password=pass'
|
|
||||||
host2 = netutils.urlsplit(host2)
|
|
||||||
self.inspector._get_auth_strategy(host2)
|
|
||||||
mock_method.assert_called_with('a', authKey='b', privKey='pass')
|
|
@ -1,193 +0,0 @@
|
|||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
import fixtures
|
|
||||||
from oslo_utils import fileutils
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from ceilometer import declarative
|
|
||||||
from ceilometer.hardware.inspector import base as inspector_base
|
|
||||||
from ceilometer.hardware.pollsters import generic
|
|
||||||
from ceilometer import sample
|
|
||||||
from ceilometer import 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 declarative.MeterDefinitionException as e:
|
|
||||||
self.assertEqual("Missing field unit", e.brief_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 declarative.MeterDefinitionException as e:
|
|
||||||
self.assertEqual("Unrecognized type value invalid",
|
|
||||||
e.brief_message)
|
|
||||||
|
|
||||||
def test_config_missing_unit_field(self):
|
|
||||||
cfg = dict(name='hardware.cpu.user',
|
|
||||||
snmp_inspector={"matching_type": "type_exact",
|
|
||||||
"oid": "1.3.6.1.4.1.2021.11.50.0",
|
|
||||||
"type": "int"})
|
|
||||||
try:
|
|
||||||
generic.MeterDefinition(cfg)
|
|
||||||
except declarative.MeterDefinitionException as e:
|
|
||||||
self.assertEqual("Missing field unit",
|
|
||||||
e.brief_message)
|
|
||||||
|
|
||||||
@mock.patch('ceilometer.hardware.pollsters.generic.LOG')
|
|
||||||
def test_bad_metric_skip(self, LOG):
|
|
||||||
cfg = {'metric': [dict(name='test1',
|
|
||||||
type='gauge',
|
|
||||||
unit='B',
|
|
||||||
snmp_inspector={}),
|
|
||||||
dict(name='test_bad',
|
|
||||||
type='invalid',
|
|
||||||
unit='B',
|
|
||||||
snmp_inspector={}),
|
|
||||||
dict(name='test2',
|
|
||||||
type='gauge',
|
|
||||||
unit='B',
|
|
||||||
snmp_inspector={})]}
|
|
||||||
data = generic.load_definition(cfg)
|
|
||||||
self.assertEqual(2, len(data))
|
|
||||||
LOG.error.assert_called_with(
|
|
||||||
"Error loading meter definition: %s",
|
|
||||||
"Unrecognized type value invalid")
|
|
||||||
|
|
||||||
|
|
||||||
class FakeInspector(inspector_base.Inspector):
|
|
||||||
net_metadata = dict(name='test.teest',
|
|
||||||
mac='001122334455',
|
|
||||||
ip='10.0.0.2',
|
|
||||||
speed=1000)
|
|
||||||
DATA = {
|
|
||||||
'test': (0.99, {}, {}),
|
|
||||||
'test2': (90, net_metadata, {}),
|
|
||||||
}
|
|
||||||
|
|
||||||
def inspect_generic(self, host, cache,
|
|
||||||
extra_metadata=None, param=None):
|
|
||||||
yield self.DATA[host.hostname]
|
|
||||||
|
|
||||||
|
|
||||||
class TestGenericPollsters(test_base.BaseTestCase):
|
|
||||||
@staticmethod
|
|
||||||
def faux_get_inspector(url, namespace=None):
|
|
||||||
return FakeInspector()
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestGenericPollsters, self).setUp()
|
|
||||||
self.conf = service.prepare_service([], [])
|
|
||||||
self.resources = ["snmp://test", "snmp://test2"]
|
|
||||||
self.useFixture(fixtures.MockPatch(
|
|
||||||
'ceilometer.hardware.inspector.get_inspector',
|
|
||||||
self.faux_get_inspector))
|
|
||||||
self.pollster = generic.GenericHardwareDeclarativePollster(self.conf)
|
|
||||||
|
|
||||||
def _setup_meter_def_file(self, cfg):
|
|
||||||
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 = declarative.load_definitions(
|
|
||||||
self.conf, {}, self.conf.hardware.meter_definitions_file)
|
|
||||||
return cfg
|
|
||||||
|
|
||||||
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
|
|
||||||
# Clear cached mapping
|
|
||||||
pollster.mapping = None
|
|
||||||
exts = pollster.get_pollsters_extensions(self.conf)
|
|
||||||
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'])
|
|
@ -1,59 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright 2013 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.
|
|
||||||
|
|
||||||
from oslo_utils import netutils
|
|
||||||
|
|
||||||
from ceilometer.hardware.pollsters import util
|
|
||||||
from ceilometer import sample
|
|
||||||
from ceilometer.tests import base as test_base
|
|
||||||
|
|
||||||
|
|
||||||
class TestPollsterUtils(test_base.BaseTestCase):
|
|
||||||
def setUp(self):
|
|
||||||
super(TestPollsterUtils, self).setUp()
|
|
||||||
self.host_url = netutils.urlsplit("snmp://127.0.0.1:161")
|
|
||||||
|
|
||||||
def test_make_sample(self):
|
|
||||||
s = util.make_sample_from_host(self.host_url,
|
|
||||||
name='test',
|
|
||||||
sample_type=sample.TYPE_GAUGE,
|
|
||||||
unit='B',
|
|
||||||
volume=1,
|
|
||||||
res_metadata={
|
|
||||||
'metakey': 'metaval',
|
|
||||||
})
|
|
||||||
self.assertEqual('127.0.0.1', s.resource_id)
|
|
||||||
self.assertIn('snmp://127.0.0.1:161', s.resource_metadata.values())
|
|
||||||
self.assertIn('metakey', s.resource_metadata.keys())
|
|
||||||
|
|
||||||
def test_make_sample_extra(self):
|
|
||||||
extra = {
|
|
||||||
'project_id': 'project',
|
|
||||||
'resource_id': 'resource'
|
|
||||||
}
|
|
||||||
s = util.make_sample_from_host(self.host_url,
|
|
||||||
name='test',
|
|
||||||
sample_type=sample.TYPE_GAUGE,
|
|
||||||
unit='B',
|
|
||||||
volume=1,
|
|
||||||
extra=extra)
|
|
||||||
self.assertIsNone(s.user_id)
|
|
||||||
self.assertEqual('project', s.project_id)
|
|
||||||
self.assertEqual('resource', s.resource_id)
|
|
||||||
self.assertEqual({'resource_url': 'snmp://127.0.0.1:161',
|
|
||||||
'project_id': 'project',
|
|
||||||
'resource_id':
|
|
||||||
'resource'},
|
|
||||||
s.resource_metadata)
|
|
@ -18,7 +18,6 @@ from unittest import mock
|
|||||||
|
|
||||||
from oslotest import base
|
from oslotest import base
|
||||||
|
|
||||||
from ceilometer.hardware import discovery as hardware
|
|
||||||
from ceilometer.polling.discovery import endpoint
|
from ceilometer.polling.discovery import endpoint
|
||||||
from ceilometer.polling.discovery import localnode
|
from ceilometer.polling.discovery import localnode
|
||||||
from ceilometer.polling.discovery import tenant as project
|
from ceilometer.polling.discovery import tenant as project
|
||||||
@ -146,63 +145,3 @@ class TestProjectDiscovery(base.BaseTestCase):
|
|||||||
result = self.discovery.discover(self.manager)
|
result = self.discovery.discover(self.manager)
|
||||||
self.assertEqual(len(result), 3)
|
self.assertEqual(len(result), 3)
|
||||||
self.assertEqual(self.manager.keystone.projects.list.call_count, 2)
|
self.assertEqual(self.manager.keystone.projects.list.call_count, 2)
|
||||||
|
|
||||||
|
|
||||||
class TestHardwareDiscovery(base.BaseTestCase):
|
|
||||||
class MockInstance(object):
|
|
||||||
addresses = {'ctlplane': [
|
|
||||||
{'addr': '0.0.0.0',
|
|
||||||
'OS-EXT-IPS-MAC:mac_addr': '01-23-45-67-89-ab'}
|
|
||||||
]}
|
|
||||||
id = 'resource_id'
|
|
||||||
image = {'id': 'image_id'}
|
|
||||||
flavor = {'id': 'flavor_id'}
|
|
||||||
|
|
||||||
expected = {
|
|
||||||
'resource_id': 'resource_id',
|
|
||||||
'resource_url': 'snmp://ro_snmp_user:password@0.0.0.0',
|
|
||||||
'mac_addr': '01-23-45-67-89-ab',
|
|
||||||
'image_id': 'image_id',
|
|
||||||
'flavor_id': 'flavor_id',
|
|
||||||
}
|
|
||||||
|
|
||||||
expected_usm = {
|
|
||||||
'resource_id': 'resource_id',
|
|
||||||
'resource_url': ''.join(['snmp://ro_snmp_user:password@0.0.0.0',
|
|
||||||
'?priv_proto=aes192',
|
|
||||||
'&priv_password=priv_pass']),
|
|
||||||
'mac_addr': '01-23-45-67-89-ab',
|
|
||||||
'image_id': 'image_id',
|
|
||||||
'flavor_id': 'flavor_id',
|
|
||||||
}
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestHardwareDiscovery, self).setUp()
|
|
||||||
self.CONF = service.prepare_service([], [])
|
|
||||||
self.discovery = hardware.NodesDiscoveryTripleO(self.CONF)
|
|
||||||
self.discovery.nova_cli = mock.MagicMock()
|
|
||||||
self.manager = mock.MagicMock()
|
|
||||||
|
|
||||||
def test_hardware_discovery(self):
|
|
||||||
self.discovery.nova_cli.instance_get_all.return_value = [
|
|
||||||
self.MockInstance()]
|
|
||||||
resources = self.discovery.discover(self.manager)
|
|
||||||
self.assertEqual(1, len(resources))
|
|
||||||
self.assertEqual(self.expected, resources[0])
|
|
||||||
|
|
||||||
def test_hardware_discovery_without_flavor(self):
|
|
||||||
instance = self.MockInstance()
|
|
||||||
instance.flavor = {}
|
|
||||||
self.discovery.nova_cli.instance_get_all.return_value = [instance]
|
|
||||||
resources = self.discovery.discover(self.manager)
|
|
||||||
self.assertEqual(0, len(resources))
|
|
||||||
|
|
||||||
def test_hardware_discovery_usm(self):
|
|
||||||
self.CONF.set_override('readonly_user_priv_proto', 'aes192',
|
|
||||||
group='hardware')
|
|
||||||
self.CONF.set_override('readonly_user_priv_password', 'priv_pass',
|
|
||||||
group='hardware')
|
|
||||||
self.discovery.nova_cli.instance_get_all.return_value = [
|
|
||||||
self.MockInstance()]
|
|
||||||
resources = self.discovery.discover(self.manager)
|
|
||||||
self.assertEqual(self.expected_usm, resources[0])
|
|
||||||
|
@ -25,7 +25,6 @@ from keystoneauth1 import exceptions as ka_exceptions
|
|||||||
from stevedore import extension
|
from stevedore import extension
|
||||||
|
|
||||||
from ceilometer.compute import discovery as nova_discover
|
from ceilometer.compute import discovery as nova_discover
|
||||||
from ceilometer.hardware import discovery
|
|
||||||
from ceilometer.polling.dynamic_pollster import DynamicPollster
|
from ceilometer.polling.dynamic_pollster import DynamicPollster
|
||||||
from ceilometer.polling.dynamic_pollster import \
|
from ceilometer.polling.dynamic_pollster import \
|
||||||
NonOpenStackApisPollsterDefinition
|
NonOpenStackApisPollsterDefinition
|
||||||
@ -689,48 +688,6 @@ class TestPollingAgent(BaseAgent):
|
|||||||
self.assertFalse(self.PollsterKeystone.samples)
|
self.assertFalse(self.PollsterKeystone.samples)
|
||||||
self.assertFalse(self.notified_samples)
|
self.assertFalse(self.notified_samples)
|
||||||
|
|
||||||
@mock.patch('ceilometer.polling.manager.LOG')
|
|
||||||
@mock.patch('ceilometer.nova_client.LOG')
|
|
||||||
def test_hardware_discover_fail_minimize_logs(self, novalog, baselog):
|
|
||||||
class PollsterHardware(TestPollster):
|
|
||||||
discovery = 'tripleo_overcloud_nodes'
|
|
||||||
|
|
||||||
class PollsterHardwareAnother(TestPollster):
|
|
||||||
discovery = 'tripleo_overcloud_nodes'
|
|
||||||
|
|
||||||
self.mgr.extensions.extend([
|
|
||||||
extension.Extension('testhardware',
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
PollsterHardware(self.CONF), ),
|
|
||||||
extension.Extension('testhardware2',
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
PollsterHardwareAnother(self.CONF), )
|
|
||||||
])
|
|
||||||
ext = extension.Extension('tripleo_overcloud_nodes',
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
discovery.NodesDiscoveryTripleO(self.CONF))
|
|
||||||
self.mgr.discoveries = (extension.ExtensionManager
|
|
||||||
.make_test_instance([ext]))
|
|
||||||
|
|
||||||
poll_cfg = {
|
|
||||||
'sources': [{
|
|
||||||
'name': "test_hardware",
|
|
||||||
'interval': 10,
|
|
||||||
'meters': ['testhardware', 'testhardware2'],
|
|
||||||
'sinks': ['test_sink']}],
|
|
||||||
'sinks': [{
|
|
||||||
'name': 'test_sink',
|
|
||||||
'publishers': ["test"]}]
|
|
||||||
}
|
|
||||||
self.setup_polling(poll_cfg)
|
|
||||||
polling_tasks = self.mgr.setup_polling_tasks()
|
|
||||||
self.mgr.interval_task(list(polling_tasks.values())[0])
|
|
||||||
self.assertEqual(1, novalog.exception.call_count)
|
|
||||||
self.assertFalse(baselog.exception.called)
|
|
||||||
|
|
||||||
@mock.patch('ceilometer.polling.manager.LOG')
|
@mock.patch('ceilometer.polling.manager.LOG')
|
||||||
def test_polling_exception(self, LOG):
|
def test_polling_exception(self, LOG):
|
||||||
source_name = 'test_pollingexception'
|
source_name = 'test_pollingexception'
|
||||||
|
@ -19,9 +19,8 @@ Notifications
|
|||||||
messages from the configured message queue system.
|
messages from the configured message queue system.
|
||||||
|
|
||||||
Polling
|
Polling
|
||||||
Retrieve information directly from the hypervisor or from the host
|
Retrieve information directly from the hypervisor or by using the APIs of
|
||||||
machine using SNMP, or by using the APIs of other OpenStack
|
other OpenStack services.
|
||||||
services.
|
|
||||||
|
|
||||||
Notifications
|
Notifications
|
||||||
=============
|
=============
|
||||||
@ -281,15 +280,13 @@ Central agent
|
|||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
This agent is responsible for polling public REST APIs to retrieve additional
|
This agent is responsible for polling public REST APIs to retrieve additional
|
||||||
information on OpenStack resources not already surfaced via notifications,
|
information on OpenStack resources not already surfaced via notifications.
|
||||||
and also for polling hardware resources over SNMP.
|
|
||||||
|
|
||||||
Some of the services polled with this agent are:
|
Some of the services polled with this agent are:
|
||||||
|
|
||||||
- OpenStack Networking
|
- OpenStack Networking
|
||||||
- OpenStack Object Storage
|
- OpenStack Object Storage
|
||||||
- OpenStack Block Storage
|
- OpenStack Block Storage
|
||||||
- Hardware resources via SNMP
|
|
||||||
|
|
||||||
To install and configure this service use the :ref:`install_rdo`
|
To install and configure this service use the :ref:`install_rdo`
|
||||||
section in the Installation Tutorials and Guides.
|
section in the Installation Tutorials and Guides.
|
||||||
|
@ -398,116 +398,6 @@ meters are recorded from capable platform:
|
|||||||
| | | | | | the system |
|
| | | | | | the system |
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
+---------------------+-------+------+----------+----------+------------------+
|
||||||
|
|
||||||
SNMP based meters
|
|
||||||
~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Telemetry supports gathering SNMP based generic host meters. In order to
|
|
||||||
be able to collect this data you need to run snmpd on each target host.
|
|
||||||
|
|
||||||
The following meters are available about the host machines by using
|
|
||||||
SNMP:
|
|
||||||
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| Name | Type | Unit | Resource | Origin | Note |
|
|
||||||
+=====================+=======+======+==========+==========+==================+
|
|
||||||
| **Meters added in the Mitaka release or earlier** |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.cpu.load.\ | Gauge | proc\| host ID | Pollster | CPU load in the |
|
|
||||||
| 1min | | ess | | | past 1 minute |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.cpu.load.\ | Gauge | proc\| host ID | Pollster | CPU load in the |
|
|
||||||
| 5min | | ess | | | past 5 minutes |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.cpu.load.\ | Gauge | proc\| host ID | Pollster | CPU load in the |
|
|
||||||
| 15min | | ess | | | past 15 minutes |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.disk.size\ | Gauge | KB | disk ID | Pollster | Total disk size |
|
|
||||||
| .total | | | | | |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.disk.size\ | Gauge | KB | disk ID | Pollster | Used disk size |
|
|
||||||
| .used | | | | | |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.memory.to\ | Gauge | KB | host ID | Pollster | Total physical |
|
|
||||||
| tal | | | | | memory size |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.memory.us\ | Gauge | KB | host ID | Pollster | Used physical m\ |
|
|
||||||
| ed | | | | | emory size |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.memory.bu\ | Gauge | KB | host ID | Pollster | Physical memory |
|
|
||||||
| ffer | | | | | buffer size |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.memory.ca\ | Gauge | KB | host ID | Pollster | Cached physical |
|
|
||||||
| ched | | | | | memory size |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.memory.sw\ | Gauge | KB | host ID | Pollster | Total swap space |
|
|
||||||
| ap.total | | | | | size |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.memory.sw\ | Gauge | KB | host ID | Pollster | Available swap |
|
|
||||||
| ap.avail | | | | | space size |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.network.i\ | Cumul\| B | interface| Pollster | Bytes received |
|
|
||||||
| ncoming.bytes | ative | | ID | | by network inte\ |
|
|
||||||
| | | | | | rface |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.network.o\ | Cumul\| B | interface| Pollster | Bytes sent by n\ |
|
|
||||||
| utgoing.bytes | ative | | ID | | etwork interface |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.network.o\ | Cumul\| pack\| interface| Pollster | Sending error o\ |
|
|
||||||
| utgoing.errors | ative | et | ID | | f network inter\ |
|
|
||||||
| | | | | | face |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.network.i\ | Cumul\| data\| host ID | Pollster | Number of recei\ |
|
|
||||||
| p.incoming.datagra\ | ative | grams| | | ved datagrams |
|
|
||||||
| ms | | | | | |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.network.i\ | Cumul\| data\| host ID | Pollster | Number of sent |
|
|
||||||
| p.outgoing.datagra\ | ative | grams| | | datagrams |
|
|
||||||
| ms | | | | | |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.system_st\ | Cumul\| bloc\| host ID | Pollster | Aggregated numb\ |
|
|
||||||
| ats.io.incoming.bl\ | ative | ks | | | er of blocks re\ |
|
|
||||||
| ocks | | | | | ceived to block |
|
|
||||||
| | | | | | device |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.system_st\ | Cumul\| bloc\| host ID | Pollster | Aggregated numb\ |
|
|
||||||
| ats.io.outgoing.bl\ | ative | ks | | | er of blocks se\ |
|
|
||||||
| ocks | | | | | nt to block dev\ |
|
|
||||||
| | | | | | ice |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| **Meters added in the Queens release** |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.disk.read.\| Gauge | B | disk ID | Pollster | Bytes read from |
|
|
||||||
| bytes | | | | | device since boot|
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.disk.write\| Gauge | B | disk ID | Pollster | Bytes written to |
|
|
||||||
| .bytes | | | | | device since boot|
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.disk.read.\| Gauge | requ\| disk ID | Pollster | Read requests to |
|
|
||||||
| requests | | ests | | | device since boot|
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.disk.write\| Gauge | requ\| disk ID | Pollster | Write requests to|
|
|
||||||
| .requests | | ests | | | device since boot|
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| **Meters added in the Stein release** |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.cpu.user | Gauge | tick | host ID | Pollster | CPU user in tick |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.cpu.system | Gauge | tick | host ID | Pollster | CPU system in t\ |
|
|
||||||
| | | | | | ick |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.cpu.nice | Gauge | tick | host ID | Pollster | CPU nice in tick |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.cpu.idle | Gauge | tick | host ID | Pollster | CPU idle in tick |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.cpu.wait | Gauge | tick | host ID | Pollster | CPU wait in tick |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.cpu.kernel | Gauge | tick | host ID | Pollster | CPU kernel in t\ |
|
|
||||||
| | | | | | ick |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
| hardware.cpu.inter\ | Gauge | tick | host ID | Pollster | CPU interrupt i\ |
|
|
||||||
| rupt | | | | | n tick |
|
|
||||||
+---------------------+-------+------+----------+----------+------------------+
|
|
||||||
|
|
||||||
OpenStack Image service
|
OpenStack Image service
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ The Ceilometer project created 2 methods to collect data:
|
|||||||
The first method is supported by the ceilometer-notification agent, which
|
The first method is supported by the ceilometer-notification agent, which
|
||||||
monitors the message queues for notifications. Polling agents can be configured
|
monitors the message queues for notifications. Polling agents can be configured
|
||||||
either to poll the local hypervisor or remote APIs (public REST APIs exposed by
|
either to poll the local hypervisor or remote APIs (public REST APIs exposed by
|
||||||
services and host-level SNMP/IPMI daemons).
|
services and host-level IPMI daemons).
|
||||||
|
|
||||||
Notification Agent: Listening for data
|
Notification Agent: Listening for data
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
``GenericHardwareDeclarativePollster`` has been removed. Because of this
|
||||||
|
removal all metrics gathered by SNMP daemon have been removed as well.
|
||||||
|
|
||||||
|
- |
|
||||||
|
The ``NodesDiscoveryTripleO`` discovery plugin has been removed.
|
@ -23,7 +23,6 @@ oslo.messaging>=10.3.0 # Apache-2.0
|
|||||||
oslo.upgradecheck>=0.1.1 # Apache-2.0
|
oslo.upgradecheck>=0.1.1 # Apache-2.0
|
||||||
oslo.utils>=4.7.0 # Apache-2.0
|
oslo.utils>=4.7.0 # Apache-2.0
|
||||||
oslo.privsep>=1.32.0 # Apache-2.0
|
oslo.privsep>=1.32.0 # Apache-2.0
|
||||||
pysnmp<5.0.0,>=4.2.3 # BSD
|
|
||||||
python-glanceclient>=2.8.0 # Apache-2.0
|
python-glanceclient>=2.8.0 # Apache-2.0
|
||||||
python-keystoneclient>=3.18.0 # Apache-2.0
|
python-keystoneclient>=3.18.0 # Apache-2.0
|
||||||
keystoneauth1>=3.18.0 # Apache-2.0
|
keystoneauth1>=3.18.0 # Apache-2.0
|
||||||
|
@ -60,7 +60,6 @@ ceilometer.discover.central =
|
|||||||
ipsec_connections = ceilometer.network.services.discovery:IPSecConnectionsDiscovery
|
ipsec_connections = ceilometer.network.services.discovery:IPSecConnectionsDiscovery
|
||||||
fw_services = ceilometer.network.services.discovery:FirewallDiscovery
|
fw_services = ceilometer.network.services.discovery:FirewallDiscovery
|
||||||
fw_policy = ceilometer.network.services.discovery:FirewallPolicyDiscovery
|
fw_policy = ceilometer.network.services.discovery:FirewallPolicyDiscovery
|
||||||
tripleo_overcloud_nodes = ceilometer.hardware.discovery:NodesDiscoveryTripleO
|
|
||||||
fip_services = ceilometer.network.services.discovery:FloatingIPDiscovery
|
fip_services = ceilometer.network.services.discovery:FloatingIPDiscovery
|
||||||
images = ceilometer.image.discovery:ImagesDiscovery
|
images = ceilometer.image.discovery:ImagesDiscovery
|
||||||
volumes = ceilometer.volume.discovery:VolumeDiscovery
|
volumes = ceilometer.volume.discovery:VolumeDiscovery
|
||||||
@ -187,17 +186,11 @@ ceilometer.poll.central =
|
|||||||
volume.snapshot.size = ceilometer.volume.cinder:VolumeSnapshotSize
|
volume.snapshot.size = ceilometer.volume.cinder:VolumeSnapshotSize
|
||||||
volume.backup.size = ceilometer.volume.cinder:VolumeBackupSize
|
volume.backup.size = ceilometer.volume.cinder:VolumeBackupSize
|
||||||
|
|
||||||
ceilometer.builder.poll.central =
|
|
||||||
hardware.snmp = ceilometer.hardware.pollsters.generic:GenericHardwareDeclarativePollster
|
|
||||||
|
|
||||||
ceilometer.compute.virt =
|
ceilometer.compute.virt =
|
||||||
libvirt = ceilometer.compute.virt.libvirt.inspector:LibvirtInspector
|
libvirt = ceilometer.compute.virt.libvirt.inspector:LibvirtInspector
|
||||||
hyperv = ceilometer.compute.virt.hyperv.inspector:HyperVInspector
|
hyperv = ceilometer.compute.virt.hyperv.inspector:HyperVInspector
|
||||||
vsphere = ceilometer.compute.virt.vmware.inspector:VsphereInspector
|
vsphere = ceilometer.compute.virt.vmware.inspector:VsphereInspector
|
||||||
|
|
||||||
ceilometer.hardware.inspectors =
|
|
||||||
snmp = ceilometer.hardware.inspector.snmp:SNMPInspector
|
|
||||||
|
|
||||||
ceilometer.sample.publisher =
|
ceilometer.sample.publisher =
|
||||||
test = ceilometer.publisher.test:TestPublisher
|
test = ceilometer.publisher.test:TestPublisher
|
||||||
notifier = ceilometer.publisher.messaging:SampleNotifierPublisher
|
notifier = ceilometer.publisher.messaging:SampleNotifierPublisher
|
||||||
|
Loading…
Reference in New Issue
Block a user