Merge "Add support for Samples coming from Ceilometer"

This commit is contained in:
Jenkins
2014-10-16 16:56:22 +00:00
committed by Gerrit Code Review
4 changed files with 242 additions and 3 deletions

View File

@@ -58,17 +58,32 @@ class prop(object):
>>> u = User()
>>> u.age = 'thirty'
TypeError: Invalid type for attr age
By specifying an alias attribute name, that alias will be read when the
primary attribute name does not appear within the resource:
>>> class User(Resource):
... name = prop('address', alias='location')
...
>>> u = User(location='Far Away')
>>> print u['address']
Far Away
"""
def __init__(self, name, type=None):
def __init__(self, name, alias=None, type=None):
self.name = name
self.type = type
self.alias = alias
def __get__(self, instance, owner):
try:
return instance._attrs[self.name]
except KeyError:
raise AttributeError('Unset property: %s' % self.name)
try:
return instance._attrs[self.alias]
except KeyError:
raise AttributeError('Unset property: %s' % self.name)
def __set__(self, instance, value):
if self.type and not isinstance(value, self.type):
@@ -80,7 +95,10 @@ class prop(object):
try:
del instance._attrs[self.name]
except KeyError:
raise AttributeError('Unset property: %s' % self.name)
try:
del instance._attrs[self.alias]
except KeyError:
raise AttributeError('Unset property: %s' % self.name)
@six.add_metaclass(abc.ABCMeta)

View File

@@ -0,0 +1,59 @@
# 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 openstack import resource
from openstack.telemetry import telemetry_service
class Sample(resource.Resource):
id_attribute = 'sample_id'
base_path = '/v2/meters/%(meter)s'
service = telemetry_service.TelemetryService()
# Supported Operations
allow_create = True
allow_list = True
# Properties
metadata = resource.prop('metadata', alias='resource_metadata')
meter = resource.prop('meter', alias='counter_name')
project_id = resource.prop('project_id')
recorded_at = resource.prop('recorded_at')
resource_id = resource.prop('resource_id')
sample_id = resource.prop('id', alias='message_id')
source = resource.prop('source')
generated_at = resource.prop('timestamp')
type = resource.prop('type', alias='counter_type')
unit = resource.prop('unit', alias='counter_unit')
user_id = resource.prop('user_id')
volume = resource.prop('volume', alias='counter_volume')
def __repr__(self):
return "sample: %s" % self._attrs
@classmethod
def list(cls, session, path_args=None, **params):
url = cls.base_path % path_args
resp = session.get(url, service=cls.service, params=params)
changes = []
for item in resp.body:
changes.append(cls.existing(**item))
return changes
def create(self, session):
url = self.base_path % {'meter': self.meter}
# telemetry expects a list of samples
resp = session.post(url, service=self.service, json=[self._attrs])
sample = self.existing(**resp.body.pop())
self._attrs['id'] = sample.id
self._reset_dirty()

View File

@@ -0,0 +1,138 @@
# 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 mock
import testtools
from openstack.telemetry.v2 import sample
SAMPLE = {
'id': None,
'metadata': {'1': 'one'},
'meter': '2',
'project_id': '3',
'recorded_at': '4',
'resource_id': '5',
'source': '6',
'timestamp': '7',
'type': '8',
'unit': '9',
'user_id': '10',
'volume': '11.1',
}
OLD_SAMPLE = {
'counter_name': '1',
'counter_type': '2',
'counter_unit': '3',
'counter_volume': '4',
'message_id': None,
'project_id': '5',
'recorded_at': '6',
'resource_id': '7',
'resource_metadata': '8',
'source': '9',
'timestamp': '10',
'user_id': '11',
}
class TestSample(testtools.TestCase):
def test_basic(self):
sot = sample.Sample(SAMPLE)
self.assertIsNone(sot.resource_key)
self.assertIsNone(sot.resources_key)
self.assertEqual('/v2/meters/%(meter)s', sot.base_path)
self.assertEqual('metering', sot.service.service_type)
self.assertTrue(sot.allow_create)
self.assertFalse(sot.allow_retrieve)
self.assertFalse(sot.allow_update)
self.assertFalse(sot.allow_delete)
self.assertTrue(sot.allow_list)
def test_make_new(self):
sot = sample.Sample(SAMPLE)
self.assertIsNone(sot.id)
self.assertEqual(SAMPLE['metadata'], sot.metadata)
self.assertEqual(SAMPLE['meter'], sot.meter)
self.assertEqual(SAMPLE['project_id'], sot.project_id)
self.assertEqual(SAMPLE['recorded_at'], sot.recorded_at)
self.assertEqual(SAMPLE['resource_id'], sot.resource_id)
self.assertIsNone(sot.sample_id)
self.assertEqual(SAMPLE['source'], sot.source)
self.assertEqual(SAMPLE['timestamp'], sot.generated_at)
self.assertEqual(SAMPLE['type'], sot.type)
self.assertEqual(SAMPLE['unit'], sot.unit)
self.assertEqual(SAMPLE['user_id'], sot.user_id)
self.assertEqual(SAMPLE['volume'], sot.volume)
def test_make_old(self):
sot = sample.Sample(OLD_SAMPLE)
self.assertIsNone(sot.id)
self.assertIsNone(sot.sample_id),
self.assertEqual(OLD_SAMPLE['counter_name'], sot.meter)
self.assertEqual(OLD_SAMPLE['counter_type'], sot.type)
self.assertEqual(OLD_SAMPLE['counter_unit'], sot.unit)
self.assertEqual(OLD_SAMPLE['counter_volume'], sot.volume)
self.assertEqual(OLD_SAMPLE['project_id'], sot.project_id)
self.assertEqual(OLD_SAMPLE['recorded_at'], sot.recorded_at)
self.assertEqual(OLD_SAMPLE['resource_id'], sot.resource_id)
self.assertEqual(OLD_SAMPLE['resource_metadata'], sot.metadata)
self.assertEqual(OLD_SAMPLE['source'], sot.source)
self.assertEqual(OLD_SAMPLE['timestamp'], sot.generated_at)
self.assertEqual(OLD_SAMPLE['user_id'], sot.user_id)
def test_list(self):
sess = mock.Mock()
resp = mock.Mock()
resp.body = [SAMPLE, OLD_SAMPLE]
sess.get = mock.Mock(return_value=resp)
path_args = {'meter': 'name_of_meter'}
found = sample.Sample.list(sess, path_args=path_args)
self.assertEqual(2, len(found))
first = found[0]
self.assertIsNone(first.id)
self.assertIsNone(first.sample_id)
self.assertEqual(SAMPLE['metadata'], first.metadata)
self.assertEqual(SAMPLE['meter'], first.meter)
self.assertEqual(SAMPLE['project_id'], first.project_id)
self.assertEqual(SAMPLE['recorded_at'], first.recorded_at)
self.assertEqual(SAMPLE['resource_id'], first.resource_id)
self.assertEqual(SAMPLE['source'], first.source)
self.assertEqual(SAMPLE['timestamp'], first.generated_at)
self.assertEqual(SAMPLE['type'], first.type)
self.assertEqual(SAMPLE['unit'], first.unit)
self.assertEqual(SAMPLE['user_id'], first.user_id)
self.assertEqual(SAMPLE['volume'], first.volume)
def test_create(self):
sess = mock.Mock()
resp = mock.Mock()
resp.body = [SAMPLE]
sess.post = mock.Mock(return_value=resp)
data = {'id': None,
'meter': 'temperature',
'project_id': 'project',
'resource_id': 'resource',
'type': 'gauge',
'unit': 'instance',
'volume': '98.6'}
new_sample = sample.Sample.new(**data)
new_sample.create(sess)
url = '/v2/meters/temperature'
sess.post.assert_called_with(url, service=new_sample.service,
json=[data])
self.assertIsNone(new_sample.id)

View File

@@ -52,6 +52,7 @@ class FakeResource(resource.Resource):
name = resource.prop('name')
first = resource.prop('attr1')
second = resource.prop('attr2')
third = resource.prop('attr3', alias='attr_three')
class ResourceTests(base.TestTransportBase):
@@ -229,6 +230,29 @@ class ResourceTests(base.TestTransportBase):
else:
self.fail("Didn't raise attribute error")
try:
obj.third
except AttributeError:
pass
else:
self.fail("Didn't raise attribute error")
def test_composite_attr_happy(self):
obj = FakeResource.existing(**{'attr3': '3'})
try:
self.assertEqual('3', obj.third)
except AttributeError:
self.fail("third was not found as expected")
def test_composite_attr_fallback(self):
obj = FakeResource.existing(**{'attr_three': '3'})
try:
self.assertEqual('3', obj.third)
except AttributeError:
self.fail("third was not found in fallback as expected")
class FakeResponse:
def __init__(self, response):