Provide a way to disable some plugins

Provide configuration options for the three plugin sets
users may want to manage (compute pollsters, central
pollsters, and notification listeners).

Extend the plugin API so the extension manager can ask
each plugin if it should be enabled. This allows, for
example, the libvirt pollster to be loaded but then
recognize that it should not be used and disable itself.

Addresses bug #1021350

Change-Id: I82da823845ec49c1a93272411c43073bd4954377
Signed-off-by: Doug Hellmann <doug.hellmann@dreamhost.com>
This commit is contained in:
Doug Hellmann 2012-10-29 11:41:53 -04:00
parent ae64a792f8
commit 05d6992ad7
8 changed files with 207 additions and 84 deletions

View File

@ -16,14 +16,21 @@
# License for the specific language governing permissions and limitations
# under the License.
from stevedore import extension
from nova import manager
from ceilometer import extension_manager
from ceilometer.openstack.common import cfg
from ceilometer.openstack.common import log
from ceilometer import publish
OPTS = [
cfg.ListOpt('disabled_central_pollsters',
default=[],
help='list of central pollsters to disable',
),
]
cfg.CONF.register_opts(OPTS)
LOG = log.getLogger(__name__)
@ -38,9 +45,9 @@ class AgentManager(manager.Manager):
# importable. Need to add check against global
# configuration flag and check that asks the plugin if
# it should be enabled.
self.ext_manager = extension.ExtensionManager(
self.ext_manager = extension_manager.ActivatedExtensionManager(
namespace=PLUGIN_NAMESPACE,
invoke_on_load=True,
disabled_names=cfg.CONF.disabled_central_pollsters,
)
return

View File

@ -20,6 +20,7 @@ from stevedore import extension
from nova import manager
from ceilometer import extension_manager
from ceilometer import meter
from ceilometer import publish
from ceilometer import storage
@ -36,6 +37,16 @@ except ImportError:
import nova.rpc as rpc
OPTS = [
cfg.ListOpt('disabled_notification_listeners',
default=[],
help='list of listener plugins to disable',
),
]
cfg.CONF.register_opts(OPTS)
LOG = log.getLogger(__name__)
@ -52,9 +63,10 @@ class CollectorManager(manager.Manager):
self.storage_engine = storage.get_engine(cfg.CONF)
self.storage_conn = self.storage_engine.get_connection(cfg.CONF)
self.ext_manager = extension.ExtensionManager(self.COLLECTOR_NAMESPACE,
invoke_on_load=True,
)
self.ext_manager = extension_manager.ActivatedExtensionManager(
namespace=self.COLLECTOR_NAMESPACE,
disabled_names=cfg.CONF.disabled_notification_listeners,
)
if not list(self.ext_manager):
LOG.warning('Failed to load any notification handlers for %s',
@ -74,7 +86,8 @@ class CollectorManager(manager.Manager):
def _setup_subscription(self, ext, *args, **kwds):
handler = ext.obj
LOG.debug('Event types: %r', handler.get_event_types())
LOG.debug('Event types from %s: %s',
ext.name, ', '.join(handler.get_event_types()))
for exchange_topic in handler.get_exchange_topics(cfg.CONF):
for topic in exchange_topic.topics:
# FIXME(dhellmann): Should be using create_worker(), except

View File

@ -59,7 +59,14 @@ def make_counter_from_instance(instance, name, type, volume):
)
class InstancePollster(plugin.ComputePollster):
class LibVirtPollster(plugin.ComputePollster):
def is_enabled(self):
# Use a fairly liberal substring check.
return 'libvirt' in FLAGS.compute_driver.lower()
class InstancePollster(LibVirtPollster):
def get_counters(self, manager, instance):
yield make_counter_from_instance(instance,
@ -75,7 +82,7 @@ class InstancePollster(plugin.ComputePollster):
)
class DiskIOPollster(plugin.ComputePollster):
class DiskIOPollster(LibVirtPollster):
LOG = log.getLogger(__name__ + '.diskio')
@ -98,53 +105,52 @@ class DiskIOPollster(plugin.ComputePollster):
])
def get_counters(self, manager, instance):
if FLAGS.compute_driver == 'libvirt.LibvirtDriver':
conn = get_libvirt_connection()
# TODO(jd) This does not work see bug#998089
# for disk in conn.get_disks(instance.name):
try:
disks = self._get_disks(conn, instance.name)
except Exception as err:
self.LOG.warning('Ignoring instance %s: %s',
instance.name, err)
self.LOG.exception(err)
else:
r_bytes = 0
r_requests = 0
w_bytes = 0
w_requests = 0
for disk in disks:
stats = conn.block_stats(instance.name, disk)
self.LOG.info(self.DISKIO_USAGE_MESSAGE,
instance, disk, stats[0], stats[1],
stats[2], stats[3], stats[4])
r_bytes += stats[0]
r_requests += stats[1]
w_bytes += stats[3]
w_requests += stats[2]
yield make_counter_from_instance(instance,
name='disk.read.requests',
type=counter.TYPE_CUMULATIVE,
volume=r_requests,
)
yield make_counter_from_instance(instance,
name='disk.read.bytes',
type=counter.TYPE_CUMULATIVE,
volume=r_bytes,
)
yield make_counter_from_instance(instance,
name='disk.write.requests',
type=counter.TYPE_CUMULATIVE,
volume=w_requests,
)
yield make_counter_from_instance(instance,
name='disk.write.bytes',
type=counter.TYPE_CUMULATIVE,
volume=w_bytes,
)
conn = get_libvirt_connection()
# TODO(jd) This does not work see bug#998089
# for disk in conn.get_disks(instance.name):
try:
disks = self._get_disks(conn, instance.name)
except Exception as err:
self.LOG.warning('Ignoring instance %s: %s',
instance.name, err)
self.LOG.exception(err)
else:
r_bytes = 0
r_requests = 0
w_bytes = 0
w_requests = 0
for disk in disks:
stats = conn.block_stats(instance.name, disk)
self.LOG.info(self.DISKIO_USAGE_MESSAGE,
instance, disk, stats[0], stats[1],
stats[2], stats[3], stats[4])
r_bytes += stats[0]
r_requests += stats[1]
w_bytes += stats[3]
w_requests += stats[2]
yield make_counter_from_instance(instance,
name='disk.read.requests',
type=counter.TYPE_CUMULATIVE,
volume=r_requests,
)
yield make_counter_from_instance(instance,
name='disk.read.bytes',
type=counter.TYPE_CUMULATIVE,
volume=r_bytes,
)
yield make_counter_from_instance(instance,
name='disk.write.requests',
type=counter.TYPE_CUMULATIVE,
volume=w_requests,
)
yield make_counter_from_instance(instance,
name='disk.write.bytes',
type=counter.TYPE_CUMULATIVE,
volume=w_bytes,
)
class CPUPollster(plugin.ComputePollster):
class CPUPollster(LibVirtPollster):
LOG = log.getLogger(__name__ + '.cpu')
@ -166,7 +172,7 @@ class CPUPollster(plugin.ComputePollster):
self.LOG.exception(err)
class NetPollster(plugin.ComputePollster):
class NetPollster(LibVirtPollster):
LOG = log.getLogger(__name__ + '.net')

View File

@ -16,14 +16,22 @@
# License for the specific language governing permissions and limitations
# under the License.
from stevedore import extension
from nova import manager
from ceilometer import extension_manager
from ceilometer.openstack.common import cfg
from ceilometer.openstack.common import log
from ceilometer import publish
OPTS = [
cfg.ListOpt('disabled_compute_pollsters',
default=[],
help='list of compute agent pollsters to disable',
),
]
cfg.CONF.register_opts(OPTS)
LOG = log.getLogger(__name__)
@ -38,9 +46,9 @@ class AgentManager(manager.Manager):
# importable. Need to add check against global
# configuration flag and check that asks the plugin if
# it should be enabled.
self.ext_manager = extension.ExtensionManager(
self.ext_manager = extension_manager.ActivatedExtensionManager(
namespace=PLUGIN_NAMESPACE,
invoke_on_load=True,
disabled_names=cfg.CONF.disabled_compute_pollsters,
)
return

View File

@ -0,0 +1,71 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2012 New Dream Network, LLC (DreamHost)
#
# Author: Doug Hellmann <doug.hellmann@dreamhost.com>
#
# 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.
"""Base class for plugin loader.
"""
from stevedore import enabled
from ceilometer.openstack.common import log
LOG = log.getLogger(__name__)
def should_use_extension(namespace, ext, disabled_names):
"""Return boolean indicating whether the extension
should be used.
Tests the extension against a couple of criteria to see whether it
should be used, logs the reason it is not used if not, and then
returns the result.
"""
if ext.name in disabled_names:
LOG.debug(
'%s extension %r disabled through configuration setting',
namespace, ext.name,
)
return False
if not ext.obj.is_enabled():
LOG.debug(
'%s extension %r reported that it is disabled',
namespace,
ext.name,
)
return False
LOG.debug('using %s extension %r', namespace, ext.name)
return True
class ActivatedExtensionManager(enabled.EnabledExtensionManager):
"""Loads extensions based on a configurable set that should be
disabled and asking each one if it should be active or not.
"""
def __init__(self, namespace, disabled_names, invoke_on_load=True,
invoke_args=(), invoke_kwds={}):
def local_check_func(ext):
return should_use_extension(namespace, ext, disabled_names)
super(ActivatedExtensionManager, self).__init__(
namespace=namespace,
check_func=local_check_func,
invoke_on_load=invoke_on_load,
invoke_args=invoke_args,
invoke_kwds=invoke_kwds,
)

View File

@ -30,11 +30,25 @@ import ceilometer.openstack.common.notifier.rabbit_notifier
ExchangeTopics = namedtuple('ExchangeTopics', ['exchange', 'topics'])
class NotificationBase(object):
class PluginBase(object):
"""Base class for all plugins.
"""
def is_enabled(self):
"""Return boolean indicating whether this plugin should
be enabled and used by the caller.
"""
return True
class NotificationBase(PluginBase):
"""Base class for plugins that support the notification API."""
__metaclass__ = abc.ABCMeta
def is_enabled(self):
return True
@abc.abstractmethod
def get_event_types(self):
"""Return a sequence of strings defining the event types to be
@ -58,7 +72,7 @@ class NotificationBase(object):
return metadata
class PollsterBase(object):
class PollsterBase(PluginBase):
"""Base class for plugins that support the polling API."""
__metaclass__ = abc.ABCMeta

View File

@ -23,30 +23,34 @@ Ceilometer specific
The following table lists the ceilometer specific options in the global configuration file.
Please note that ceilometer uses openstack-common extensively, which requires that
the other parameters are set appropriately. For information we are listing the configuration
elements that we use after the ceilometer specific elements.
elements that we use after the ceilometer specific elements.
If you use sql alchemy, its specific paramaters will need to be set.
========================== ==================================== ==============================================================
Parameter Default Note
========================== ==================================== ==============================================================
nova_control_exchange nova Exchange name for Nova notifications
glance_control_exchange glance_notifications Exchange name for Glance notifications
cinder_control_exchange cinder Exchange name for Cinder notifications
quantum_control_exchange quantum Exchange name for Quantum notifications
metering_secret change this or be hacked Secret value for signing metering messages
metering_topic metering the topic ceilometer uses for metering messages
counter_source openstack The source name of emited counters
control_exchange ceilometer AMQP exchange to connect to if using RabbitMQ or Qpid
periodic_interval 600 seconds between running periodic tasks
os-username glance Username to use for openstack service access
os-password admin Password to use for openstack service access
os-tenant-id Tenant ID to use for openstack service access
os-tenant-name admin Tenant name to use for openstack service access
os-auth-url http://localhost:5000/v2.0 Auth URL to use for openstack service access
database_connection mongodb://localhost:27017/ceilometer Database connection string
metering_api_port 8777 The port for the ceilometer API server
========================== ==================================== ==============================================================
=============================== ==================================== ==============================================================
Parameter Default Note
=============================== ==================================== ==============================================================
nova_control_exchange nova Exchange name for Nova notifications
glance_control_exchange glance_notifications Exchange name for Glance notifications
cinder_control_exchange cinder Exchange name for Cinder notifications
quantum_control_exchange quantum Exchange name for Quantum notifications
metering_secret change this or be hacked Secret value for signing metering messages
metering_topic metering the topic ceilometer uses for metering messages
counter_source openstack The source name of emited counters
control_exchange ceilometer AMQP exchange to connect to if using RabbitMQ or Qpid
periodic_interval 600 seconds between running periodic tasks
os-username glance Username to use for openstack service access
os-password admin Password to use for openstack service access
os-tenant-id Tenant ID to use for openstack service access
os-tenant-name admin Tenant name to use for openstack service access
os-auth-url http://localhost:5000/v2.0 Auth URL to use for openstack service access
database_connection mongodb://localhost:27017/ceilometer Database connection string
metering_api_port 8777 The port for the ceilometer API server
disabled_central_pollsters List of central pollsters to skip loading
disabled_compute_pollsters List of compute pollsters to skip loading
disabled_notification_listeners List of notification listeners to skip loading
=============================== ==================================== ==============================================================
SQL Alchemy
===========

View File

@ -8,6 +8,6 @@ sqlalchemy
eventlet
anyjson>=0.3.1
Flask==0.9
stevedore>=0.5
stevedore>=0.6
python-glanceclient
python-cinderclient