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
This commit is contained in:
Julien Danjou 2015-01-23 11:50:01 +01:00
parent 419669019f
commit eed463659f
7 changed files with 1 additions and 530 deletions

View File

@ -1,183 +0,0 @@
#
# Copyright 2013 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.
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)

View File

@ -51,8 +51,7 @@ def write_autodoc_index():
RSTDIR = os.path.abspath(os.path.join(BASE_DIR, "sourcecode")) RSTDIR = os.path.abspath(os.path.join(BASE_DIR, "sourcecode"))
SRCS = {'ceilometer': ROOT} SRCS = {'ceilometer': ROOT}
EXCLUDED_MODULES = ('ceilometer.compute.nova_notifier', EXCLUDED_MODULES = ('ceilometer.openstack.common.log_handler',
'ceilometer.openstack.common.log_handler',
'ceilometer.tests') 'ceilometer.tests')
CURRENT_SOURCES = {} CURRENT_SOURCES = {}

View File

@ -331,7 +331,6 @@ Installing the Compute Agent
instance_usage_audit_period=hour instance_usage_audit_period=hour
notify_on_state_change=vm_and_task_state notify_on_state_change=vm_and_task_state
notification_driver=nova.openstack.common.notifier.rpc_notifier notification_driver=nova.openstack.common.notifier.rpc_notifier
notification_driver=ceilometer.compute.nova_notifier
2. Clone the ceilometer git repository to the server:: 2. Clone the ceilometer git repository to the server::

View File

@ -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

View File

@ -1,20 +0,0 @@
#
# Copyright 2013 Intel Corp.
#
# Author: Lianhao Lu <lianhao.lu@intel.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.
import gettext
gettext.install('nova', unicode=True)

View File

@ -1,307 +0,0 @@
#
# Copyright 2012 New Dream Network, LLC (DreamHost)
#
# Author: Julien Danjou <julien@danjou.info>
# 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.
"""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'

View File

@ -35,19 +35,6 @@ then
echo "notification_driver=neutron.openstack.common.notifier.rabbit_notifier" >> $NEUTRON_CONF echo "notification_driver=neutron.openstack.common.notifier.rabbit_notifier" >> $NEUTRON_CONF
fi 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 # SPECIAL CASE
# Glance does not use the openstack common notifier library, # Glance does not use the openstack common notifier library,
# so we have to set a different option. # so we have to set a different option.