Handle Heat notifications for stack CRUD

Add meters for all successfully CRUD operations :
- stack.create
- stack.delete
- stack.update
- stack.suspend
- stack.resume

Implements blueprint handle-heat-notifications

Change-Id: I98ffd1544d184237dc91aed98b1ad7b2fce8734a
This commit is contained in:
Swann Croiset 2014-01-27 16:18:31 +01:00
parent d19db8efe6
commit e26daa9ca5
8 changed files with 247 additions and 0 deletions

View File

View File

@ -0,0 +1,76 @@
# Author: Swann Croiset <swann.croiset@bull.net>
#
# 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.
"""Handler for producing orchestration metering from Heat notification
events.
"""
from oslo.config import cfg
from ceilometer import plugin
from ceilometer import sample
OPTS = [
cfg.StrOpt('heat_control_exchange',
default='heat',
help="Exchange name for Heat notifications"),
]
cfg.CONF.register_opts(OPTS)
SERVICE = 'orchestration'
class StackCRUD(plugin.NotificationBase):
resource_name = '%s.stack' % SERVICE
@property
def event_types(self):
return [
'%s.create.end' % (self.resource_name),
'%s.update.end' % (self.resource_name),
'%s.delete.end' % (self.resource_name),
'%s.resume.end' % (self.resource_name),
'%s.suspend.end' % (self.resource_name),
]
@staticmethod
def get_exchange_topics(conf):
return [
plugin.ExchangeTopics(
exchange=conf.heat_control_exchange,
topics=set(topic + ".info"
for topic in conf.notification_topics)),
]
def process_notification(self, message):
name = message['event_type'] \
.replace(self.resource_name, 'stack') \
.replace('.end', '')
project_id = message['payload']['tenant_id']
# Trying to use the trustor_id if trusts is used by Heat,
user_id = message.get('_context_trustor_user_id') or \
message['_context_user_id']
yield sample.Sample.from_notification(
name=name,
type=sample.TYPE_DELTA,
unit='stack',
volume=1,
resource_id=message['payload']['stack_identity'],
user_id=user_id,
project_id=project_id,
message=message)

View File

@ -0,0 +1,130 @@
# Author: Swann Croiset <swann.croiset@bull.net>
#
# 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 datetime
from oslo.config import cfg
from ceilometer.openstack.common import test
from ceilometer.orchestration import notifications
from ceilometer import sample
NOW = datetime.datetime.isoformat(datetime.datetime.utcnow())
TENANT_ID = u'4c35985848bf4419b3f3d52c22e5792d'
STACK_NAME = u'AS1-ASGroup-53sqbo7sor7i'
STACK_ID = u'cb4a6fd1-1f5d-4002-ae91-9b91573cfb03'
USER_NAME = u'demo'
USER_ID = u'2e61f25ec63a4f6c954a6245421448a4'
TRUSTOR_ID = u'foo-Trustor-Id'
STACK_ARN = u'arn:openstack:heat::%s:stacks/%s/%s' % (TENANT_ID,
STACK_NAME,
STACK_ID)
CONF = cfg.CONF
CONF.set_override('use_stderr', True)
from ceilometer.openstack.common import log
LOG = log.getLogger(__name__)
def stack_notification_for(operation, use_trust=None):
if use_trust:
trust_id = 'footrust'
trustor_id = TRUSTOR_ID
else:
trust_id = None
trustor_id = None
return {
u'event_type': '%s.stack.%s.end' % (notifications.SERVICE,
operation),
u'_context_roles': [
u'Member',
],
u'_context_request_id': u'req-cf24cf30-af35-4a47-ae29-e74d75ebc6de',
u'_context_auth_url': u'http://0.1.0.1:1010/v2.0',
u'timestamp': NOW,
u'_unique_id': u'1afb4283660f410c802af4d5992a39f2',
u'_context_tenant_id': TENANT_ID,
u'payload': {
u'state_reason': u'Stack create completed successfully',
u'user_id': USER_NAME,
u'stack_identity': STACK_ARN,
u'stack_name': STACK_NAME,
u'tenant_id': TENANT_ID,
u'create_at': u'2014-01-27T13:13:19Z',
u'state': u'CREATE_COMPLETE'
},
u'_context_username': USER_NAME,
u'_context_auth_token': u'MIISAwYJKoZIhvcNAQcCoII...',
u'_context_password': u'password',
u'_context_user_id': USER_ID,
u'_context_trustor_user_id': trustor_id,
u'_context_aws_creds': None,
u'_context_show_deleted': False,
u'_context_tenant': USER_NAME,
u'_context_trust_id': trust_id,
u'priority': u'INFO',
u'_context_is_admin': False,
u'_context_user': USER_NAME,
u'publisher_id': u'orchestration.node-n5x66lxdy67d',
u'message_id': u'ef921faa-7f7b-4854-8b86-a424ab93c96e',
}
class TestNotification(test.BaseTestCase):
def _verify_common_sample(self, s, name, volume):
self.assertIsNotNone(s)
self.assertEqual(s.name, 'stack.%s' % name)
self.assertEqual(s.timestamp, NOW)
self.assertEqual(s.type, sample.TYPE_DELTA)
self.assertEqual(s.project_id, TENANT_ID)
self.assertEqual(s.resource_id, STACK_ARN)
metadata = s.resource_metadata
self.assertEqual(metadata.get('host'),
u'orchestration.node-n5x66lxdy67d')
def _test_operation(self, operation, trust=None):
notif = stack_notification_for(operation, trust)
handler = notifications.StackCRUD()
data = list(handler.process_notification(notif))
self.assertEqual(len(data), 1)
if trust:
self.assertEqual(data[0].user_id, TRUSTOR_ID)
else:
self.assertEqual(data[0].user_id, USER_ID)
self._verify_common_sample(data[0], operation, 1)
def test_create(self):
self._test_operation('create')
def test_create_trust(self):
self._test_operation('create', trust=True)
def test_update(self):
self._test_operation('update')
def test_delete(self):
self._test_operation('delete')
def test_resume(self):
self._test_operation('resume')
def test_suspend(self):
self._test_operation('suspend')

View File

@ -467,6 +467,23 @@ Configuring keystone to work with API
default port value for ceilometer API is 8777. If the port value
has been customized, adjust accordingly.
Configuring Heat to send notifications
======================================
Configure the driver in ``heat.conf``
::
notification_driver=heat.openstack.common.notifier.rpc_notifier
Or if migration to oslo.messaging is done for Icehouse:
::
notification_driver=oslo.messaging.notifier.Notifier
Notifications queues
========================

View File

@ -170,6 +170,21 @@ Swift as described in :ref:`installing_manually`. Note that they may not be
updated right after an upload/download, since Swift takes some time to update
the container properties.
Orchestration (Heat)
====================
=============================== ========== ========== =========== ============ ==========================================
Name Type Unit Resource Origin Note
=============================== ========== ========== =========== ============ ==========================================
stack.create Delta stack stack ID notification Creation requests for a stack successful
stack.update Delta stack stack ID notification Updating requests for a stack successful
stack.delete Delta stack stack ID notification Deletion requests for a stack successful
stack.resume Delta stack stack ID notification Resuming requests for a stack successful
stack.suspend Delta stack stack ID notification Suspending requests for a stack successful
=============================== ========== ========== =========== ============ ==========================================
To enable Heat notifications configure Heat as described in :ref:`installing_manually`.
Energy (Kwapi)
==============

View File

@ -486,6 +486,14 @@
#matchmaker_heartbeat_ttl=600
#
# Options defined in ceilometer.orchestration.notifications
#
# Exchange name for Heat notifications (string value)
#heat_control_exchange=heat
#
# Options defined in ceilometer.storage
#

View File

@ -62,6 +62,7 @@ ceilometer.notification =
bandwidth = ceilometer.network.notifications:Bandwidth
http.request = ceilometer.middleware:HTTPRequest
http.response = ceilometer.middleware:HTTPResponse
stack_crud = ceilometer.orchestration.notifications:StackCRUD
ceilometer.poll.compute =
disk.read.requests = ceilometer.compute.pollsters.disk:ReadRequestsPollster