Add vanity names to notification samples
This change adds "project_name" and "user_name" fields to the polling samples created from notifications of "event_type". Also move caching helper functions into "ceilometer/cache_utils.py" to make them accessible throughout the project. Change-Id: I68bd4ee096b28a2fd952e749d56a6b3eed9bfb94
This commit is contained in:
parent
7dd58d6f75
commit
6e339d3e74
|
@ -16,6 +16,8 @@
|
|||
"""Simple wrapper for oslo_cache."""
|
||||
import uuid
|
||||
|
||||
from ceilometer import keystone_client
|
||||
from keystoneauth1 import exceptions as ka_exceptions
|
||||
from oslo_cache import core as cache
|
||||
from oslo_cache import exception
|
||||
from oslo_log import log
|
||||
|
@ -76,3 +78,29 @@ def cache_key_mangler(key):
|
|||
"""Construct an opaque cache key."""
|
||||
|
||||
return uuid.uuid5(CACHE_NAMESPACE, key).hex
|
||||
|
||||
|
||||
def resolve_uuid_from_cache(conf, attr, uuid):
|
||||
# empty cache_client means either caching is not enabled or
|
||||
# there was an error configuring cache
|
||||
cache_client = get_client(conf)
|
||||
if cache_client:
|
||||
resource_name = cache_client.get(uuid)
|
||||
if resource_name:
|
||||
return resource_name
|
||||
|
||||
# Retrieve project and user names from Keystone only
|
||||
# if ceilometer doesn't have a caching backend
|
||||
resource_name = resolve_uuid_from_keystone(conf, attr, uuid)
|
||||
if cache_client:
|
||||
cache_client.set(uuid, resource_name)
|
||||
return resource_name
|
||||
|
||||
|
||||
def resolve_uuid_from_keystone(conf, attr, uuid):
|
||||
try:
|
||||
return getattr(keystone_client.get_client(conf), attr).get(uuid).name
|
||||
except AttributeError as e:
|
||||
LOG.warning("Found '%s' while resolving uuid %s to name", e, uuid)
|
||||
except ka_exceptions.NotFound as e:
|
||||
LOG.warning(e.message)
|
||||
|
|
|
@ -15,6 +15,7 @@ import itertools
|
|||
import os
|
||||
import re
|
||||
|
||||
from ceilometer import cache_utils
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from stevedore import extension
|
||||
|
@ -165,8 +166,22 @@ class MeterDefinition(object):
|
|||
# NOTE(sileht): Transform the sample with multiple values per
|
||||
# attribute into multiple samples with one value per attribute.
|
||||
for values in zip(*samples_values):
|
||||
yield dict((attributes[idx], value)
|
||||
for idx, value in enumerate(values))
|
||||
sample = dict((attributes[idx], value)
|
||||
for idx, value in enumerate(values))
|
||||
|
||||
# populate user_name and project_name fields in the sample
|
||||
# created from notifications
|
||||
if sample['user_id']:
|
||||
sample['user_name'] = \
|
||||
cache_utils.resolve_uuid_from_cache(
|
||||
self.conf, 'users', sample['user_id']
|
||||
)
|
||||
if sample['project_id']:
|
||||
sample['project_name'] = \
|
||||
cache_utils.resolve_uuid_from_cache(
|
||||
self.conf, 'projects', sample['project_id']
|
||||
)
|
||||
yield sample
|
||||
else:
|
||||
yield sample
|
||||
|
||||
|
|
|
@ -159,29 +159,6 @@ class PollingTask(object):
|
|||
key = Resources.key(source.name, pollster)
|
||||
self.resources[key].setup(source)
|
||||
|
||||
def resolve_uuid_from_cache(self, attr, uuid):
|
||||
if self.cache_client:
|
||||
name = self.cache_client.get(uuid)
|
||||
if name:
|
||||
return name
|
||||
# empty cache_client means either caching is not enabled or
|
||||
# there was an error configuring cache
|
||||
name = self.resolve_uuid_from_keystone(attr, uuid)
|
||||
self.cache_client.set(uuid, name)
|
||||
return name
|
||||
|
||||
# Retrieve project and user names from Keystone only
|
||||
# if ceilometer doesn't have a caching backend
|
||||
return self.resolve_uuid_from_keystone(attr, uuid)
|
||||
|
||||
def resolve_uuid_from_keystone(self, attr, uuid):
|
||||
try:
|
||||
return getattr(self.ks_client, attr).get(uuid).name
|
||||
except AttributeError as e:
|
||||
LOG.warning("Found '%s' while resolving uuid %s to name", e, uuid)
|
||||
except ka_exceptions.NotFound as e:
|
||||
LOG.warning(e.message)
|
||||
|
||||
def poll_and_notify(self):
|
||||
"""Polling sample and notify."""
|
||||
cache = {}
|
||||
|
@ -240,16 +217,20 @@ class PollingTask(object):
|
|||
# and then keystone
|
||||
if sample.project_id:
|
||||
sample.project_name = \
|
||||
self.resolve_uuid_from_cache(
|
||||
"projects", sample.project_id
|
||||
cache_utils.resolve_uuid_from_cache(
|
||||
self.manager.conf,
|
||||
"projects",
|
||||
sample.project_id
|
||||
)
|
||||
|
||||
# Try to resolve user UUIDs from cache first,
|
||||
# and then keystone
|
||||
if sample.user_id:
|
||||
sample.user_name = \
|
||||
self.resolve_uuid_from_cache(
|
||||
"users", sample.user_id
|
||||
cache_utils.resolve_uuid_from_cache(
|
||||
self.manager.conf,
|
||||
"users",
|
||||
sample.user_id
|
||||
)
|
||||
|
||||
sample_dict = (
|
||||
|
|
|
@ -121,7 +121,8 @@ class Sample(object):
|
|||
@classmethod
|
||||
def from_notification(cls, name, type, volume, unit,
|
||||
user_id, project_id, resource_id,
|
||||
message, timestamp=None, metadata=None, source=None):
|
||||
message, timestamp=None, metadata=None, source=None,
|
||||
user_name=None, project_name=None):
|
||||
if not metadata:
|
||||
metadata = (copy.copy(message['payload'])
|
||||
if isinstance(message['payload'], dict) else {})
|
||||
|
@ -138,7 +139,9 @@ class Sample(object):
|
|||
resource_id=resource_id,
|
||||
timestamp=ts,
|
||||
resource_metadata=metadata,
|
||||
source=source)
|
||||
source=source,
|
||||
user_name=user_name,
|
||||
project_name=project_name)
|
||||
|
||||
def set_timestamp(self, timestamp):
|
||||
self.timestamp = timestamp
|
||||
|
|
|
@ -141,19 +141,23 @@ FULL_MULTI_MSG = {
|
|||
'payload': [{
|
||||
'counter_name': 'instance1',
|
||||
'user_id': 'user1',
|
||||
'user_name': 'test-resource',
|
||||
'resource_id': 'res1',
|
||||
'counter_unit': 'ns',
|
||||
'counter_volume': 28.0,
|
||||
'project_id': 'proj1',
|
||||
'project_name': 'test-resource',
|
||||
'counter_type': 'gauge'
|
||||
},
|
||||
{
|
||||
'counter_name': 'instance2',
|
||||
'user_id': 'user2',
|
||||
'user_name': 'test-resource',
|
||||
'resource_id': 'res2',
|
||||
'counter_unit': '%',
|
||||
'counter_volume': 1.0,
|
||||
'project_id': 'proj2',
|
||||
'project_name': 'test-resource',
|
||||
'counter_type': 'delta'
|
||||
}],
|
||||
'ctxt': {'domain': None,
|
||||
|
@ -615,7 +619,16 @@ class TestMeterProcessing(test.BaseTestCase):
|
|||
c = list(self.handler.build_sample(event))
|
||||
self.assertEqual(0, len(c))
|
||||
|
||||
def test_multi_meter_payload_all_multi(self):
|
||||
@mock.patch('ceilometer.cache_utils.resolve_uuid_from_cache')
|
||||
def test_multi_meter_payload_all_multi(self, fake_cached_resource_name):
|
||||
|
||||
# return "test-resource" as the name of the user and project from cache
|
||||
fake_cached_resource_name.return_value = "test-resource"
|
||||
|
||||
# expect user_name and project_name values to be set to "test-resource"
|
||||
fake_user_name = "test-resource"
|
||||
fake_project_name = "test-resource"
|
||||
|
||||
cfg = yaml.dump(
|
||||
{'metric': [dict(name="$.payload.[*].counter_name",
|
||||
event_type="full.sample",
|
||||
|
@ -640,6 +653,8 @@ class TestMeterProcessing(test.BaseTestCase):
|
|||
self.assertEqual(msg[idx]['resource_id'], s1['resource_id'])
|
||||
self.assertEqual(msg[idx]['project_id'], s1['project_id'])
|
||||
self.assertEqual(msg[idx]['user_id'], s1['user_id'])
|
||||
self.assertEqual(fake_user_name, s1['user_name'])
|
||||
self.assertEqual(fake_project_name, s1['project_name'])
|
||||
|
||||
@mock.patch('ceilometer.meter.notifications.LOG')
|
||||
def test_multi_meter_payload_invalid_missing(self, LOG):
|
||||
|
|
Loading…
Reference in New Issue