collectd-openstack-plugins/collectd_openstack/common/settings.py

227 lines
7.6 KiB
Python

# -*- coding: 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.
"""Ceilometer collectd plugin configuration"""
from __future__ import unicode_literals
from collectd_openstack.common.singleton import Singleton
from collectd_openstack.common.units import UNITS
from collections import namedtuple
import logging
import six
LOGGER = logging.getLogger(__name__)
class BadConfigError(Exception):
"""Configuration exception"""
pass
class CfgParam(namedtuple('CfgParam', ['key', 'default', 'data_type'])):
"""Configuration parameter definition"""
def value(self, data):
"""Convert a string to the parameter type"""
try:
return self.data_type(data)
except (ValueError, TypeError) as exc:
LOGGER.info('Config value exception: %s', six.text_type(exc))
raise BadConfigError(
'Invalid value "%s" for configuration parameter "%s"' % (
data, self.key))
@Singleton
class Config(object):
"""Plugin confguration"""
_configuration = [
CfgParam('BATCH_SIZE', 1, int),
CfgParam('OS_AUTH_URL', None, six.text_type),
CfgParam('OS_IDENTITY_API_VERSION', '3', six.text_type),
CfgParam('CEILOMETER_URL_TYPE', 'internalURL', six.text_type),
CfgParam('CEILOMETER_TIMEOUT', 1000, int),
CfgParam('OS_USERNAME', None, six.text_type),
CfgParam('OS_PASSWORD', None, six.text_type),
CfgParam('OS_TENANT_NAME', None, six.text_type),
CfgParam('VERBOSE', False, bool),
CfgParam('LIBVIRT_METER_ENABLED', False, bool),
# Gnocchi only
CfgParam('DEFAULT_ARCHIVE_POLICY', None, six.text_type),
CfgParam('LIBVIRT_CONN_URI', 'qemu:///system', six.text_type),
]
_config_dict = {cfg.key: cfg for cfg in _configuration}
_config_keys = _config_dict.keys()
def __init__(self):
"""Set the default values"""
# init all parameters to default values
for cfg in self._configuration:
setattr(self, cfg.key, cfg.default)
# dictionary for user-defined units
self._user_units = {}
self._units = UNITS.copy()
self._libvirt_meter = False
# dictionary for user defined severities
self._alarm_severities = {}
def read(self, cfg):
"""Read the collectd configuration
@param cfg configuration provided by collectd
"""
LOGGER.info('Reading the OS plugin configuration')
assert 'MODULE' == cfg.key.upper()
self._read_node(cfg)
# verify the configuration
error = False
for key in self._config_keys:
if getattr(self, key, None) is None:
LOGGER.error('Configuration parameter %s not set.', key)
error = True
if error:
LOGGER.error(
'Collectd plugin will not work properly')
else:
LOGGER.info('Configuration OK')
def unit(self, plugin, pltype, pltype_instance=None):
"""Get unit for plugin and type"""
if plugin == "virt" and pltype == "perf":
unit = self._units.get(
"%s.%s.%s" % (plugin, pltype, pltype_instance))
if unit:
return unit
elif pltype:
unit = self._units.get('%s.%s' % (plugin, pltype))
if unit:
return unit
return self._units.get(plugin, "None")
def alarm_severity(self, meter):
"""Get alarm_severity for a meter."""
# check for an user-defined alarm severity
try:
# check for an user-defined alarm severity
return self._alarm_severities[meter]
except KeyError as ke:
LOGGER.info(ke)
LOGGER.info('There is no user-defined severity for this alarm')
return 'moderate'
def libvirt_enabled(self):
"""Check if the libvirt meter is enabled"""
return self._libvirt_meter
def _read_node(self, node):
"""Read a configuration node
@param node collectd configuration node
"""
key = node.key.upper()
# if the node is 'UNITS' call the units function
if key == 'UNITS':
self._read_units(node.children)
return
# if the node is 'ALARM_SEVERITIES' call the alarm severities function
if key == 'ALARM_SEVERITIES':
self._read_alarm_severities(node.children)
return
# if we have a root node read only all its children
# as we don't expect any value here
if node.children:
for child in node.children:
self._read_node(child)
return
# if the configuration key is known
if key in self._config_keys:
try:
# read, normalize and check the value
val = self._config_dict[key].value(node.values[0])
# and store it as a attribute of current instance
setattr(self, key, val)
except (IndexError, TypeError):
# the variable node.value is an empty list
# or it is not a list at all
LOGGER.error('No configuration value found for "%s"', key)
return
except BadConfigError as exc:
LOGGER.error(six.text_type(exc))
return
# do not show the password in the logs
if key == 'OS_PASSWORD':
val = '*****'
LOGGER.info(
'Got configuration parameter: %s -> "%s"', key, val)
if key == 'LIBVIRT_METER_ENABLED':
self._libvirt_meter = val
else:
LOGGER.error('Unknown configuration parameter "%s"', key)
def _read_units(self, nodes):
"""Read user-defined units
@param node collectd configuration nodes
"""
for node in nodes:
if node.key.upper() == 'UNIT':
if len(node.values) == 2:
key, val = node.values
self._user_units[key] = val
LOGGER.info(
'Got user defined unit "%s" for "%s"', val, key)
else:
LOGGER.error(
'Invalid unit configuration: unit %s' % ' '.join(
['"%s"' % i for i in node.values]))
else:
LOGGER.error(
'Invalid unit configuration: %s', node.key.upper())
self._units.update(self._user_units)
def _read_alarm_severities(self, nodes):
"""Read in any user-defined severity settings for alarms."""
for node in nodes:
if node.key.upper() == 'ALARM_SEVERITY':
if len(node.values) == 2:
key, val = node.values
self._alarm_severities[key] = val
else:
LOGGER.error(
'Invalid alarm severity configuration:\
severity %s' % ' '.join(
['"%s"' % i for i in node.values]))
else:
LOGGER.error(
'Invalid alarm severity configuration: %s',
node.key.upper())