Merge "Decouple the nova notifier from ceilometer code"

This commit is contained in:
Jenkins 2013-03-01 13:51:44 +00:00 committed by Gerrit Code Review
commit ca064bdbd6
17 changed files with 627 additions and 48 deletions

View File

@ -3,7 +3,8 @@ include ChangeLog
include ceilometer/storage/sqlalchemy/migrate_repo/migrate.cfg
exclude .gitignore
exclude .gitreview
recursive-include tests *.py
recursive-include nova_tests *.py
global-exclude *.pyc
recursive-include public *
recursive-include ceilometer/locale *

View File

@ -29,6 +29,10 @@ from ceilometer.openstack.common import log
from ceilometer.openstack.common import timeutils
from ceilometer.openstack.common.rpc import dispatcher as rpc_dispatcher
# Import rpc_notifier to register `notification_topics` flag so that
# plugins can use it
# FIXME(dhellmann): Use option importing feature of oslo.config instead.
import ceilometer.openstack.common.notifier.rpc_notifier
OPTS = [
cfg.ListOpt('disabled_notification_listeners',
@ -56,6 +60,7 @@ class CollectorService(service.PeriodicService):
def initialize_service_hook(self, service):
'''Consumers must be declared before consume_thread start.'''
LOG.debug('initialize_service_hooks')
publisher_manager = dispatch.NameDispatchExtensionManager(
namespace=pipeline.PUBLISHER_NAMESPACE,
check_func=lambda x: True,
@ -63,6 +68,8 @@ class CollectorService(service.PeriodicService):
)
self.pipeline_manager = pipeline.setup_pipeline(publisher_manager)
LOG.debug('loading notification handlers from %s',
self.COLLECTOR_NAMESPACE)
self.notification_manager = \
extension_manager.ActivatedExtensionManager(
namespace=self.COLLECTOR_NAMESPACE,

View File

@ -0,0 +1,28 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 New Dream Network, LLC
#
# 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.
from oslo.config import cfg
OPTS = [
cfg.ListOpt('disabled_compute_pollsters',
default=[],
help='list of compute agent pollsters to disable',
),
]
cfg.CONF.register_opts(OPTS)

View File

@ -46,10 +46,15 @@ def get_metadata_from_object(instance):
'host': instance.hostId,
# Image properties
'image_ref': (instance.image['id'] if instance.image else None),
'image_ref_url': (instance.image['links'][0]['href']
if instance.image else None),
}
# Images that come through the conductor API in the nova notifier
# plugin will not have links.
if instance.image and instance.image.get('links'):
metadata['image_ref_url'] = instance.image['links'][0]['href']
else:
metadata['image_ref_url'] = None
for name in INSTANCE_PROPERTIES:
metadata[name] = getattr(instance, name, u'')
return metadata

View File

@ -17,7 +17,6 @@
# under the License.
from oslo.config import cfg
from stevedore import driver
from ceilometer import agent
from ceilometer import extension_manager
@ -25,18 +24,6 @@ from ceilometer import nova_client
from ceilometer.compute.virt import inspector as virt_inspector
from ceilometer.openstack.common import log
OPTS = [
cfg.ListOpt('disabled_compute_pollsters',
default=[],
help='list of compute agent pollsters to disable',
),
cfg.StrOpt('hypervisor_inspector',
default='libvirt',
help='Inspector to use for inspecting the hypervisor layer'),
]
cfg.CONF.register_opts(OPTS)
LOG = log.getLogger(__name__)
@ -64,18 +51,6 @@ class PollingTask(agent.PollingTask):
self.manager.nv.instance_get_all_by_host(cfg.CONF.host))
def get_hypervisor_inspector():
try:
namespace = 'ceilometer.compute.virt'
mgr = driver.DriverManager(namespace,
cfg.CONF.hypervisor_inspector,
invoke_on_load=True)
return mgr.driver
except ImportError as e:
LOG.error("Unable to load the hypervisor inspector: %s" % (e))
return virt_inspector.Inspector()
class AgentManager(agent.AgentManager):
def __init__(self):
@ -85,7 +60,7 @@ class AgentManager(agent.AgentManager):
disabled_names=cfg.CONF.disabled_compute_pollsters,
),
)
self._inspector = get_hypervisor_inspector()
self._inspector = virt_inspector.get_hypervisor_inspector()
self.nv = nova_client.Client()
def create_polling_task(self):

View File

@ -172,3 +172,29 @@ class InstanceFlavor(_Base):
)
)
return counters
class InstanceDelete(_Base):
"""Handle the messages sent by the nova notifier plugin
when an instance is being deleted.
"""
@staticmethod
def get_event_types():
return ['compute.instance.delete.samples']
def process_notification(self, message):
return [
counter.Counter(name=sample['name'],
type=sample['type'],
unit=sample['unit'],
volume=sample['volume'],
user_id=message['payload']['user_id'],
project_id=message['payload']['tenant_id'],
resource_id=message['payload']['instance_id'],
timestamp=message['timestamp'],
resource_metadata=self.notification_to_metadata(
message),
)
for sample in message['payload'].get('samples', [])
]

View File

@ -0,0 +1,28 @@
# -*- encoding: utf-8 -*-
#
# 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.
# NOTE(dhellmann): The implementations of the notifier for folsom and
# grizzly are completely different. Rather than have lots of checks
# throughout the code, the two implementations are placed in separate
# modules and the right version is imported here.
try:
import nova.conductor
except ImportError:
from .folsom import *
else:
from .grizzly import *

View File

@ -16,17 +16,18 @@
# License for the specific language governing permissions and limitations
# under the License.
__all__ = [
'notify',
'initialize_manager',
]
from oslo.config import cfg
from ceilometer.openstack.common import log as logging
from ceilometer.compute.manager import AgentManager
try:
from nova.conductor import api
instance_info_source = api.API()
except ImportError:
from nova import db as instance_info_source
from nova import db as instance_info_source
# This module runs inside the nova compute
# agent, which only configures the "nova" logger.

View File

@ -0,0 +1,175 @@
# -*- encoding: utf-8 -*-
#
# 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.
__all__ = [
'notify',
'DeletedInstanceStatsGatherer',
'initialize_gatherer',
'instance_info_source',
'_gatherer', # for tests to mock
]
import sys
from nova import notifications
from nova.openstack.common.notifier import api as notifier_api
from nova.openstack.common import log as logging
# 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.
import ceilometer # use the real ceilometer base package
for name in ['openstack', 'openstack.common', 'openstack.common.log']:
sys.modules['ceilometer.' + name] = sys.modules['nova.' + name]
from nova.conductor import api
from oslo.config import cfg
from ceilometer import extension_manager
from ceilometer.compute.virt import inspector
# 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
instance_info_source = api.API()
class DeletedInstanceStatsGatherer(object):
def __init__(self, extensions):
self.mgr = extensions
self.inspector = inspector.get_hypervisor_inspector()
def _get_counters_from_plugin(self, ext, instance, *args, **kwds):
"""Used with the extenaion manager map() method."""
return ext.obj.get_counters(self, instance)
def __call__(self, instance):
counters = self.mgr.map(self._get_counters_from_plugin,
instance=instance,
)
# counters is a list of lists, so flatten it before returning
# the results
results = []
for clist in counters:
results.extend(clist)
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_manager.ActivatedExtensionManager(
namespace='ceilometer.poll.compute',
disabled_names=cfg.CONF.disabled_compute_pollsters,
)
_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 dictonary so we
can pass it to the pollsters.
"""
def __init__(self, info):
for k, v in info.iteritems():
setattr(self, k, v)
LOG.debug('INFO %r', info)
@property
def tenant_id(self):
return self.project_id
@property
def flavor(self):
return {
'id': self.instance_type_id,
'name': self.instance_type.get('name', 'UNKNOWN'),
}
@property
def hostId(self):
return self.host
@property
def image(self):
return {'id': self.image_ref}
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 = instance_info_source.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 counter objects, since a lot
# of the fields are the same.
instance = Instance(instance_ref)
counters = gatherer(instance)
payload['samples'] = [{'name': c.name,
'type': c.type,
'unit': c.unit,
'volume': c.volume}
for c in counters]
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

@ -3,6 +3,7 @@
# Copyright © 2012 Red Hat, Inc
#
# Author: Eoghan Glynn <eglynn@redhat.com>
# 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
@ -19,6 +20,23 @@
import collections
from oslo.config import cfg
from stevedore import driver
from ceilometer.openstack.common import log
OPTS = [
cfg.StrOpt('hypervisor_inspector',
default='libvirt',
help='Inspector to use for inspecting the hypervisor layer'),
]
cfg.CONF.register_opts(OPTS)
LOG = log.getLogger(__name__)
# Named tuple representing instances.
#
# name: the name of the instance
@ -128,3 +146,15 @@ class Inspector(object):
read and written, and the error count
"""
raise NotImplementedError()
def get_hypervisor_inspector():
try:
namespace = 'ceilometer.compute.virt'
mgr = driver.DriverManager(namespace,
cfg.CONF.hypervisor_inspector,
invoke_on_load=True)
return mgr.driver
except ImportError as e:
LOG.error("Unable to load the hypervisor inspector: %s" % (e))
return Inspector()

View File

@ -21,10 +21,6 @@
import abc
from collections import namedtuple
# Import rpc_notifier to register notification_topics flag so that
# plugins can use it
import ceilometer.openstack.common.notifier.rpc_notifier
ExchangeTopics = namedtuple('ExchangeTopics', ['exchange', 'topics'])

View File

@ -18,6 +18,13 @@
"""Tests for ceilometer.compute.nova_notifier
"""
try:
import nova.conductor
import nose.plugins.skip
raise nose.SkipTest('do not run folsom tests under grizzly')
except ImportError:
pass
# FIXME(dhellmann): Temporarily disable these tests so we can get a
# fix to go through Jenkins.
import nose.plugins.skip
@ -31,13 +38,9 @@ from stevedore import extension
from stevedore.tests import manager as test_manager
from ceilometer.compute import manager
try:
from nova import config
nova_CONF = config.cfg.CONF
except ImportError:
# XXX Folsom compat
from nova import flags
nova_CONF = flags.FLAGS
# XXX Folsom compat
from nova import flags
nova_CONF = flags.FLAGS
from nova import db
from nova import context
from nova import service # For nova_CONF.compute_manager

229
nova_tests/test_grizzly.py Normal file
View File

@ -0,0 +1,229 @@
# -*- encoding: utf-8 -*-
#
# 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
"""
try:
import nova.conductor
except ImportError:
import nose.plugins.skip
raise nose.SkipTest('do not run grizzly tests under folsom')
import contextlib
import datetime
import mock
from oslo.config import cfg
from stevedore import extension
from stevedore.tests import manager as test_manager
## 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 import config
from nova import db
from nova import context
from nova.tests import fake_network
from nova.compute import vm_states
from nova.openstack.common.notifier import api as notifier_api
from nova.openstack.common import importutils
from nova.openstack.common import log as logging
# For nova_CONF.compute_manager, used in the nova_notifier module.
from nova import 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 counter
from ceilometer.tests import base
LOG = logging.getLogger(__name__)
nova_CONF = config.cfg.CONF
class TestNovaNotifier(base.TestCase):
class Pollster(object):
instances = []
test_data = counter.Counter(
name='test',
type=counter.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_counters(self, manager, instance):
self.instances.append((manager, instance))
return [self.test_data]
def get_counter_names(self):
return ['test']
@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_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()
fake_network.set_stub_network_methods(self.stubs)
self.instance = {"name": "instance-1",
'OS-EXT-SRV-ATTR:instance_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,
"host": "fakehost",
"availability_zone":
"1e3ce043029547f1a61c1996d1a531a4",
"created_at": '2012-05-08 20:23:41',
"os_type": "linux",
"kernel_id": "kernelid",
"ramdisk_id": "ramdiskid",
"vm_state": vm_states.ACTIVE,
"access_ip_v4": "someip",
"access_ip_v6": "someip",
"metadata": {},
"uuid": "144e08f4-00cb-11e2-888e-5453ed1bbb5f",
"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 context, uuid, kwargs: (self.instance,
self.instance))
# Set up to capture the notification messages generated by the
# plugin and to invoke our notifier plugin.
self.notifications = []
notifier_api._reset_drivers()
notifier_api.add_driver(self)
notifier_api.add_driver(nova_notifier)
ext_mgr = test_manager.TestExtensionManager([
extension.Extension('test',
None,
None,
self.Pollster(),
),
])
self.ext_mgr = ext_mgr
self.gatherer = nova_notifier.DeletedInstanceStatsGatherer(ext_mgr)
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.instance_info_source,
'instance_get_by_uuid',
self.fake_instance_ref_get),
):
self.compute.terminate_instance(self.context,
instance=self.instance)
def tearDown(self):
notifier_api._reset_drivers()
self.Pollster.instances = []
super(TestNovaNotifier, self).tearDown()
nova_notifier._gatherer = None
def fake_instance_ref_get(self, context, id_):
if self.instance['uuid'] == id_:
return self.instance
return {}
@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):
# The notifier plugin sends another notification for the same
# instance, so we expect to have 2 entries in the list.
self.assertEqual(len(self.Pollster.instances), 2)
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_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']
self.assertEqual(len(samples), 1)
s = payload['samples'][0]
self.assertEqual(s, {'name': 'test',
'type': counter.TYPE_CUMULATIVE,
'unit': 'units-go-here',
'volume': 1,
})
break
else:
assert False, 'Did not find expected event'

View File

@ -91,6 +91,7 @@ setuptools.setup(
[ceilometer.collector]
instance = ceilometer.compute.notifications:Instance
instance_flavor = ceilometer.compute.notifications:InstanceFlavor
instance_delete = ceilometer.compute.notifications:InstanceDelete
memory = ceilometer.compute.notifications:Memory
vcpus = ceilometer.compute.notifications:VCpus
disk_root_size = ceilometer.compute.notifications:RootDiskSize

View File

@ -285,13 +285,79 @@ INSTANCE_RESIZE_REVERT_END = {
u'priority': u'INFO'
}
INSTANCE_DELETE_SAMPLES = {
u'_context_roles': [u'admin'],
u'_context_request_id': u'req-9da1d714-dabe-42fd-8baa-583e57cd4f1a',
u'_context_quota_class': None,
u'event_type': u'compute.instance.delete.samples',
u'_context_user_name': u'admin',
u'_context_project_name': u'admin',
u'timestamp': u'2013-01-04 15:20:32.009532',
u'_context_is_admin': True,
u'message_id': u'c48deeba-d0c3-4154-b3db-47480b52267a',
u'_context_auth_token': None,
u'_context_instance_lock_checked': False,
u'_context_project_id': u'cea4b25edb484e5392727181b7721d29',
u'_context_timestamp': u'2013-01-04T15:19:51.018218',
u'_context_read_deleted': u'no',
u'_context_user_id': u'01b83a5e23f24a6fb6cd073c0aee6eed',
u'_context_remote_address': u'10.147.132.184',
u'publisher_id': u'compute.ip-10-147-132-184.ec2.internal',
u'payload': {u'state_description': u'resize_reverting',
u'availability_zone': None,
u'ephemeral_gb': 0,
u'instance_type_id': 2,
u'deleted_at': u'',
u'reservation_id': u'r-u3fvim06',
u'memory_mb': 512,
u'user_id': u'01b83a5e23f24a6fb6cd073c0aee6eed',
u'hostname': u's1',
u'state': u'resized',
u'launched_at': u'2013-01-04T15:10:14.000000',
u'metadata': [],
u'ramdisk_id': u'5f23128e-5525-46d8-bc66-9c30cd87141a',
u'access_ip_v6': None,
u'disk_gb': 0,
u'access_ip_v4': None,
u'kernel_id': u'571478e0-d5e7-4c2e-95a5-2bc79443c28a',
u'host': u'ip-10-147-132-184.ec2.internal',
u'display_name': u's1',
u'image_ref_url': u'http://10.147.132.184:9292/images/'
'a130b9d9-e00e-436e-9782-836ccef06e8a',
u'root_gb': 0,
u'tenant_id': u'cea4b25edb484e5392727181b7721d29',
u'created_at': u'2013-01-04T11:21:48.000000',
u'instance_id': u'648e8963-6886-4c3c-98f9-4511c292f86b',
u'instance_type': u'm1.tiny',
u'vcpus': 1,
u'image_meta': {u'kernel_id':
u'571478e0-d5e7-4c2e-95a5-2bc79443c28a',
u'ramdisk_id':
u'5f23128e-5525-46d8-bc66-9c30cd87141a',
u'base_image_ref':
u'a130b9d9-e00e-436e-9782-836ccef06e8a'},
u'architecture': None,
u'os_type': None,
u'samples': [{u'name': u'sample-name1',
u'type': u'sample-type1',
u'unit': u'sample-units1',
u'volume': 1},
{u'name': u'sample-name2',
u'type': u'sample-type2',
u'unit': u'sample-units2',
u'volume': 2},
],
},
u'priority': u'INFO'
}
class TestNotifications(unittest.TestCase):
def test_process_notification(self):
info = notifications.Instance().process_notification(
INSTANCE_CREATE_END
)[0]
for name, actual, expected in [
('counter_name', info.name, 'instance'),
('counter_type', info.type, counter.TYPE_GAUGE),
@ -435,3 +501,10 @@ class TestNotifications(unittest.TestCase):
c = counters[0]
self.assertEqual(c.volume,
INSTANCE_RESIZE_REVERT_END['payload']['vcpus'])
def test_instance_delete_samples(self):
ic = notifications.InstanceDelete()
counters = ic.process_notification(INSTANCE_DELETE_SAMPLES)
self.assertEqual(len(counters), 2)
names = [c.name for c in counters]
self.assertEqual(names, ['sample-name1', 'sample-name2'])

View File

@ -33,9 +33,9 @@ class TestPollsterBase(test_base.TestCase):
def setUp(self):
super(TestPollsterBase, self).setUp()
self.mox.StubOutWithMock(manager, 'get_hypervisor_inspector')
self.mox.StubOutWithMock(virt_inspector, 'get_hypervisor_inspector')
self.inspector = self.mox.CreateMock(virt_inspector.Inspector)
manager.get_hypervisor_inspector().AndReturn(self.inspector)
virt_inspector.get_hypervisor_inspector().AndReturn(self.inspector)
self.instance = mock.MagicMock()
self.instance.name = 'instance-00000001'
setattr(self.instance, 'OS-EXT-SRV-ATTR:instance_name',

View File

@ -11,6 +11,7 @@ setenv = VIRTUAL_ENV={envdir}
NOSE_OPENSTACK_YELLOW=0.025
NOSE_OPENSTACK_SHOW_ELAPSED=1
commands =
nosetests --no-path-adjustment --where=./nova_tests
nosetests --no-path-adjustment --where=./tests
sitepackages = False