diff --git a/ceilometer/api/app.py b/ceilometer/api/app.py index e72dd4efc..00982e240 100644 --- a/ceilometer/api/app.py +++ b/ceilometer/api/app.py @@ -66,6 +66,7 @@ def setup_app(pecan_config=None, extra_hooks=None): app_hooks = [hooks.ConfigHook(), hooks.DBHook( storage.get_connection_from_config(cfg.CONF, 'metering'), + storage.get_connection_from_config(cfg.CONF, 'event'), storage.get_connection_from_config(cfg.CONF, 'alarm'),), hooks.PipelineHook(), hooks.TranslationHook()] diff --git a/ceilometer/api/controllers/v2.py b/ceilometer/api/controllers/v2.py index 0dc7ec73c..f952fd0bc 100644 --- a/ceilometer/api/controllers/v2.py +++ b/ceilometer/api/controllers/v2.py @@ -2386,7 +2386,7 @@ class TraitsController(rest.RestController): """ LOG.debug(_("Getting traits for %s") % event_type) return [Trait._convert_storage_trait(t) - for t in pecan.request.storage_conn + for t in pecan.request.event_storage_conn .get_traits(event_type, trait_name)] @requires_admin @@ -2399,7 +2399,7 @@ class TraitsController(rest.RestController): get_trait_name = event_models.Trait.get_name_by_type return [TraitDescription(name=t['name'], type=get_trait_name(t['data_type'])) - for t in pecan.request.storage_conn + for t in pecan.request.event_storage_conn .get_trait_types(event_type)] @@ -2416,7 +2416,7 @@ class EventTypesController(rest.RestController): @wsme_pecan.wsexpose([unicode]) def get_all(self): """Get all event types.""" - return list(pecan.request.storage_conn.get_event_types()) + return list(pecan.request.event_storage_conn.get_event_types()) class EventsController(rest.RestController): @@ -2436,7 +2436,7 @@ class EventsController(rest.RestController): generated=event.generated, traits=event.traits) for event in - pecan.request.storage_conn.get_events(event_filter)] + pecan.request.event_storage_conn.get_events(event_filter)] @requires_admin @wsme_pecan.wsexpose(Event, wtypes.text) @@ -2447,7 +2447,7 @@ class EventsController(rest.RestController): """ event_filter = storage.EventFilter(message_id=message_id) events = [event for event - in pecan.request.storage_conn.get_events(event_filter)] + in pecan.request.event_storage_conn.get_events(event_filter)] if not events: raise EntityNotFound(_("Event"), message_id) @@ -2549,7 +2549,9 @@ class Capabilities(_Base): storage = {wtypes.text: bool} "A flattened dictionary of storage capabilities" alarm_storage = {wtypes.text: bool} - "A flattened dictionary of storage capabilities" + "A flattened dictionary of alarm storage capabilities" + event_storage = {wtypes.text: bool} + "A flattened dictionary of event storage capabilities" @classmethod def sample(cls): @@ -2591,6 +2593,7 @@ class Capabilities(_Base): }), storage=_flatten_capabilities({'production_ready': True}), alarm_storage=_flatten_capabilities({'production_ready': True}), + event_storage=_flatten_capabilities({'production_ready': True}), ) @@ -2607,14 +2610,19 @@ class CapabilitiesController(rest.RestController): # the lack of strict feature parity across storage drivers conn = pecan.request.storage_conn alarm_conn = pecan.request.alarm_storage_conn + event_conn = pecan.request.event_storage_conn driver_capabilities = conn.get_capabilities().copy() driver_capabilities['alarms'] = alarm_conn.get_capabilities()['alarms'] + driver_capabilities['events'] = event_conn.get_capabilities()['events'] driver_perf = conn.get_storage_capabilities() alarm_driver_perf = alarm_conn.get_storage_capabilities() + event_driver_perf = event_conn.get_storage_capabilities() return Capabilities(api=_flatten_capabilities(driver_capabilities), storage=_flatten_capabilities(driver_perf), alarm_storage=_flatten_capabilities( - alarm_driver_perf)) + alarm_driver_perf), + event_storage=_flatten_capabilities( + event_driver_perf)) class V2Controller(object): diff --git a/ceilometer/api/hooks.py b/ceilometer/api/hooks.py index 019b94897..b9d99dde4 100644 --- a/ceilometer/api/hooks.py +++ b/ceilometer/api/hooks.py @@ -36,12 +36,14 @@ class ConfigHook(hooks.PecanHook): class DBHook(hooks.PecanHook): - def __init__(self, storage_connection, alarm_storage_connection): - self.storage_connection = storage_connection - self.alarm_storage_connection = alarm_storage_connection + def __init__(self, conn, event_conn, alarm_conn): + self.storage_connection = conn + self.event_storage_connection = event_conn + self.alarm_storage_connection = alarm_conn def before(self, state): state.request.storage_conn = self.storage_connection + state.request.event_storage_conn = self.event_storage_connection state.request.alarm_storage_conn = self.alarm_storage_connection diff --git a/ceilometer/cmd/storage.py b/ceilometer/cmd/storage.py index 766d3c222..f7f95ef51 100644 --- a/ceilometer/cmd/storage.py +++ b/ceilometer/cmd/storage.py @@ -31,6 +31,7 @@ def dbsync(): service.prepare_service() storage.get_connection_from_config(cfg.CONF, 'metering').upgrade() storage.get_connection_from_config(cfg.CONF, 'alarm').upgrade() + storage.get_connection_from_config(cfg.CONF, 'event').upgrade() def expirer(): diff --git a/ceilometer/event/storage/base.py b/ceilometer/event/storage/base.py new file mode 100644 index 000000000..2aff29834 --- /dev/null +++ b/ceilometer/event/storage/base.py @@ -0,0 +1,90 @@ +# +# 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 ceilometer + + +class Connection(object): + """Base class for event storage system connections.""" + + # A dictionary representing the capabilities of this driver. + CAPABILITIES = { + 'events': {'query': {'simple': False}}, + } + + STORAGE_CAPABILITIES = { + 'storage': {'production_ready': False}, + } + + def __init__(self, url): + """Constructor.""" + pass + + @staticmethod + def upgrade(): + """Migrate the database to `version` or the most recent version.""" + + @staticmethod + def clear(): + """Clear database.""" + + @staticmethod + def record_events(events): + """Write the events to the backend storage system. + + :param events: a list of model.Event objects. + """ + raise ceilometer.NotImplementedError('Events not implemented.') + + @staticmethod + def get_events(event_filter): + """Return an iterable of model.Event objects.""" + raise ceilometer.NotImplementedError('Events not implemented.') + + @staticmethod + def get_event_types(): + """Return all event types as an iterable of strings.""" + raise ceilometer.NotImplementedError('Events not implemented.') + + @staticmethod + def get_trait_types(event_type): + """Return a dictionary containing the name and data type of the trait. + + Only trait types for the provided event_type are + returned. + :param event_type: the type of the Event + """ + raise ceilometer.NotImplementedError('Events not implemented.') + + @staticmethod + def get_traits(event_type, trait_type=None): + """Return all trait instances associated with an event_type. + + If trait_type is specified, only return instances of that trait type. + :param event_type: the type of the Event to filter by + :param trait_type: the name of the Trait to filter by + """ + + raise ceilometer.NotImplementedError('Events not implemented.') + + @classmethod + def get_capabilities(cls): + """Return an dictionary with the capabilities of each driver.""" + return cls.CAPABILITIES + + @classmethod + def get_storage_capabilities(cls): + """Return a dictionary representing the performance capabilities. + + This is needed to evaluate the performance of each driver. + """ + return cls.STORAGE_CAPABILITIES diff --git a/ceilometer/event/storage/impl_db2.py b/ceilometer/event/storage/impl_db2.py new file mode 100644 index 000000000..3e0d31fb9 --- /dev/null +++ b/ceilometer/event/storage/impl_db2.py @@ -0,0 +1,20 @@ +# +# 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. +"""DB2 storage backend +""" +from ceilometer.event.storage import pymongo_base +from ceilometer.storage import impl_db2 + + +class Connection(impl_db2.Connection, pymongo_base.Connection): + """The db2 event storage for Ceilometer.""" diff --git a/ceilometer/event/storage/impl_hbase.py b/ceilometer/event/storage/impl_hbase.py new file mode 100644 index 000000000..5ebf48669 --- /dev/null +++ b/ceilometer/event/storage/impl_hbase.py @@ -0,0 +1,83 @@ +# +# 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. +"""HBase storage backend +""" +from ceilometer.openstack.common.gettextutils import _ +from ceilometer.openstack.common import log +from ceilometer.storage.hbase import utils as hbase_utils +from ceilometer.storage import impl_hbase as base +from ceilometer import utils + +LOG = log.getLogger(__name__) + + +AVAILABLE_CAPABILITIES = { + 'events': {'query': {'simple': True}}, +} + + +AVAILABLE_STORAGE_CAPABILITIES = { + 'storage': {'production_ready': True}, +} + + +class Connection(base.Connection): + """Put the event data into a HBase database + + Collections: + + - events: + + - row_key: timestamp of event's generation + uuid of event + in format: "%s+%s" % (ts, Event.message_id) + - Column Families: + + f: contains the following qualifiers: + + - event_type: description of event's type + - timestamp: time stamp of event generation + - all traits for this event in format: + + .. code-block:: python + + "%s+%s" % (trait_name, trait_type) + """ + + CAPABILITIES = utils.update_nested(base.Connection.CAPABILITIES, + AVAILABLE_CAPABILITIES) + STORAGE_CAPABILITIES = utils.update_nested( + base.Connection.STORAGE_CAPABILITIES, + AVAILABLE_STORAGE_CAPABILITIES, + ) + _memory_instance = None + + EVENT_TABLE = "event" + + def upgrade(self): + tables = [self.EVENT_TABLE] + column_families = {'f': dict(max_versions=1)} + with self.conn_pool.connection() as conn: + hbase_utils.create_tables(conn, tables, column_families) + + def clear(self): + LOG.debug(_('Dropping HBase schema...')) + with self.conn_pool.connection() as conn: + for table in [self.EVENT_TABLE]: + try: + conn.disable_table(table) + except Exception: + LOG.debug(_('Cannot disable table but ignoring error')) + try: + conn.delete_table(table) + except Exception: + LOG.debug(_('Cannot delete table but ignoring error')) diff --git a/ceilometer/event/storage/impl_log.py b/ceilometer/event/storage/impl_log.py new file mode 100644 index 000000000..2a86b440a --- /dev/null +++ b/ceilometer/event/storage/impl_log.py @@ -0,0 +1,17 @@ +# +# 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.i +from ceilometer.event.storage import base + + +class Connection(base.Connection): + """Log event data.""" diff --git a/ceilometer/event/storage/impl_mongodb.py b/ceilometer/event/storage/impl_mongodb.py new file mode 100644 index 000000000..1b3551e9c --- /dev/null +++ b/ceilometer/event/storage/impl_mongodb.py @@ -0,0 +1,19 @@ +# +# 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. +"""MongoDB storage backend""" +from ceilometer.event.storage import pymongo_base +from ceilometer.storage import impl_mongodb + + +class Connection(impl_mongodb.Connection, pymongo_base.Connection): + """Put the event data into a MongoDB database.""" diff --git a/ceilometer/event/storage/impl_sqlalchemy.py b/ceilometer/event/storage/impl_sqlalchemy.py new file mode 100644 index 000000000..2c432eae6 --- /dev/null +++ b/ceilometer/event/storage/impl_sqlalchemy.py @@ -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. + +"""SQLAlchemy storage backend.""" + +from __future__ import absolute_import +import os + +from oslo.config import cfg +from oslo.db.sqlalchemy import session as db_session + +from ceilometer.storage import impl_sqlalchemy as base +from ceilometer import utils + + +AVAILABLE_CAPABILITIES = { + 'events': {'query': {'simple': True}}, +} + + +AVAILABLE_STORAGE_CAPABILITIES = { + 'storage': {'production_ready': True}, +} + + +class Connection(base.Connection): + """Put the event data into a SQLAlchemy database. + + """ + CAPABILITIES = utils.update_nested(base.Connection.CAPABILITIES, + AVAILABLE_CAPABILITIES) + STORAGE_CAPABILITIES = utils.update_nested( + base.Connection.STORAGE_CAPABILITIES, + AVAILABLE_STORAGE_CAPABILITIES, + ) + + def __init__(self, url): + self._engine_facade = db_session.EngineFacade( + url, + **dict(cfg.CONF.database.items()) + ) + + def upgrade(self): + # NOTE(gordc): to minimise memory, only import migration when needed + from oslo.db.sqlalchemy import migration + path = os.path.join(os.path.abspath(os.path.dirname(__file__)), + '..', '..', 'storage', 'sqlalchemy', + 'migrate_repo') + migration.db_sync(self._engine_facade.get_engine(), path) diff --git a/ceilometer/event/storage/pymongo_base.py b/ceilometer/event/storage/pymongo_base.py new file mode 100644 index 000000000..e7fed7532 --- /dev/null +++ b/ceilometer/event/storage/pymongo_base.py @@ -0,0 +1,38 @@ +# +# 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. +"""Common functions for MongoDB and DB2 backends +""" +from ceilometer.event.storage import base as event_base +from ceilometer.storage import pymongo_base as base +from ceilometer import utils + + +COMMON_AVAILABLE_CAPABILITIES = { + 'events': {'query': {'simple': True}}, +} + + +AVAILABLE_STORAGE_CAPABILITIES = { + 'storage': {'production_ready': True}, +} + + +class Connection(base.Connection, event_base.Connection): + """Base event Connection class for MongoDB and DB2 drivers.""" + CAPABILITIES = utils.update_nested(base.Connection.CAPABILITIES, + COMMON_AVAILABLE_CAPABILITIES) + + STORAGE_CAPABILITIES = utils.update_nested( + base.Connection.STORAGE_CAPABILITIES, + AVAILABLE_STORAGE_CAPABILITIES, + ) diff --git a/ceilometer/storage/__init__.py b/ceilometer/storage/__init__.py index 36d91cc74..cc1861f5d 100644 --- a/ceilometer/storage/__init__.py +++ b/ceilometer/storage/__init__.py @@ -53,6 +53,10 @@ STORAGE_OPTS = [ default=None, help='The connection string used to connect to the alarm ' 'database. (if unset, connection is used)'), + cfg.StrOpt('event_connection', + default=None, + help='The connection string used to connect to the event ' + 'database. (if unset, connection is used)'), ] cfg.CONF.register_opts(STORAGE_OPTS, group='database') diff --git a/ceilometer/tests/api/v2/test_event_scenarios.py b/ceilometer/tests/api/v2/test_event_scenarios.py index 43a3c405c..da7c22aad 100644 --- a/ceilometer/tests/api/v2/test_event_scenarios.py +++ b/ceilometer/tests/api/v2/test_event_scenarios.py @@ -61,7 +61,7 @@ class EventTestBase(v2.FunctionalTest, traits=trait_models)) base += 100 self.trait_time += datetime.timedelta(days=1) - self.conn.record_events(event_models) + self.event_conn.record_events(event_models) class TestEventTypeAPI(EventTestBase): diff --git a/ceilometer/tests/db.py b/ceilometer/tests/db.py index 2cba7f2f7..3eaef98ec 100644 --- a/ceilometer/tests/db.py +++ b/ceilometer/tests/db.py @@ -52,6 +52,8 @@ class MongoDbManager(fixtures.Fixture): self.url, 'ceilometer.metering.storage') self.alarm_connection = storage.get_connection( self.url, 'ceilometer.alarm.storage') + self.event_connection = storage.get_connection( + self.url, 'ceilometer.event.storage') except storage.StorageBadVersion as e: raise testcase.TestSkipped(six.text_type(e)) @@ -74,6 +76,8 @@ class MySQLDbManager(fixtures.Fixture): self.url, 'ceilometer.metering.storage') self.alarm_connection = storage.get_connection( self.url, 'ceilometer.alarm.storage') + self.event_connection = storage.get_connection( + self.url, 'ceilometer.event.storage') @property def url(self): @@ -90,6 +94,8 @@ class HBaseManager(fixtures.Fixture): self.url, 'ceilometer.metering.storage') self.alarm_connection = storage.get_connection( self.url, 'ceilometer.alarm.storage') + self.event_connection = storage.get_connection( + self.url, 'ceilometer.event.storage') # Unique prefix for each test to keep data is distinguished because # all test data is stored in one table data_prefix = str(uuid.uuid4().hex) @@ -129,6 +135,8 @@ class SQLiteManager(fixtures.Fixture): self.url, 'ceilometer.metering.storage') self.alarm_connection = storage.get_connection( self.url, 'ceilometer.alarm.storage') + self.event_connection = storage.get_connection( + self.url, 'ceilometer.event.storage') class TestBase(testscenarios.testcase.WithScenarios, test_base.BaseTestCase): @@ -166,6 +174,9 @@ class TestBase(testscenarios.testcase.WithScenarios, test_base.BaseTestCase): self.alarm_conn = self.db_manager.alarm_connection self.alarm_conn.upgrade() + self.event_conn = self.db_manager.event_connection + self.event_conn.upgrade() + self.useFixture(mockpatch.Patch('ceilometer.storage.get_connection', side_effect=self._get_connection)) @@ -179,6 +190,8 @@ class TestBase(testscenarios.testcase.WithScenarios, test_base.BaseTestCase): ) def tearDown(self): + self.event_conn.clear() + self.event_conn = None self.alarm_conn.clear() self.alarm_conn = None self.conn.clear() @@ -188,6 +201,8 @@ class TestBase(testscenarios.testcase.WithScenarios, test_base.BaseTestCase): def _get_connection(self, url, namespace): if namespace == "ceilometer.alarm.storage": return self.alarm_conn + elif namespace == "ceilometer.event.storage": + return self.event_conn return self.conn def _get_driver_manager(self, engine): diff --git a/ceilometer/tests/storage/test_get_connection.py b/ceilometer/tests/storage/test_get_connection.py index 82bdf2ed0..b1d5172f2 100644 --- a/ceilometer/tests/storage/test_get_connection.py +++ b/ceilometer/tests/storage/test_get_connection.py @@ -21,6 +21,7 @@ from oslotest import base from ceilometer.alarm.storage import impl_log as impl_log_alarm from ceilometer.alarm.storage import impl_sqlalchemy as impl_sqlalchemy_alarm +from ceilometer.event.storage import impl_hbase as impl_hbase_event from ceilometer import storage from ceilometer.storage import impl_log from ceilometer.storage import impl_sqlalchemy @@ -68,6 +69,21 @@ class ConnectionConfigTest(base.BaseTestCase): conn = storage.get_connection_from_config(self.CONF, 'alarm') self.assertIsInstance(conn, impl_sqlalchemy_alarm.Connection) + def test_three_urls(self): + self.CONF.set_override("connection", "log://", group="database") + self.CONF.set_override("alarm_connection", "sqlite://", + group="database") + self.CONF.set_override("event_connection", "hbase://__test__", + group="database") + conn = storage.get_connection_from_config(self.CONF) + self.assertIsInstance(conn, impl_log.Connection) + conn = storage.get_connection_from_config(self.CONF, 'metering') + self.assertIsInstance(conn, impl_log.Connection) + conn = storage.get_connection_from_config(self.CONF, 'alarm') + self.assertIsInstance(conn, impl_sqlalchemy_alarm.Connection) + conn = storage.get_connection_from_config(self.CONF, 'event') + self.assertIsInstance(conn, impl_hbase_event.Connection) + def test_sqlalchemy_driver(self): self.CONF.set_override("connection", "sqlite+pysqlite://", group="database") diff --git a/ceilometer/tests/storage/test_impl_sqlalchemy.py b/ceilometer/tests/storage/test_impl_sqlalchemy.py index a7ee31527..f8863508f 100644 --- a/ceilometer/tests/storage/test_impl_sqlalchemy.py +++ b/ceilometer/tests/storage/test_impl_sqlalchemy.py @@ -52,26 +52,26 @@ class TraitTypeTest(tests_db.TestBase): # Not applicable to other drivers. def test_trait_type_exists(self): - tt1 = self.conn._get_or_create_trait_type("foo", 0) + tt1 = self.event_conn._get_or_create_trait_type("foo", 0) self.assertTrue(tt1.id >= 0) - tt2 = self.conn._get_or_create_trait_type("foo", 0) + tt2 = self.event_conn._get_or_create_trait_type("foo", 0) self.assertEqual(tt2.id, tt1.id) self.assertEqual(tt2.desc, tt1.desc) self.assertEqual(tt2.data_type, tt1.data_type) def test_new_trait_type(self): - tt1 = self.conn._get_or_create_trait_type("foo", 0) + tt1 = self.event_conn._get_or_create_trait_type("foo", 0) self.assertTrue(tt1.id >= 0) - tt2 = self.conn._get_or_create_trait_type("blah", 0) + tt2 = self.event_conn._get_or_create_trait_type("blah", 0) self.assertNotEqual(tt1.id, tt2.id) self.assertNotEqual(tt1.desc, tt2.desc) # Test the method __repr__ returns a string self.assertTrue(repr.repr(tt2)) def test_trait_different_data_type(self): - tt1 = self.conn._get_or_create_trait_type("foo", 0) + tt1 = self.event_conn._get_or_create_trait_type("foo", 0) self.assertTrue(tt1.id >= 0) - tt2 = self.conn._get_or_create_trait_type("foo", 1) + tt2 = self.event_conn._get_or_create_trait_type("foo", 1) self.assertNotEqual(tt1.id, tt2.id) self.assertEqual(tt2.desc, tt1.desc) self.assertNotEqual(tt1.data_type, tt2.data_type) @@ -85,16 +85,16 @@ class EventTypeTest(tests_db.TestBase): # Not applicable to other drivers. def test_event_type_exists(self): - et1 = self.conn._get_or_create_event_type("foo") + et1 = self.event_conn._get_or_create_event_type("foo") self.assertTrue(et1.id >= 0) - et2 = self.conn._get_or_create_event_type("foo") + et2 = self.event_conn._get_or_create_event_type("foo") self.assertEqual(et2.id, et1.id) self.assertEqual(et2.desc, et1.desc) def test_event_type_unique(self): - et1 = self.conn._get_or_create_event_type("foo") + et1 = self.event_conn._get_or_create_event_type("foo") self.assertTrue(et1.id >= 0) - et2 = self.conn._get_or_create_event_type("blah") + et2 = self.event_conn._get_or_create_event_type("blah") self.assertNotEqual(et1.id, et2.id) self.assertNotEqual(et1.desc, et2.desc) # Test the method __repr__ returns a string @@ -109,7 +109,7 @@ class MyException(Exception): class EventTest(tests_db.TestBase): def test_string_traits(self): model = models.Trait("Foo", models.Trait.TEXT_TYPE, "my_text") - trait = self.conn._make_trait(model, None) + trait = self.event_conn._make_trait(model, None) self.assertEqual(models.Trait.TEXT_TYPE, trait.trait_type.data_type) self.assertIsNone(trait.t_float) self.assertIsNone(trait.t_int) @@ -119,7 +119,7 @@ class EventTest(tests_db.TestBase): def test_int_traits(self): model = models.Trait("Foo", models.Trait.INT_TYPE, 100) - trait = self.conn._make_trait(model, None) + trait = self.event_conn._make_trait(model, None) self.assertEqual(models.Trait.INT_TYPE, trait.trait_type.data_type) self.assertIsNone(trait.t_float) self.assertIsNone(trait.t_string) @@ -129,7 +129,7 @@ class EventTest(tests_db.TestBase): def test_float_traits(self): model = models.Trait("Foo", models.Trait.FLOAT_TYPE, 123.456) - trait = self.conn._make_trait(model, None) + trait = self.event_conn._make_trait(model, None) self.assertEqual(models.Trait.FLOAT_TYPE, trait.trait_type.data_type) self.assertIsNone(trait.t_int) self.assertIsNone(trait.t_string) @@ -140,7 +140,7 @@ class EventTest(tests_db.TestBase): def test_datetime_traits(self): now = datetime.datetime.utcnow() model = models.Trait("Foo", models.Trait.DATETIME_TYPE, now) - trait = self.conn._make_trait(model, None) + trait = self.event_conn._make_trait(model, None) self.assertEqual(models.Trait.DATETIME_TYPE, trait.trait_type.data_type) self.assertIsNone(trait.t_int) @@ -154,9 +154,9 @@ class EventTest(tests_db.TestBase): m = [models.Event("1", "Foo", now, []), models.Event("2", "Zoo", now, [])] - with mock.patch.object(self.conn, "_record_event") as mock_save: + with mock.patch.object(self.event_conn, "_record_event") as mock_save: mock_save.side_effect = MyException("Boom") - problem_events = self.conn.record_events(m) + problem_events = self.event_conn.record_events(m) self.assertEqual(2, len(problem_events)) for bad, event in problem_events: self.assertEqual(bad, models.Event.UNKNOWN_PROBLEM) @@ -227,7 +227,7 @@ class CapabilitiesTest(test_base.BaseTestCase): 'stddev': True, 'cardinality': True}} }, - 'events': {'query': {'simple': True}} + 'events': {'query': {'simple': True}}, } actual_capabilities = impl_sqlalchemy.Connection.get_capabilities() diff --git a/ceilometer/tests/storage/test_storage_scenarios.py b/ceilometer/tests/storage/test_storage_scenarios.py index c4bdfb556..e4eae4cc5 100644 --- a/ceilometer/tests/storage/test_storage_scenarios.py +++ b/ceilometer/tests/storage/test_storage_scenarios.py @@ -2723,7 +2723,7 @@ class EventTest(EventTestBase): now = datetime.datetime.utcnow() m = [event_models.Event("1", "Foo", now, None), event_models.Event("1", "Zoo", now, [])] - problem_events = self.conn.record_events(m) + problem_events = self.event_conn.record_events(m) self.assertEqual(1, len(problem_events)) bad = problem_events[0] self.assertEqual(event_models.Event.DUPLICATE, bad[0]) @@ -2753,11 +2753,11 @@ class GetEventTest(EventTestBase): now = now + datetime.timedelta(hours=1) self.end = now - self.conn.record_events(self.event_models) + self.event_conn.record_events(self.event_models) def test_generated_is_datetime(self): event_filter = storage.EventFilter(self.start, self.end) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(6, len(events)) for i, event in enumerate(events): self.assertIsInstance(event.generated, datetime.datetime) @@ -2771,7 +2771,7 @@ class GetEventTest(EventTestBase): def test_simple_get(self): event_filter = storage.EventFilter(self.start, self.end) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(6, len(events)) start_time = None for i, type in enumerate(['Foo', 'Bar', 'Zoo']): @@ -2800,7 +2800,7 @@ class GetEventTest(EventTestBase): } event_filter = storage.EventFilter(self.start, self.end, "Bar") - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(2, len(events)) self.assertEqual(events[0].event_type, "Bar") self.assertEqual(events[1].event_type, "Bar") @@ -2822,7 +2822,7 @@ class GetEventTest(EventTestBase): trait_filters = [{'key': 'trait_B', 'integer': 101}] event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(1, len(events)) self.assertEqual(events[0].event_type, "Bar") self.assertEqual(4, len(events[0].traits)) @@ -2832,38 +2832,38 @@ class GetEventTest(EventTestBase): 'op': 'eq'}] event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(2, len(events)) self.assertEqual("Foo", events[0].event_type) self.assertEqual(4, len(events[0].traits)) trait_filters[0].update({'key': 'trait_A', 'op': 'lt'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(2, len(events)) self.assertEqual("Bar", events[0].event_type) trait_filters[0].update({'key': 'trait_A', 'op': 'le'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(4, len(events)) self.assertEqual("Bar", events[1].event_type) trait_filters[0].update({'key': 'trait_A', 'op': 'ne'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(4, len(events)) self.assertEqual("Zoo", events[3].event_type) trait_filters[0].update({'key': 'trait_A', 'op': 'gt'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(2, len(events)) self.assertEqual("Zoo", events[0].event_type) trait_filters[0].update({'key': 'trait_A', 'op': 'ge'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(4, len(events)) self.assertEqual("Foo", events[2].event_type) @@ -2871,38 +2871,38 @@ class GetEventTest(EventTestBase): trait_filters = [{'key': 'trait_B', 'integer': 101, 'op': 'eq'}] event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(1, len(events)) self.assertEqual("Bar", events[0].event_type) self.assertEqual(4, len(events[0].traits)) trait_filters[0].update({'key': 'trait_B', 'op': 'lt'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(1, len(events)) self.assertEqual("Foo", events[0].event_type) trait_filters[0].update({'key': 'trait_B', 'op': 'le'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(2, len(events)) self.assertEqual("Bar", events[1].event_type) trait_filters[0].update({'key': 'trait_B', 'op': 'ne'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(5, len(events)) self.assertEqual("Zoo", events[4].event_type) trait_filters[0].update({'key': 'trait_B', 'op': 'gt'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(4, len(events)) self.assertEqual("Zoo", events[0].event_type) trait_filters[0].update({'key': 'trait_B', 'op': 'ge'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(5, len(events)) self.assertEqual("Foo", events[2].event_type) @@ -2910,38 +2910,38 @@ class GetEventTest(EventTestBase): trait_filters = [{'key': 'trait_C', 'float': 300.123456, 'op': 'eq'}] event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(1, len(events)) self.assertEqual("Foo", events[0].event_type) self.assertEqual(4, len(events[0].traits)) trait_filters[0].update({'key': 'trait_C', 'op': 'lt'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(3, len(events)) self.assertEqual("Zoo", events[2].event_type) trait_filters[0].update({'key': 'trait_C', 'op': 'le'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(4, len(events)) self.assertEqual("Bar", events[1].event_type) trait_filters[0].update({'key': 'trait_C', 'op': 'ne'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(5, len(events)) self.assertEqual("Zoo", events[2].event_type) trait_filters[0].update({'key': 'trait_C', 'op': 'gt'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(2, len(events)) self.assertEqual("Bar", events[0].event_type) trait_filters[0].update({'key': 'trait_C', 'op': 'ge'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(3, len(events)) self.assertEqual("Zoo", events[2].event_type) @@ -2951,38 +2951,38 @@ class GetEventTest(EventTestBase): 'op': 'eq'}] event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(1, len(events)) self.assertEqual("Zoo", events[0].event_type) self.assertEqual(4, len(events[0].traits)) trait_filters[0].update({'key': 'trait_D', 'op': 'lt'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(2, len(events)) trait_filters[0].update({'key': 'trait_D', 'op': 'le'}) self.assertEqual("Bar", events[1].event_type) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(3, len(events)) self.assertEqual("Bar", events[1].event_type) trait_filters[0].update({'key': 'trait_D', 'op': 'ne'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(5, len(events)) self.assertEqual("Foo", events[2].event_type) trait_filters[0].update({'key': 'trait_D', 'op': 'gt'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(3, len(events)) self.assertEqual("Zoo", events[2].event_type) trait_filters[0].update({'key': 'trait_D', 'op': 'ge'}) event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(4, len(events)) self.assertEqual("Bar", events[2].event_type) @@ -2991,7 +2991,7 @@ class GetEventTest(EventTestBase): {'key': 'trait_A', 'string': 'my_Foo_text'}] event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(1, len(events)) self.assertEqual("Foo", events[0].event_type) self.assertEqual(4, len(events[0].traits)) @@ -3001,12 +3001,12 @@ class GetEventTest(EventTestBase): {'key': 'trait_A', 'string': 'my_Zoo_text'}] event_filter = storage.EventFilter(self.start, self.end, traits_filter=trait_filters) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(0, len(events)) def test_get_event_types(self): event_types = [e for e in - self.conn.get_event_types()] + self.event_conn.get_event_types()] self.assertEqual(3, len(event_types)) self.assertTrue("Bar" in event_types) @@ -3015,7 +3015,7 @@ class GetEventTest(EventTestBase): def test_get_trait_types(self): trait_types = [tt for tt in - self.conn.get_trait_types("Foo")] + self.event_conn.get_trait_types("Foo")] self.assertEqual(4, len(trait_types)) trait_type_names = map(lambda x: x['name'], trait_types) self.assertIn("trait_A", trait_type_names) @@ -3025,11 +3025,11 @@ class GetEventTest(EventTestBase): def test_get_trait_types_unknown_event(self): trait_types = [tt for tt in - self.conn.get_trait_types("Moo")] + self.event_conn.get_trait_types("Moo")] self.assertEqual(0, len(trait_types)) def test_get_traits(self): - traits = self.conn.get_traits("Bar") + traits = self.event_conn.get_traits("Bar") # format results in a way that makes them easier to work with trait_dict = {} for trait in traits: @@ -3046,7 +3046,7 @@ class GetEventTest(EventTestBase): trait_dict["trait_D"]) def test_get_all_traits(self): - traits = self.conn.get_traits("Foo") + traits = self.event_conn.get_traits("Foo") traits = sorted([t for t in traits], key=operator.attrgetter('dtype')) self.assertEqual(8, len(traits)) trait = traits[0] @@ -3056,9 +3056,9 @@ class GetEventTest(EventTestBase): def test_simple_get_event_no_traits(self): new_events = [event_models.Event("id_notraits", "NoTraits", self.start, [])] - bad_events = self.conn.record_events(new_events) + bad_events = self.event_conn.record_events(new_events) event_filter = storage.EventFilter(self.start, self.end, "NoTraits") - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(0, len(bad_events)) self.assertEqual(1, len(events)) self.assertEqual(events[0].message_id, "id_notraits") @@ -3067,7 +3067,7 @@ class GetEventTest(EventTestBase): def test_simple_get_no_filters(self): event_filter = storage.EventFilter(None, None, None) - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(6, len(events)) def test_get_by_message_id(self): @@ -3076,9 +3076,9 @@ class GetEventTest(EventTestBase): self.start, [])] - bad_events = self.conn.record_events(new_events) + bad_events = self.event_conn.record_events(new_events) event_filter = storage.EventFilter(message_id="id_testid") - events = [event for event in self.conn.get_events(event_filter)] + events = [event for event in self.event_conn.get_events(event_filter)] self.assertEqual(0, len(bad_events)) self.assertEqual(1, len(events)) event = events[0] diff --git a/setup.cfg b/setup.cfg index b75e0a21b..a94290073 100644 --- a/setup.cfg +++ b/setup.cfg @@ -193,7 +193,6 @@ ceilometer.poll.central = network.services.firewall = ceilometer.network.services.fwaas:FirewallPollster network.services.firewall.policy = ceilometer.network.services.fwaas:FirewallPolicyPollster - ceilometer.alarm.storage = log = ceilometer.alarm.storage.impl_log:Connection mongodb = ceilometer.alarm.storage.impl_mongodb:Connection @@ -203,6 +202,15 @@ ceilometer.alarm.storage = hbase = ceilometer.alarm.storage.impl_hbase:Connection db2 = ceilometer.alarm.storage.impl_db2:Connection +ceilometer.event.storage = + log = ceilometer.event.storage.impl_log:Connection + mongodb = ceilometer.event.storage.impl_mongodb:Connection + mysql = ceilometer.event.storage.impl_sqlalchemy:Connection + postgresql = ceilometer.event.storage.impl_sqlalchemy:Connection + sqlite = ceilometer.event.storage.impl_sqlalchemy:Connection + hbase = ceilometer.event.storage.impl_hbase:Connection + db2 = ceilometer.event.storage.impl_db2:Connection + ceilometer.metering.storage = log = ceilometer.storage.impl_log:Connection mongodb = ceilometer.storage.impl_mongodb:Connection