monasca-ceilometer/ceilosca/ceilometer/publisher/monasca_data_filter.py

182 lines
6.3 KiB
Python

#
# Copyright 2015 Hewlett-Packard Company
#
# 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 datetime
from oslo_config import cfg
from oslo_log import log
from oslo_utils import timeutils
import yaml
from ceilometer.i18n import _LI
from ceilometer import sample as sample_util
OPTS = [
cfg.StrOpt('monasca_mappings',
default='/etc/ceilometer/monasca_field_definitions.yaml',
help='Monasca static and dynamic field mappings'),
]
cfg.CONF.register_opts(OPTS, group='monasca')
MULTI_REGION_OPTS = [
cfg.StrOpt('control_plane',
default='None',
help='The name of control plane'),
cfg.StrOpt('cluster',
default='None',
help='The name of cluster'),
cfg.StrOpt('cloud_name',
default='None',
help='The name of cloud')
]
cfg.CONF.register_opts(MULTI_REGION_OPTS)
LOG = log.getLogger(__name__)
class UnableToLoadMappings(Exception):
pass
class NoMappingsFound(Exception):
pass
class MonascaDataFilter(object):
def __init__(self):
self._mapping = {}
self._mapping = self._get_mapping()
def _get_mapping(self):
with open(cfg.CONF.monasca.monasca_mappings, 'r') as f:
try:
return yaml.safe_load(f)
except yaml.YAMLError as exc:
raise UnableToLoadMappings(exc.message)
def _convert_timestamp(self, timestamp):
if isinstance(timestamp, datetime.datetime):
ts = timestamp
else:
ts = timeutils.parse_isotime(timestamp)
tdelta = (ts - datetime.datetime(1970, 1, 1, tzinfo=ts.tzinfo))
# convert timestamp to milli seconds as Monasca expects
return int(tdelta.total_seconds() * 1000)
def _convert_to_sample(self, s):
return sample_util.Sample(
name=s['counter_name'],
type=s['counter_type'],
unit=s['counter_unit'],
volume=s['counter_volume'],
user_id=s['user_id'],
project_id=s['project_id'],
resource_id=s['resource_id'],
timestamp=s['timestamp'],
resource_metadata=s['resource_metadata'],
source=s.get('source')).as_dict()
def get_value_for_nested_dictionary(self, lst, dct):
val = dct
for element in lst:
if isinstance(val, dict) and element in val:
val = val.get(element)
else:
return
return val
def process_sample_for_monasca(self, sample_obj):
if not self._mapping:
raise NoMappingsFound("Unable to process the sample")
dimensions = {}
dimensions['datasource'] = 'ceilometer'
# control_plane, cluster and cloud_name can be None, but we use
# literal 'None' for such case
dimensions['control_plane'] = cfg.CONF.control_plane or 'None'
dimensions['cluster'] = cfg.CONF.cluster or 'None'
dimensions['cloud_name'] = cfg.CONF.cloud_name or 'None'
if isinstance(sample_obj, sample_util.Sample):
sample = sample_obj.as_dict()
elif isinstance(sample_obj, dict):
if 'counter_name' in sample_obj:
sample = self._convert_to_sample(sample_obj)
else:
sample = sample_obj
sample_meta = sample.get('resource_metadata', None)
for dim in self._mapping['dimensions']:
val = sample.get(dim, None)
if val is not None:
dimensions[dim] = val
else:
dimensions[dim] = 'None'
value_meta = {}
meter_name = sample.get('name') or sample.get('counter_name')
if sample_meta:
for meta_key in self._mapping['metadata']['common']:
val = sample_meta.get(meta_key, None)
if val is not None:
value_meta[meta_key] = val
else:
if len(meta_key.split('.')) > 1:
val = self.get_value_for_nested_dictionary(
meta_key.split('.'), sample_meta)
if val is not None:
value_meta[meta_key] = val
else:
value_meta[meta_key] = 'None'
else:
value_meta[meta_key] = 'None'
if meter_name in self._mapping['metadata'].keys():
for meta_key in self._mapping['metadata'][meter_name]:
val = sample_meta.get(meta_key, None)
if val is not None:
value_meta[meta_key] = val
else:
if len(meta_key.split('.')) > 1:
val = self.get_value_for_nested_dictionary(
meta_key.split('.'), sample_meta)
if val is not None:
value_meta[meta_key] = val
else:
value_meta[meta_key] = 'None'
else:
value_meta[meta_key] = 'None'
meter_value = sample.get('volume') or sample.get('counter_volume')
if meter_value is None:
meter_value = 0
metric = dict(
name=meter_name,
timestamp=self._convert_timestamp(sample['timestamp']),
value=meter_value,
dimensions=dimensions,
value_meta=value_meta,
)
LOG.debug(_LI("Generated metric with name %(name)s,"
" timestamp %(timestamp)s, value %(value)s,"
" dimensions %(dimensions)s") %
{'name': metric['name'],
'timestamp': metric['timestamp'],
'value': metric['value'],
'dimensions': metric['dimensions']})
return metric