From 38b6f710cade7a8b384b48abb2fcb4b79fbfaab2 Mon Sep 17 00:00:00 2001 From: Pierre Padrixe Date: Fri, 14 Feb 2014 17:47:53 +0100 Subject: [PATCH] Add sqlalchemy objects for the 'sensor' resource Added sqlalchemy objects for sensor and migration script for alembic Added unit tests for CRUD db operations on sensor objects. Change-Id: I9c2d428d208e8a0a4f4de95b111c026aa92bf118 --- solum/common/exception.py | 8 +++ solum/objects/sensor.py | 28 +++++++++ solum/objects/sqlalchemy/__init__.py | 4 ++ .../versions/3d1c8e21f103_add_sensor.py | 51 ++++++++++++++++ solum/objects/sqlalchemy/sensor.py | 58 +++++++++++++++++++ solum/tests/objects/test_sensor.py | 55 ++++++++++++++++++ 6 files changed, 204 insertions(+) create mode 100644 solum/objects/sensor.py create mode 100644 solum/objects/sqlalchemy/migration/alembic_migrations/versions/3d1c8e21f103_add_sensor.py create mode 100644 solum/objects/sqlalchemy/sensor.py create mode 100644 solum/tests/objects/test_sensor.py diff --git a/solum/common/exception.py b/solum/common/exception.py index 42dcc85d4..8eadfbc26 100644 --- a/solum/common/exception.py +++ b/solum/common/exception.py @@ -160,6 +160,10 @@ class OperationNotFound(NotFound): msg_fmt = _("The operation %(operation_id)s could not be found.") +class SensorNotFound(NotFound): + msg_fmt = _("The sensor %(sensor_id)s could not be found.") + + class ResourceExists(SolumException): msg_fmt = _("The requested resource already exists.") code = 409 @@ -181,6 +185,10 @@ class OperationExists(ResourceExists): msg_fmt = _("This operation already exists.") +class SensorExists(ResourceExists): + msg_fmt = _("This sensor already exists.") + + class NotImplemented(SolumException): msg_fmt = _("The requested operation is not implemented.") code = 501 diff --git a/solum/objects/sensor.py b/solum/objects/sensor.py new file mode 100644 index 000000000..cb1023f9a --- /dev/null +++ b/solum/objects/sensor.py @@ -0,0 +1,28 @@ +# 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 solum.common import exception +from solum.objects import base + + +class Sensor(base.CrudMixin): + # Version 1.0: Initial version + VERSION = '1.0' + + @classmethod + def _raise_not_found(cls, item_id): + """Raise a not found exception.""" + raise exception.SensorNotFound(sensor_id=item_id) + + +class SensorList(list, base.CrudListMixin): + """List of Sensor.""" diff --git a/solum/objects/sqlalchemy/__init__.py b/solum/objects/sqlalchemy/__init__.py index 36a8a2215..f74fe5417 100644 --- a/solum/objects/sqlalchemy/__init__.py +++ b/solum/objects/sqlalchemy/__init__.py @@ -15,9 +15,11 @@ from solum import objects from solum.objects import operation as abstract_operation from solum.objects import plan as abstract_plan +from solum.objects import sensor as abstract_sensor from solum.objects import service as abstract_srvc from solum.objects.sqlalchemy import operation from solum.objects.sqlalchemy import plan +from solum.objects.sqlalchemy import sensor from solum.objects.sqlalchemy import service @@ -30,3 +32,5 @@ def load(): objects.registry.add(abstract_operation.Operation, operation.Operation) objects.registry.add(abstract_operation.OperationList, operation.OperationList) + objects.registry.add(abstract_sensor.Sensor, sensor.Sensor) + objects.registry.add(abstract_sensor.SensorList, sensor.SensorList) diff --git a/solum/objects/sqlalchemy/migration/alembic_migrations/versions/3d1c8e21f103_add_sensor.py b/solum/objects/sqlalchemy/migration/alembic_migrations/versions/3d1c8e21f103_add_sensor.py new file mode 100644 index 000000000..eea490f5d --- /dev/null +++ b/solum/objects/sqlalchemy/migration/alembic_migrations/versions/3d1c8e21f103_add_sensor.py @@ -0,0 +1,51 @@ +# +# 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. + +"""Add sensor + +Revision ID: 3d1c8e21f103 +Revises: a42f578cef8 +Create Date: 2014-02-06 15:15:19.447394 + +""" +from alembic import op +import sqlalchemy as sa + +from solum.openstack.common import timeutils + +# revision identifiers, used by Alembic. +revision = '3d1c8e21f103' +down_revision = '46ffedad6d56' + + +def upgrade(): + op.create_table( + 'sensor', + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('uuid', sa.String(length=36), nullable=False), + sa.Column('created_at', sa.DateTime, default=timeutils.utcnow), + sa.Column('updated_at', sa.DateTime, onupdate=timeutils.utcnow), + sa.Column('project_id', sa.String(length=36)), + sa.Column('user_id', sa.String(length=36)), + sa.Column('name', sa.String(255)), + sa.Column('sensor_type', sa.String(255)), + sa.Column('value', sa.String(255)), + sa.Column('timestamp', sa.DateTime), + sa.Column('description', sa.String(255)), + sa.Column('documentation', sa.String(255)), + sa.Column('target_resource', sa.String(255)), + ) + + +def downgrade(): + op.drop_table('sensor') diff --git a/solum/objects/sqlalchemy/sensor.py b/solum/objects/sqlalchemy/sensor.py new file mode 100644 index 000000000..728c881ab --- /dev/null +++ b/solum/objects/sqlalchemy/sensor.py @@ -0,0 +1,58 @@ +# 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 sqlalchemy + +from solum.common import exception +from solum.objects import sensor as abstract +from solum.objects.sqlalchemy import models as sql +from solum.openstack.common.db.sqlalchemy import session as db_session + + +class Sensor(sql.Base, abstract.Sensor): + """Represent an sensor in sqlalchemy.""" + + __tablename__ = 'sensor' + __table_args__ = sql.table_args() + + id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, + autoincrement=True) + uuid = sqlalchemy.Column(sqlalchemy.String(36)) + project_id = sqlalchemy.Column(sqlalchemy.String(36)) + user_id = sqlalchemy.Column(sqlalchemy.String(36)) + name = sqlalchemy.Column(sqlalchemy.String(255)) + sensor_type = sqlalchemy.Column(sqlalchemy.String(255)) + value = sqlalchemy.Column(sqlalchemy.String(255)) + timestamp = sqlalchemy.Column(sqlalchemy.DateTime), + description = sqlalchemy.Column(sqlalchemy.String(255)) + documentation = sqlalchemy.Column(sqlalchemy.String(255)) + target_resource = sqlalchemy.Column(sqlalchemy.String(255)) + + @classmethod + def _raise_duplicate_object(cls, e, self): + raise exception.SensorExists() + + @classmethod + def get_by_uuid(cls, context, item_uuid): + query = db_session.get_session().query(cls).filter_by(uuid=item_uuid) + result = query.first() + if not result: + cls._raise_not_found(item_uuid) + return result + + +class SensorList(abstract.SensorList): + """Represent a list of sensors in sqlalchemy.""" + + @classmethod + def get_all(cls, context): + return SensorList(sql.model_query(context, Sensor)) diff --git a/solum/tests/objects/test_sensor.py b/solum/tests/objects/test_sensor.py new file mode 100644 index 000000000..25eaa37e5 --- /dev/null +++ b/solum/tests/objects/test_sensor.py @@ -0,0 +1,55 @@ +# 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 solum.objects import registry +from solum.objects.sqlalchemy import sensor +from solum.tests import base +from solum.tests import utils + + +class TestSensor(base.BaseTestCase): + def setUp(self): + super(TestSensor, self).setUp() + self.db = self.useFixture(utils.Database()) + self.ctx = utils.dummy_context() + + self.data = [{'uuid': 'test-uuid-34dsxD', + 'project_id': 'bla', + 'user_id': '55f41cf46df74320b9486a35f5d28a11', + 'name': 'hb', + 'description': 'A heartbeat sensor', + 'documentation': 'http://example.com/docs/heartbeat/', + 'target_resource': 'http://example.com/instances/uuid', + 'sensor_type': 'str', + 'value': '30'}] + utils.create_models_from_data(sensor.Sensor, + self.data, self.ctx) + + def test_objects_registered(self): + self.assertTrue(registry.Sensor) + self.assertTrue(registry.SensorList) + + def test_get_all(self): + lst = sensor.SensorList() + self.assertEqual(1, len(lst.get_all(self.ctx))) + + def test_check_data_by_id(self): + s = sensor.Sensor().get_by_id(self.ctx, self.data[0]['id']) + self.assertIsNotNone(s) + for key, value in self.data[0].items(): + self.assertEqual(value, getattr(s, key)) + + def test_check_data_by_uuid(self): + s = sensor.Sensor().get_by_uuid(self.ctx, self.data[0]['uuid']) + self.assertIsNotNone(s) + for key, value in self.data[0].items(): + self.assertEqual(value, getattr(s, key))