Merge "Fix bug in get_capabilities behavior in DB drivers"

This commit is contained in:
Jenkins 2014-03-17 20:43:12 +00:00 committed by Gerrit Code Review
commit 6bd9038d90
10 changed files with 365 additions and 97 deletions

View File

@ -75,6 +75,24 @@ class DB2Storage(base.StorageEngine):
return Connection(conf)
AVAILABLE_CAPABILITIES = {
'meters': {'query': {'simple': True,
'metadata': True}},
'resources': {'query': {'simple': True,
'metadata': True}},
'samples': {'query': {'simple': True,
'metadata': True,
'complex': True}},
'statistics': {'groupby': True,
'query': {'simple': True,
'metadata': True},
'aggregation': {'standard': True}},
'alarms': {'query': {'simple': True,
'complex': True},
'history': {'query': {'simple': True}}},
}
class Connection(pymongo_base.Connection):
"""DB2 connection.
"""
@ -132,6 +150,9 @@ class Connection(pymongo_base.Connection):
self.db.authenticate(connection_options['username'],
connection_options['password'])
self.CAPABILITIES = utils.update_nested(self.DEFAULT_CAPABILITIES,
AVAILABLE_CAPABILITIES)
self.upgrade()
@classmethod
@ -423,20 +444,4 @@ class Connection(pymongo_base.Connection):
def get_capabilities(self):
"""Return an dictionary representing the capabilities of this driver.
"""
available = {
'meters': {'query': {'simple': True,
'metadata': True}},
'resources': {'query': {'simple': True,
'metadata': True}},
'samples': {'query': {'simple': True,
'metadata': True,
'complex': True}},
'statistics': {'groupby': True,
'query': {'simple': True,
'metadata': True},
'aggregation': {'standard': True}},
'alarms': {'query': {'simple': True,
'complex': True},
'history': {'query': {'simple': True}}},
}
return utils.update_nested(self.DEFAULT_CAPABILITIES, available)
return self.CAPABILITIES

View File

@ -94,6 +94,19 @@ class HBaseStorage(base.StorageEngine):
return Connection(conf)
AVAILABLE_CAPABILITIES = {
'meters': {'query': {'simple': True,
'metadata': True}},
'resources': {'query': {'simple': True,
'metadata': True}},
'samples': {'query': {'simple': True,
'metadata': True}},
'statistics': {'query': {'simple': True,
'metadata': True},
'aggregation': {'standard': True}},
}
class Connection(base.Connection):
"""HBase connection.
"""
@ -128,6 +141,9 @@ class Connection(base.Connection):
self.conn = self._get_connection(opts)
self.conn.open()
self.CAPABILITIES = utils.update_nested(self.DEFAULT_CAPABILITIES,
AVAILABLE_CAPABILITIES)
def upgrade(self):
self.conn.create_table(self.PROJECT_TABLE, {'f': dict()})
self.conn.create_table(self.USER_TABLE, {'f': dict()})
@ -565,18 +581,7 @@ class Connection(base.Connection):
def get_capabilities(self):
"""Return an dictionary representing the capabilities of this driver.
"""
available = {
'meters': {'query': {'simple': True,
'metadata': True}},
'resources': {'query': {'simple': True,
'metadata': True}},
'samples': {'query': {'simple': True,
'metadata': True}},
'statistics': {'query': {'simple': True,
'metadata': True},
'aggregation': {'standard': True}},
}
return utils.update_nested(self.DEFAULT_CAPABILITIES, available)
return self.CAPABILITIES
###############

View File

@ -82,6 +82,34 @@ class MongoDBStorage(base.StorageEngine):
return Connection(conf)
AVAILABLE_CAPABILITIES = {
'meters': {'query': {'simple': True,
'metadata': True}},
'resources': {'query': {'simple': True,
'metadata': True}},
'samples': {'query': {'simple': True,
'metadata': True,
'complex': True}},
'statistics': {'groupby': True,
'query': {'simple': True,
'metadata': True},
'aggregation': {'standard': True,
'selectable': {
'max': True,
'min': True,
'sum': True,
'avg': True,
'count': True,
'stddev': True,
'cardinality': True}}
},
'alarms': {'query': {'simple': True,
'complex': True},
'history': {'query': {'simple': True,
'complex': True}}},
}
class Connection(pymongo_base.Connection):
"""MongoDB connection.
"""
@ -409,6 +437,9 @@ class Connection(pymongo_base.Connection):
self.db.authenticate(connection_options['username'],
connection_options['password'])
self.CAPABILITIES = utils.update_nested(self.DEFAULT_CAPABILITIES,
AVAILABLE_CAPABILITIES)
# NOTE(jd) Upgrading is just about creating index, so let's do this
# on connection to be sure at least the TTL is correcly updated if
# needed.
@ -956,30 +987,4 @@ class Connection(pymongo_base.Connection):
def get_capabilities(self):
"""Return an dictionary representing the capabilities of this driver.
"""
available = {
'meters': {'query': {'simple': True,
'metadata': True}},
'resources': {'query': {'simple': True,
'metadata': True}},
'samples': {'query': {'simple': True,
'metadata': True,
'complex': True}},
'statistics': {'groupby': True,
'query': {'simple': True,
'metadata': True},
'aggregation': {'standard': True,
'selectable': {
'max': True,
'min': True,
'sum': True,
'avg': True,
'count': True,
'stddev': True,
'cardinality': True}}
},
'alarms': {'query': {'simple': True,
'complex': True},
'history': {'query': {'simple': True,
'complex': True}}},
}
return utils.update_nested(self.DEFAULT_CAPABILITIES, available)
return self.CAPABILITIES

View File

@ -133,6 +133,36 @@ PARAMETERIZED_AGGREGATES = dict(
)
)
AVAILABLE_CAPABILITIES = {
'meters': {'query': {'simple': True,
'metadata': True}},
'resources': {'query': {'simple': True,
'metadata': True}},
'samples': {'pagination': True,
'groupby': True,
'query': {'simple': True,
'metadata': True,
'complex': True}},
'statistics': {'groupby': True,
'query': {'simple': True,
'metadata': True},
'aggregation': {'standard': True,
'selectable': {
'max': True,
'min': True,
'sum': True,
'avg': True,
'count': True,
'stddev': True,
'cardinality': True}}
},
'alarms': {'query': {'simple': True,
'complex': True},
'history': {'query': {'simple': True,
'complex': True}}},
'events': {'query': {'simple': True}},
}
def apply_metaquery_filter(session, query, metaquery):
"""Apply provided metaquery filter to existing query.
@ -223,6 +253,8 @@ class Connection(base.Connection):
self._maker = sqlalchemy_session.get_maker(self._engine)
sqlalchemy_session._ENGINE = None
sqlalchemy_session._MAKER = None
self._CAPABILITIES = utils.update_nested(self.DEFAULT_CAPABILITIES,
AVAILABLE_CAPABILITIES)
def _get_db_session(self):
return self._maker()
@ -1262,6 +1294,11 @@ class Connection(base.Connection):
dtype=type.data_type,
value=trait.get_value())
def get_capabilities(self):
"""Return an dictionary representing the capabilities of this driver.
"""
return self._CAPABILITIES
class QueryTransformer(object):
operators = {"=": operator.eq,
@ -1349,37 +1386,3 @@ class QueryTransformer(object):
def get_query(self):
return self.query
def get_capabilities(self):
"""Return an dictionary representing the capabilities of this driver.
"""
available = {
'meters': {'query': {'simple': True,
'metadata': True}},
'resources': {'query': {'simple': True,
'metadata': True}},
'samples': {'pagination': True,
'groupby': True,
'query': {'simple': True,
'metadata': True,
'complex': True}},
'statistics': {'groupby': True,
'query': {'simple': True,
'metadata': True},
'aggregation': {'standard': True,
'selectable': {
'max': True,
'min': True,
'sum': True,
'avg': True,
'count': True,
'stddev': True,
'cardinality': True}}
},
'alarms': {'query': {'simple': True,
'complex': True},
'history': {'query': {'simple': True,
'complex': True}}},
'events': {'query': {'simple': True}},
}
return utils.update_nested(self.DEFAULT_CAPABILITIES, available)

View File

@ -0,0 +1,37 @@
# -*- encoding: utf-8 -*-
#
# Copyright Ericsson AB 2014. All rights reserved
#
# Authors: Ildiko Vancsa <ildiko.vancsa@ericsson.com>
#
# 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 testscenarios
from ceilometer.tests.api import v2 as tests_api
from ceilometer.tests import db as tests_db
load_tests = testscenarios.load_tests_apply_scenarios
class TestCapabilitiesController(tests_api.FunctionalTest,
tests_db.MixinTestsWithBackendScenarios):
def setUp(self):
super(TestCapabilitiesController, self).setUp()
self.url = '/capabilities'
def test_capabilities(self):
data = self.get_json(self.url)
self.assertIsNotNone(data)
self.assertNotEqual({}, data)

View File

@ -0,0 +1,76 @@
# -*- encoding: utf-8 -*-
#
# Copyright Ericsson AB 2014. All rights reserved
#
# Authors: Ildiko Vancsa <ildiko.vancsa@ericsson.com>
#
# 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.
"""Tests for ceilometer/storage/impl_db2.py
.. note::
In order to run the tests against another MongoDB server set the
environment variable CEILOMETER_TEST_DB2_URL to point to a DB2
server before running the tests.
"""
from ceilometer.tests import db as tests_db
class DB2EngineTestBase(tests_db.TestBase):
database_connection = tests_db.DB2FakeConnectionUrl()
class CapabilitiesTest(DB2EngineTestBase):
# Check the returned capabilities list, which is specific to each DB
# driver
def test_capabilities(self):
expected_capabilities = {
'meters': {'pagination': False,
'query': {'simple': True,
'metadata': True,
'complex': False}},
'resources': {'pagination': False,
'query': {'simple': True,
'metadata': True,
'complex': False}},
'samples': {'pagination': False,
'groupby': False,
'query': {'simple': True,
'metadata': True,
'complex': True}},
'statistics': {'pagination': False,
'groupby': True,
'query': {'simple': True,
'metadata': True,
'complex': False},
'aggregation': {'standard': True,
'selectable': {
'max': False,
'min': False,
'sum': False,
'avg': False,
'count': False,
'stddev': False,
'cardinality': False}}
},
'alarms': {'query': {'simple': True,
'complex': True},
'history': {'query': {'simple': True,
'complex': False}}},
'events': {'query': {'simple': False}}
}
actual_capabilities = self.conn.get_capabilities()
self.assertEqual(expected_capabilities, actual_capabilities)

View File

@ -56,3 +56,48 @@ class ConnectionTest(HBaseEngineTestBase):
side_effect=get_connection):
conn = hbase.Connection(self.CONF)
self.assertIsInstance(conn.conn, TestConn)
class CapabilitiesTest(HBaseEngineTestBase):
# Check the returned capabilities list, which is specific to each DB
# driver
def test_capabilities(self):
expected_capabilities = {
'meters': {'pagination': False,
'query': {'simple': True,
'metadata': True,
'complex': False}},
'resources': {'pagination': False,
'query': {'simple': True,
'metadata': True,
'complex': False}},
'samples': {'pagination': False,
'groupby': False,
'query': {'simple': True,
'metadata': True,
'complex': False}},
'statistics': {'pagination': False,
'groupby': False,
'query': {'simple': True,
'metadata': True,
'complex': False},
'aggregation': {'standard': True,
'selectable': {
'max': False,
'min': False,
'sum': False,
'avg': False,
'count': False,
'stddev': False,
'cardinality': False}}
},
'alarms': {'query': {'simple': False,
'complex': False},
'history': {'query': {'simple': False,
'complex': False}}},
'events': {'query': {'simple': False}}
}
actual_capabilities = self.conn.get_capabilities()
self.assertEqual(expected_capabilities, actual_capabilities)

View File

@ -144,3 +144,48 @@ class AlarmTestPagination(test_storage_scenarios.AlarmTestBase,
'counter-name-foo')
except base.MultipleResultsFound:
self.assertTrue(True)
class CapabilitiesTest(MongoDBEngineTestBase):
# Check the returned capabilities list, which is specific to each DB
# driver
def test_capabilities(self):
expected_capabilities = {
'meters': {'pagination': False,
'query': {'simple': True,
'metadata': True,
'complex': False}},
'resources': {'pagination': False,
'query': {'simple': True,
'metadata': True,
'complex': False}},
'samples': {'pagination': False,
'groupby': False,
'query': {'simple': True,
'metadata': True,
'complex': True}},
'statistics': {'pagination': False,
'groupby': True,
'query': {'simple': True,
'metadata': True,
'complex': False},
'aggregation': {'standard': True,
'selectable': {
'max': True,
'min': True,
'sum': True,
'avg': True,
'count': True,
'stddev': True,
'cardinality': True}}
},
'alarms': {'query': {'simple': True,
'complex': True},
'history': {'query': {'simple': True,
'complex': True}}},
'events': {'query': {'simple': False}}
}
actual_capabilities = self.conn.get_capabilities()
self.assertEqual(expected_capabilities, actual_capabilities)

View File

@ -222,3 +222,48 @@ class RelationshipTest(scenarios.DBTestBase):
session.query(sql_models.User.id)
.group_by(sql_models.User.id)
)).count(), 0)
class CapabilitiesTest(EventTestBase):
# Check the returned capabilities list, which is specific to each DB
# driver
def test_capabilities(self):
expected_capabilities = {
'meters': {'pagination': False,
'query': {'simple': True,
'metadata': True,
'complex': False}},
'resources': {'pagination': False,
'query': {'simple': True,
'metadata': True,
'complex': False}},
'samples': {'pagination': True,
'groupby': True,
'query': {'simple': True,
'metadata': True,
'complex': True}},
'statistics': {'pagination': False,
'groupby': True,
'query': {'simple': True,
'metadata': True,
'complex': False},
'aggregation': {'standard': True,
'selectable': {
'max': True,
'min': True,
'sum': True,
'avg': True,
'count': True,
'stddev': True,
'cardinality': True}}
},
'alarms': {'query': {'simple': True,
'complex': True},
'history': {'query': {'simple': True,
'complex': True}}},
'events': {'query': {'simple': True}}
}
actual_capabilities = self.conn.get_capabilities()
self.assertEqual(expected_capabilities, actual_capabilities)

View File

@ -19,6 +19,7 @@
"""Utilities and helper functions."""
import calendar
import copy
import datetime
import decimal
@ -134,14 +135,15 @@ def lowercase_values(mapping):
mapping[key] = value.lower()
def update_nested(d, u):
def update_nested(original_dict, updates):
"""Updates the leaf nodes in a nest dict, without replacing
entire sub-dicts.
"""
for k, v in u.iteritems():
if isinstance(v, dict):
r = update_nested(d.get(k, {}), v)
d[k] = r
dict_to_update = copy.deepcopy(original_dict)
for key, value in updates.iteritems():
if isinstance(value, dict):
sub_dict = update_nested(dict_to_update.get(key, {}), value)
dict_to_update[key] = sub_dict
else:
d[k] = u[k]
return d
dict_to_update[key] = updates[key]
return dict_to_update