# -*- 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_ceilometer.common.singleton import Singleton from collectd_ceilometer.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), 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): """Get unit for plugin and type""" if 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())