ceilometer/ceilometer/compute/pollsters/__init__.py

173 lines
6.6 KiB
Python

# Copyright 2014 Mirantis, Inc.
#
# 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 collections
import monotonic
from oslo_log import log
from oslo_utils import timeutils
import ceilometer
from ceilometer.compute.pollsters import util
from ceilometer.compute.virt import inspector as virt_inspector
from ceilometer.polling import plugin_base
from ceilometer import sample
LOG = log.getLogger(__name__)
class NoVolumeException(Exception):
pass
class GenericComputePollster(plugin_base.PollsterBase):
"""This class aims to cache instance statistics data
First polled pollsters that inherit of this will retrieve and cache
stats of an instance, then other pollsters will just build the samples
without queyring the backend anymore.
"""
sample_name = None
sample_unit = ''
sample_type = sample.TYPE_GAUGE
sample_stats_key = None
inspector_method = None
def setup_environment(self):
super(GenericComputePollster, self).setup_environment()
self.inspector = self._get_inspector(self.conf)
@staticmethod
def aggregate_method(stats):
# Don't aggregate anything by default
return stats
@classmethod
def _get_inspector(cls, conf):
# FIXME(sileht): This doesn't looks threadsafe...
try:
inspector = cls._inspector
except AttributeError:
inspector = virt_inspector.get_hypervisor_inspector(conf)
cls._inspector = inspector
return inspector
@property
def default_discovery(self):
return 'local_instances'
def _record_poll_time(self):
"""Method records current time as the poll time.
:return: time in seconds since the last poll time was recorded
"""
current_time = timeutils.utcnow()
duration = None
if hasattr(self, '_last_poll_time'):
duration = timeutils.delta_seconds(self._last_poll_time,
current_time)
self._last_poll_time = current_time
return duration
@staticmethod
def get_additional_metadata(instance, stats):
pass
@staticmethod
def get_resource_id(instance, stats):
return instance.id
def _inspect_cached(self, cache, instance, duration):
cache.setdefault(self.inspector_method, {})
if instance.id not in cache[self.inspector_method]:
result = getattr(self.inspector, self.inspector_method)(
instance, duration)
polled_time = monotonic.monotonic()
# Ensure we don't cache an iterator
if isinstance(result, collections.Iterable):
result = list(result)
else:
result = [result]
cache[self.inspector_method][instance.id] = (polled_time, result)
return cache[self.inspector_method][instance.id]
def _stats_to_sample(self, instance, stats, polled_time):
volume = getattr(stats, self.sample_stats_key)
LOG.debug("%(instance_id)s/%(name)s volume: "
"%(volume)s" % {
'name': self.sample_name,
'instance_id': instance.id,
'volume': (volume if volume is not None
else 'Unavailable')})
if volume is None:
raise NoVolumeException()
return util.make_sample_from_instance(
self.conf,
instance,
name=self.sample_name,
unit=self.sample_unit,
type=self.sample_type,
resource_id=self.get_resource_id(instance, stats),
volume=volume,
additional_metadata=self.get_additional_metadata(
instance, stats),
monotonic_time=polled_time,
)
def get_samples(self, manager, cache, resources):
self._inspection_duration = self._record_poll_time()
for instance in resources:
try:
polled_time, result = self._inspect_cached(
cache, instance, self._inspection_duration)
if not result:
continue
for stats in self.aggregate_method(result):
yield self._stats_to_sample(instance, stats, polled_time)
except NoVolumeException:
# FIXME(sileht): This should be a removed... but I will
# not change the test logic for now
LOG.warning("%(name)s statistic in not available for "
"instance %(instance_id)s" %
{'name': self.sample_name,
'instance_id': instance.id})
except virt_inspector.InstanceNotFoundException as err:
# Instance was deleted while getting samples. Ignore it.
LOG.debug('Exception while getting samples %s', err)
except virt_inspector.InstanceShutOffException as e:
LOG.debug('Instance %(instance_id)s was shut off while '
'getting sample of %(name)s: %(exc)s',
{'instance_id': instance.id,
'name': self.sample_name, 'exc': e})
except virt_inspector.NoDataException as e:
LOG.warning('Cannot inspect data of %(pollster)s for '
'%(instance_id)s, non-fatal reason: %(exc)s',
{'pollster': self.__class__.__name__,
'instance_id': instance.id, 'exc': e})
except ceilometer.NotImplementedError:
# Selected inspector does not implement this pollster.
LOG.debug('%(inspector)s does not provide data for '
'%(pollster)s',
{'inspector': self.inspector.__class__.__name__,
'pollster': self.__class__.__name__})
raise plugin_base.PollsterPermanentError(resources)
except Exception as err:
LOG.error(
'Could not get %(name)s events for %(id)s: %(e)s', {
'name': self.sample_name, 'id': instance.id, 'e': err},
exc_info=True)