From eed463659f2e6f7018b5db1fcae9931ae1d60a00 Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Fri, 23 Jan 2015 11:50:01 +0100 Subject: [PATCH] Remove the Nova notifier This has not been tested for months and doesn't work anymore. There's no way people are using it, so let's drop it until someone starts fixing it again. Change-Id: I7d6472a1924320c890dec47c41bf22db07a36efc --- ceilometer/compute/nova_notifier.py | 183 ----------------- doc/source/conf.py | 3 +- doc/source/install/manual.rst | 1 - nova_tests/.testr.conf | 4 - nova_tests/__init__.py | 20 -- nova_tests/test_notifier.py | 307 ---------------------------- tools/enable_notifications.sh | 13 -- 7 files changed, 1 insertion(+), 530 deletions(-) delete mode 100644 ceilometer/compute/nova_notifier.py delete mode 100644 nova_tests/.testr.conf delete mode 100644 nova_tests/__init__.py delete mode 100644 nova_tests/test_notifier.py diff --git a/ceilometer/compute/nova_notifier.py b/ceilometer/compute/nova_notifier.py deleted file mode 100644 index 5bdd3800..00000000 --- a/ceilometer/compute/nova_notifier.py +++ /dev/null @@ -1,183 +0,0 @@ -# -# Copyright 2013 New Dream Network, LLC (DreamHost) -# -# Author: Doug Hellmann -# -# 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 sys - -from nova.compute import flavors -from nova import conductor -from nova import notifications -from nova.openstack.common import log as logging -from nova.openstack.common.notifier import api as notifier_api -from nova import utils -import six -from stevedore import extension - -# HACK(dhellmann): Insert the nova version of openstack.common into -# sys.modules as though it was the copy from ceilometer, so that when -# we use modules from ceilometer below they do not re-define options. -# use the real ceilometer base package -import ceilometer # noqa -from ceilometer.compute.virt import inspector -from ceilometer.i18n import _ - -for name in ['openstack', 'openstack.common', 'openstack.common.log']: - sys.modules['ceilometer.' + name] = sys.modules['nova.' + name] - -# This module runs inside the nova compute -# agent, which only configures the "nova" logger. -# We use a fake logger name in that namespace -# so that messages from this module appear -# in the log file. -LOG = logging.getLogger('nova.ceilometer.notifier') - -_gatherer = None -conductor_api = conductor.API() - - -class DeletedInstanceStatsGatherer(object): - - def __init__(self, extensions): - self.mgr = extensions - self.inspector = inspector.get_hypervisor_inspector() - - def __call__(self, instance): - cache = {} - samples = self.mgr.map_method('get_samples', - self, - cache, - instance) - # samples is a list of lists, so flatten it before returning - # the results - results = [] - for slist in samples: - results.extend(slist) - return results - - -def initialize_gatherer(gatherer=None): - """Set the callable used to gather stats for the instance. - - gatherer should be a callable accepting one argument (the instance - ref), or None to have a default gatherer used - """ - global _gatherer - if gatherer is not None: - LOG.debug(_('using provided stats gatherer %r'), gatherer) - _gatherer = gatherer - if _gatherer is None: - LOG.debug(_('making a new stats gatherer')) - mgr = extension.ExtensionManager( - namespace='ceilometer.poll.compute', - invoke_on_load=True, - ) - _gatherer = DeletedInstanceStatsGatherer(mgr) - return _gatherer - - -class Instance(object): - """Model class for instances - - The pollsters all expect an instance that looks like what the - novaclient gives them, but the conductor API gives us a - dictionary. This class makes an object from the dictionary so we - can pass it to the pollsters. - """ - def __init__(self, context, info): - for k, v in six.iteritems(info): - if k == 'name': - setattr(self, 'OS-EXT-SRV-ATTR:instance_name', v) - elif k == 'metadata': - setattr(self, k, utils.metadata_to_dict(v)) - else: - setattr(self, k, v) - - instance_type = flavors.extract_flavor(info) - self.flavor_name = instance_type.get('name', 'UNKNOWN') - self.instance_flavor_id = instance_type.get('flavorid', '') - LOG.debug(_('INFO %r'), info) - - @property - def tenant_id(self): - return self.project_id - - @property - def flavor(self): - return { - 'id': self.instance_type_id, - 'flavor_id': self.instance_flavor_id, - 'name': self.flavor_name, - 'vcpus': self.vcpus, - 'ram': self.memory_mb, - 'disk': self.root_gb + self.ephemeral_gb, - 'ephemeral': self.ephemeral_gb - } - - @property - def hostId(self): - return self.host - - @property - def image(self): - return {'id': self.image_ref} - - @property - def name(self): - return self.display_name - - -def notify(context, message): - if message['event_type'] != 'compute.instance.delete.start': - LOG.debug(_('ignoring %s'), message['event_type']) - return - LOG.info(_('processing %s'), message['event_type']) - gatherer = initialize_gatherer() - - instance_id = message['payload']['instance_id'] - LOG.debug(_('polling final stats for %r'), instance_id) - - # Ask for the instance details - instance_ref = conductor_api.instance_get_by_uuid( - context, - instance_id, - ) - - # Get the default notification payload - payload = notifications.info_from_instance( - context, instance_ref, None, None) - - # Extend the payload with samples from our plugins. We only need - # to send some of the data from the sample objects, since a lot - # of the fields are the same. - instance = Instance(context, instance_ref) - samples = gatherer(instance) - payload['samples'] = [{'name': s.name, - 'type': s.type, - 'unit': s.unit, - 'volume': s.volume} - for s in samples] - - publisher_id = notifier_api.publisher_id('compute', None) - - # We could simply modify the incoming message payload, but we - # can't be sure that this notifier will be called before the RPC - # notifier. Modifying the content may also break the message - # signature. So, we start a new message publishing. We will be - # called again recursively as a result, but we ignore the event we - # generate so it doesn't matter. - notifier_api.notify(context, publisher_id, - 'compute.instance.delete.samples', - notifier_api.INFO, payload) diff --git a/doc/source/conf.py b/doc/source/conf.py index eb0eebaf..1c62f294 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -51,8 +51,7 @@ def write_autodoc_index(): RSTDIR = os.path.abspath(os.path.join(BASE_DIR, "sourcecode")) SRCS = {'ceilometer': ROOT} - EXCLUDED_MODULES = ('ceilometer.compute.nova_notifier', - 'ceilometer.openstack.common.log_handler', + EXCLUDED_MODULES = ('ceilometer.openstack.common.log_handler', 'ceilometer.tests') CURRENT_SOURCES = {} diff --git a/doc/source/install/manual.rst b/doc/source/install/manual.rst index 810ff27d..f0c665d7 100755 --- a/doc/source/install/manual.rst +++ b/doc/source/install/manual.rst @@ -331,7 +331,6 @@ Installing the Compute Agent instance_usage_audit_period=hour notify_on_state_change=vm_and_task_state notification_driver=nova.openstack.common.notifier.rpc_notifier - notification_driver=ceilometer.compute.nova_notifier 2. Clone the ceilometer git repository to the server:: diff --git a/nova_tests/.testr.conf b/nova_tests/.testr.conf deleted file mode 100644 index d3284395..00000000 --- a/nova_tests/.testr.conf +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -test_command=${PYTHON:-python} -m subunit.run discover -t ./nova_tests ./nova_tests $LISTOPT $IDOPTION -test_id_option=--load-list $IDFILE -test_list_option=--list diff --git a/nova_tests/__init__.py b/nova_tests/__init__.py deleted file mode 100644 index 1db4852e..00000000 --- a/nova_tests/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2013 Intel Corp. -# -# Author: Lianhao Lu -# -# 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 gettext - -gettext.install('nova', unicode=True) diff --git a/nova_tests/test_notifier.py b/nova_tests/test_notifier.py deleted file mode 100644 index bb0af0b9..00000000 --- a/nova_tests/test_notifier.py +++ /dev/null @@ -1,307 +0,0 @@ -# -# Copyright 2012 New Dream Network, LLC (DreamHost) -# -# Author: Julien Danjou -# Doug Hellmann -# -# 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. -"""Tests for ceilometer.compute.nova_notifier -""" - -import contextlib -import datetime - -import mock -from oslo_utils import importutils -from oslotest import base -from oslotest import moxstubout - -from stevedore import extension - -## NOTE(dhellmann): These imports are not in the generally approved -## alphabetical order, but they are in the order that actually -## works. Please don't change them. -from nova.tests import fake_network -from nova.compute import vm_states -try: - from nova.compute import flavors -except ImportError: - from nova.compute import instance_types as flavors - -from nova.objects import instance as nova_instance -from nova import config -from nova import context -from nova import db -from nova.openstack.common import log as logging - -# This option is used in the nova_notifier module, so make -# sure it is defined. -config.cfg.CONF.import_opt('compute_manager', 'nova.service') - -# HACK(dhellmann): Import this before any other ceilometer code -# because the notifier module messes with the import path to force -# nova's version of oslo to be used instead of ceilometer's. -from ceilometer.compute import nova_notifier - -from ceilometer import sample -from ceilometer.compute.pollsters import util - -LOG = logging.getLogger(__name__) -nova_CONF = config.cfg.CONF - - -class TestNovaNotifier(base.BaseTestCase): - - class Pollster(object): - instances = [] - test_data_1 = sample.Sample( - name='test1', - type=sample.TYPE_CUMULATIVE, - unit='units-go-here', - volume=1, - user_id='test', - project_id='test', - resource_id='test_run_tasks', - timestamp=datetime.datetime.utcnow().isoformat(), - resource_metadata={'name': 'Pollster', - }, - ) - - def get_samples(self, manager, cache, instance): - self.instances.append((manager, instance)) - test_data_2 = util.make_sample_from_instance( - instance, - name='test2', - type=sample.TYPE_CUMULATIVE, - unit='units-go-here', - volume=1, - ) - return [self.test_data_1, test_data_2] - - @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) - def setUp(self): - super(TestNovaNotifier, self).setUp() - nova_CONF.compute_driver = 'nova.virt.fake.FakeDriver' - nova_CONF.notification_driver = [ - nova_notifier.__name__, - 'nova.openstack.common.notifier.rpc_notifier', - ] - nova_CONF.rpc_backend = 'nova.openstack.common.rpc.impl_fake' - nova_CONF.vnc_enabled = False - nova_CONF.spice.enabled = False - self.compute = importutils.import_object(nova_CONF.compute_manager) - self.context = context.get_admin_context() - self.stubs = self.useFixture(moxstubout.MoxStubout()).stubs - fake_network.set_stub_network_methods(self.stubs) - - self.instance_data = {"display_name": "instance-1", - "id": 1, - "image_ref": "FAKE", - "user_id": "FAKE", - "project_id": "FAKE", - "display_name": "FAKE NAME", - "hostname": "abcdef", - "reservation_id": "FAKE RID", - "instance_type_id": 1, - "architecture": "x86", - "memory_mb": "1024", - "root_gb": "20", - "ephemeral_gb": "0", - "vcpus": 1, - 'node': "fakenode", - "host": "fakehost", - "availability_zone": - "1e3ce043029547f1a61c1996d1a531a4", - "created_at": '2012-05-08 20:23:41', - "launched_at": '2012-05-08 20:25:45', - "terminated_at": '2012-05-09 20:23:41', - "os_type": "linux", - "kernel_id": "kernelid", - "ramdisk_id": "ramdiskid", - "vm_state": vm_states.ACTIVE, - "task_state": None, - "access_ip_v4": "192.168.5.4", - "access_ip_v6": "2001:DB8::0", - "metadata": {}, - "uuid": "144e08f4-00cb-11e2-888e-5453ed1bbb5f", - "system_metadata": {}, - "user_data": None, - "cleaned": 0, - "deleted": None, - "vm_mode": None, - "deleted_at": None, - "disable_terminate": False, - "root_device_name": None, - "default_swap_device": None, - "launched_on": None, - "display_description": None, - "key_data": None, - "key_name": None, - "config_drive": None, - "power_state": None, - "default_ephemeral_device": None, - "progress": 0, - "scheduled_at": None, - "updated_at": None, - "shutdown_terminate": False, - "cell_name": 'cell', - "locked": False, - "locked_by": None, - "launch_index": 0, - "auto_disk_config": False, - "ephemeral_key_uuid": None - } - - self.instance = nova_instance.Instance() - self.instance = nova_instance.Instance._from_db_object( - context, self.instance, self.instance_data, - expected_attrs=['metadata', 'system_metadata']) - - self.stubs.Set(db, 'instance_info_cache_delete', self.do_nothing) - self.stubs.Set(db, 'instance_destroy', self.do_nothing) - self.stubs.Set(db, 'instance_system_metadata_get', - self.fake_db_instance_system_metadata_get) - self.stubs.Set(db, 'block_device_mapping_get_all_by_instance', - lambda context, instance: {}) - self.stubs.Set(db, 'instance_update_and_get_original', - lambda *args, **kwargs: (self.instance, self.instance)) - self.stubs.Set(flavors, 'extract_flavor', self.fake_extract_flavor) - - # Set up to capture the notification messages generated by the - # plugin and to invoke our notifier plugin. - self.notifications = [] - - ext_mgr = extension.ExtensionManager.make_test_instance( - extensions=[ - extension.Extension('test', - None, - None, - self.Pollster(), - ), - ], - ) - self.ext_mgr = ext_mgr - self.gatherer = nova_notifier.DeletedInstanceStatsGatherer(ext_mgr) - # Initialize the global _gatherer in nova_notifier to use the - # gatherer in this test instead of the gatherer in nova_notifier. - nova_notifier.initialize_gatherer(self.gatherer) - - # Terminate the instance to trigger the notification. - with contextlib.nested( - # Under Grizzly, Nova has moved to no-db access on the - # compute node. The compute manager uses RPC to talk to - # the conductor. We need to disable communication between - # the nova manager and the remote system since we can't - # expect the message bus to be available, or the remote - # controller to be there if the message bus is online. - mock.patch.object(self.compute, 'conductor_api'), - # The code that looks up the instance uses a global - # reference to the API, so we also have to patch that to - # return our fake data. - mock.patch.object(nova_notifier.conductor_api, - 'instance_get_by_uuid', - self.fake_instance_ref_get), - mock.patch('nova.openstack.common.notifier.rpc_notifier.notify', - self.notify) - ): - with mock.patch.object(self.compute.conductor_api, - 'instance_destroy', - return_value=self.instance): - self.compute.terminate_instance(self.context, - instance=self.instance, - bdms=[], - reservations=[]) - - def tearDown(self): - self.Pollster.instances = [] - super(TestNovaNotifier, self).tearDown() - nova_notifier._gatherer = None - - # The instance returned by conductor API is a dictionary actually, - # and it will be transformed to an nova_notifier.Instance object - # that looks like what the novaclient gives them. - def fake_instance_ref_get(self, context, id_): - return self.instance_data - - @staticmethod - def fake_extract_flavor(instance_ref): - return {'ephemeral_gb': 0, - 'flavorid': '1', - 'id': 2, - 'memory_mb': 512, - 'name': 'm1.tiny', - 'root_gb': 1, - 'rxtx_factor': 1.0, - 'swap': 0, - 'vcpu_weight': None, - 'vcpus': 1} - - @staticmethod - def do_nothing(*args, **kwargs): - pass - - @staticmethod - def fake_db_instance_system_metadata_get(context, uuid): - return dict(meta_a=123, meta_b="foobar") - - def notify(self, context, message): - self.notifications.append(message) - - def test_pollster_called(self): - self.assertEqual(len(self.Pollster.instances), 1) - - def test_correct_instance(self): - for i, (gatherer, inst) in enumerate(self.Pollster.instances): - self.assertEqual((i, inst.uuid), (i, self.instance.uuid)) - - def test_correct_gatherer(self): - for i, (gatherer, inst) in enumerate(self.Pollster.instances): - self.assertEqual((i, gatherer), (i, self.gatherer)) - - def test_instance_flavor(self): - inst = nova_notifier.Instance(context, self.instance) - self.assertEqual(inst.flavor['name'], 'm1.tiny') - self.assertEqual(inst.flavor['flavor_id'], '1') - - def test_samples(self): - # Ensure that the outgoing notification looks like what we expect - for message in self.notifications: - event = message['event_type'] - if event != 'compute.instance.delete.samples': - continue - payload = message['payload'] - samples = payload['samples'] - - # Because the playload's samples doesn't include instance - # metadata, we can't check the metadata field directly. - # But if we make a mistake in the instance attributes, such - # as missing instance.name or instance.flavor['name'], it - # will raise AttributeError, which results the number of - # the samples doesn't equal to 2. - self.assertEqual(len(samples), 2) - s1 = payload['samples'][0] - self.assertEqual(s1, {'name': 'test1', - 'type': sample.TYPE_CUMULATIVE, - 'unit': 'units-go-here', - 'volume': 1, - }) - s2 = payload['samples'][1] - self.assertEqual(s2, {'name': 'test2', - 'type': sample.TYPE_CUMULATIVE, - 'unit': 'units-go-here', - 'volume': 1, - }) - - break - else: - assert False, 'Did not find expected event' diff --git a/tools/enable_notifications.sh b/tools/enable_notifications.sh index c9564ab6..f4916823 100755 --- a/tools/enable_notifications.sh +++ b/tools/enable_notifications.sh @@ -35,19 +35,6 @@ then echo "notification_driver=neutron.openstack.common.notifier.rabbit_notifier" >> $NEUTRON_CONF fi -# For nova we set both the rabbit notifier and the special ceilometer -# notifier that forces one more poll to happen before an instance is -# removed. -NOVA_CONF=/etc/nova/nova.conf -if ! grep -q "notification_driver=ceilometer.compute.nova_notifier" $NOVA_CONF -then - echo "notification_driver=ceilometer.compute.nova_notifier" >> $NOVA_CONF -fi -if ! grep -q "notification_driver=nova.openstack.common.notifier.rabbit_notifier" $NOVA_CONF -then - echo "notification_driver=nova.openstack.common.notifier.rabbit_notifier" >> $NOVA_CONF -fi - # SPECIAL CASE # Glance does not use the openstack common notifier library, # so we have to set a different option.