diff --git a/cloudkitty/common/config.py b/cloudkitty/common/config.py index e6d7410d..462b1afd 100644 --- a/cloudkitty/common/config.py +++ b/cloudkitty/common/config.py @@ -24,7 +24,7 @@ import cloudkitty.config import cloudkitty.orchestrator import cloudkitty.service import cloudkitty.storage -import cloudkitty.storage.gnocchi +import cloudkitty.storage.hybrid.backends.gnocchi import cloudkitty.tenant_fetcher import cloudkitty.tenant_fetcher.keystone import cloudkitty.utils @@ -55,7 +55,7 @@ _opts = [ ('storage', list(itertools.chain( cloudkitty.storage.storage_opts))), ('storage_gnocchi', list(itertools.chain( - cloudkitty.storage.gnocchi.gnocchi_storage_opts))), + cloudkitty.storage.hybrid.backends.gnocchi.gnocchi_storage_opts))), ('tenant_fetcher', list(itertools.chain( cloudkitty.tenant_fetcher.fetchers_opts))), (None, list(itertools.chain( diff --git a/cloudkitty/storage/__init__.py b/cloudkitty/storage/__init__.py index a2368c70..e2f97aa2 100644 --- a/cloudkitty/storage/__init__.py +++ b/cloudkitty/storage/__init__.py @@ -50,9 +50,6 @@ def get_storage(collector=None): cfg.CONF.storage.backend, invoke_on_load=True, invoke_kwds=storage_args).driver - if cfg.CONF.storage.backend not in ['sqlalchemy', 'hybrid']: - LOG.warning('{} storage backend is deprecated and will be removed ' - 'in a future release.'.format(cfg.CONF.storage.backend)) return backend diff --git a/cloudkitty/storage/gnocchi/__init__.py b/cloudkitty/storage/gnocchi/__init__.py deleted file mode 100644 index 9ed102f1..00000000 --- a/cloudkitty/storage/gnocchi/__init__.py +++ /dev/null @@ -1,415 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 (c) Openstack Foundation -# -# 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. -# -# @author: Sergio Colinas -# -import datetime -import decimal -import json - -import dateutil.parser -from gnocchiclient import client as gclient -from gnocchiclient import exceptions as gexceptions -from keystoneauth1 import loading as ks_loading -from oslo_config import cfg -from oslo_log import log -from oslo_utils import uuidutils -import six - -from cloudkitty import storage -from cloudkitty import utils as ck_utils - -LOG = log.getLogger(__name__) -CONF = cfg.CONF - -METRICS_CONF = ck_utils.get_metrics_conf(CONF.collect.metrics_conf) - -GNOCCHI_STORAGE_OPTS = 'storage_gnocchi' -gnocchi_storage_opts = [ - cfg.StrOpt('interface', - default='internalURL', - help='endpoint url type'), - cfg.StrOpt('archive_policy_name', - default='rating', - help='Gnocchi storage archive policy name.'), - # The archive policy definition MUST include the collect period granularity - cfg.StrOpt('archive_policy_definition', - default='[{"granularity": ' - + six.text_type(METRICS_CONF['period']) + - ', "timespan": "90 days"}, ' - '{"granularity": 86400, "timespan": "360 days"}, ' - '{"granularity": 2592000, "timespan": "1800 days"}]', - help='Gnocchi storage archive policy definition.'), ] -CONF.register_opts(gnocchi_storage_opts, GNOCCHI_STORAGE_OPTS) - -ks_loading.register_session_conf_options( - CONF, - GNOCCHI_STORAGE_OPTS) -ks_loading.register_auth_conf_options( - CONF, - GNOCCHI_STORAGE_OPTS) - -CLOUDKITTY_STATE_RESOURCE = 'cloudkitty_state' -CLOUDKITTY_STATE_METRIC = 'state' - - -class GnocchiStorage(storage.BaseStorage): - """Gnocchi Storage Backend. - - Driver used to add full native support for gnocchi, improving performance - and taking advantage of gnocchi capabilities. - """ - - def __init__(self, **kwargs): - super(GnocchiStorage, self).__init__(**kwargs) - self.auth = ks_loading.load_auth_from_conf_options( - CONF, - GNOCCHI_STORAGE_OPTS) - self.session = ks_loading.load_session_from_conf_options( - CONF, - GNOCCHI_STORAGE_OPTS, - auth=self.auth) - self._conn = gclient.Client( - '1', - session=self.session, - adapter_options={'connect_retries': 3, - 'interface': CONF.storage_gnocchi.interface}) - self._measures = {} - self._archive_policy_name = ( - CONF.storage_gnocchi.archive_policy_name) - self._archive_policy_definition = json.loads( - CONF.storage_gnocchi.archive_policy_definition) - self._period = METRICS_CONF['period'] - if "period" in kwargs: - self._period = kwargs["period"] - - def init(self): - # Creates rating archive-policy if not exists - try: - self._conn.archive_policy.get(self._archive_policy_name) - except gexceptions.ArchivePolicyNotFound: - ck_policy = {} - ck_policy["name"] = self._archive_policy_name - ck_policy["back_window"] = 0 - ck_policy["aggregation_methods"] = ["sum", ] - ck_policy["definition"] = self._archive_policy_definition - self._conn.archive_policy.create(ck_policy) - # Creates state resource if it doesn't exist - try: - self._conn.resource_type.create( - {'name': CLOUDKITTY_STATE_RESOURCE}) - except gexceptions.ResourceAlreadyExists: - pass - - def _get_or_create_resource(self, resource_type, tenant_id): - """Return the id of a resource or create it. - - :param resource_type: The type of the resource. - :type metric_name: str - :param tenant_id: Owner's resource tenant id. - :type metric_name: str - """ - query = {"=": {"project_id": tenant_id}} - resources = self._conn.resource.search( - resource_type=resource_type, - query=query, - limit=1) - if not resources: - # NOTE(sheeprine): We don't have the user id information and we are - # doing rating on a per tenant basis. Put garbage in it - resource = self._conn.resource.create( - resource_type=resource_type, - resource={'id': uuidutils.generate_uuid(), - 'user_id': None, - 'project_id': tenant_id}) - return resource['id'] - return resources[0]['id'] - - def _get_or_create_metric(self, metric_name, resource_id): - """Return the metric id from a metric or create it. - - :param metric_name: The name of the metric. - :type metric_name: str - :param resource_id: Resource id containing the metric. - :type metric_name: str - """ - resource = self._conn.resource.get( - resource_type='generic', - resource_id=resource_id, - history=False) - metric_id = resource["metrics"].get(metric_name) - if not metric_id: - new_metric = {} - new_metric["archive_policy_name"] = self._archive_policy_name - new_metric["name"] = metric_name - new_metric["resource_id"] = resource_id - metric = self._conn.metric.create(new_metric) - metric_id = metric['id'] - return metric_id - - def _pre_commit(self, tenant_id): - measures = self._measures.pop(tenant_id, {}) - self._measures[tenant_id] = dict() - for resource_id, metrics in measures.items(): - total = metrics.pop('total.cost') - total_id = self._get_or_create_metric( - 'total.cost', - resource_id) - # TODO(sheeprine): Find a better way to handle total - total_value = sum([decimal.Decimal(val["value"]) for val in total]) - total_timestamp = max([dateutil.parser.parse(val["timestamp"]) - for val in total]) - self._measures[tenant_id][total_id] = [{ - 'timestamp': total_timestamp.isoformat(), - 'value': six.text_type(total_value)}] - for metric_name, values in metrics.items(): - metric_id = self._get_or_create_metric( - metric_name, - resource_id) - self._measures[tenant_id][metric_id] = values - state_resource_id = self._get_or_create_resource( - CLOUDKITTY_STATE_RESOURCE, - tenant_id) - state_metric_id = self._get_or_create_metric( - CLOUDKITTY_STATE_METRIC, - state_resource_id) - self._measures[tenant_id][state_metric_id] = [{ - 'timestamp': self.usage_start_dt.get(tenant_id).isoformat(), - 'value': 1}] - - def _commit(self, tenant_id): - if tenant_id in self._measures: - self._conn.metric.batch_metrics_measures( - self._measures[tenant_id]) - - def _post_commit(self, tenant_id): - super(GnocchiStorage, self)._post_commit(tenant_id) - if tenant_id in self._measures: - del self._measures[tenant_id] - - def _append_metric(self, resource_id, metric_name, value, tenant_id): - sample = {} - sample["timestamp"] = self.usage_start_dt.get(tenant_id).isoformat() - sample["value"] = six.text_type(value) - measures = self._measures.get(tenant_id) or dict() - if not measures: - self._measures[tenant_id] = measures - metrics = measures.get(resource_id) or dict() - if not metrics: - measures[resource_id] = metrics - metrics[metric_name] = [sample] - - def _dispatch(self, data, tenant_id): - for metric_name, metrics in data.items(): - for item in metrics: - resource_id = item["desc"]["resource_id"] - price = item["rating"]["price"] - self._append_metric( - resource_id, - metric_name + ".cost", - price, - tenant_id) - self._append_metric( - resource_id, - 'total.cost', - price, - tenant_id) - self._has_data[tenant_id] = True - - def set_state(self, state, tenant_id): - state_resource_id = self._get_or_create_resource( - CLOUDKITTY_STATE_RESOURCE, - tenant_id) - state_metric_id = self._get_or_create_metric( - CLOUDKITTY_STATE_METRIC, - state_resource_id) - self._conn.metric.add_measures( - state_metric_id, - [{'timestamp': state.isoformat(), - 'value': 1}]) - - def get_state(self, tenant_id=None): - # Return the last written frame's timestamp. - query = {"=": {"project_id": tenant_id}} if tenant_id else {} - state_resource_id = self._get_or_create_resource( - CLOUDKITTY_STATE_RESOURCE, - tenant_id) - try: - # (aolwas) add "refresh=True" to be sure to get all posted - # measures for this particular metric - r = self._conn.metric.get_measures( - metric=CLOUDKITTY_STATE_METRIC, - resource_id=state_resource_id, - query=query, - aggregation="sum", - limit=1, - granularity=self._period, - needed_overlap=0, - refresh=True) - except gexceptions.MetricNotFound: - return - if len(r) > 0: - # NOTE(lukapeschke) Since version 5.0.0, gnocchiclient returns a - # datetime object instead of a timestamp. This fixture is made - # to ensure compatibility with all versions - try: - # (aolwas) According http://gnocchi.xyz/rest.html#metrics, - # gnocchi always returns measures ordered by timestamp - return ck_utils.dt2ts(dateutil.parser.parse(r[-1][0])) - except TypeError: - return ck_utils.dt2ts(r[-1][0]) - - def get_total(self, begin=None, end=None, tenant_id=None, - service=None, groupby=None): - # Get total rate in timeframe from gnocchi - metric = "total.cost" - if service: - metric = service + ".cost" - # We need to pass a query to force a post in gnocchi client metric - # aggregation, so we use one that always meets - query = {"and": [{">": {"started_at": "1900-01-01T00:00"}}]} - if tenant_id: - query = {"=": {"project_id": tenant_id}} - # TODO(Aaron): need support with groupby - if groupby: - LOG.warning('Now get total with groupby not support ' - 'in gnocchi storage backend') - # TODO(sheeprine): Use server side aggregation - r = self._conn.metric.aggregation(metrics=metric, query=query, - start=begin, stop=end, - aggregation="sum", - granularity=self._period, - needed_overlap=0) - - rate = sum([measure[2] for measure in r]) if len(r) else 0 - # Return a list of dict - totallist = [] - total = dict(begin=begin, end=end, rate=rate) - totallist.append(total) - return totallist - - def get_tenants(self, begin, end): - # We need to pass a query to force a post in gnocchi client metric - # aggregation, so we use one that always meets - query = {'=': {'type': 'cloudkitty_state'}} - r = self._conn.metric.aggregation( - metrics=CLOUDKITTY_STATE_METRIC, - query=query, - start=begin, - stop=end, - aggregation="sum", - granularity=self._period, - needed_overlap=0, - resource_type=CLOUDKITTY_STATE_RESOURCE, - groupby="project_id") - projects = [measures["group"]["project_id"] - for measures in r if len(measures["measures"])] - if len(projects) > 0: - return projects - return [] - - def _get_resource_data(self, res_type, resource_id, begin, end): - # Get resource information from gnocchi - return self._collector.resource_info( - resource_name=res_type, - start=begin, - end=end, - project_id=None) - - def _to_cloudkitty(self, res_type, resource_data, measure): - # NOTE(lukapeschke) Since version 5.0.0, gnocchiclient returns a - # datetime object instead of a timestamp. This fixture is made - # to ensure compatibility with all versions - try: - begin = dateutil.parser.parse(measure[0]) - end = (dateutil.parser.parse(measure[0]) + - datetime.timedelta(seconds=self._period)) - except TypeError: - begin = measure[0] - end = begin + datetime.timedelta(seconds=self._period) - cost = decimal.Decimal(measure[2]) - # Rating informations - rating_dict = {} - rating_dict['price'] = cost - - # Encapsulate informations in a resource dict - res_dict = {} - # TODO(sheeprine): Properly recurse on elements - resource_data = resource_data[0] - res_dict['desc'] = resource_data['desc'] - if "qty" in resource_data["vol"]: - resource_data["vol"]["qty"] = ( - decimal.Decimal(resource_data["vol"]["qty"])) - res_dict['vol'] = resource_data['vol'] - res_dict['rating'] = rating_dict - res_dict['tenant_id'] = resource_data['desc']['project_id'] - - # Add resource to the usage dict - usage_dict = {} - usage_dict[res_type] = [res_dict] - - # Time informations - period_dict = {} - period_dict['begin'] = begin.isoformat() - period_dict['end'] = end.isoformat() - - # Add period to the resource informations - ck_dict = {} - ck_dict['period'] = period_dict - ck_dict['usage'] = usage_dict - return ck_dict - - def get_time_frame(self, begin, end, **filters): - tenant_id = filters.get('tenant_id') - query = dict() - if tenant_id: - query['='] = {'project_id': tenant_id} - else: - # NOTE(sheeprine): Dummy filter to comply with gnocchi - query['!='] = {'project_id': None} - try: - res_map = METRICS_CONF['services_objects'] - except KeyError: - res_map = self._collector.retrieve_mappings - res_type = filters.get('res_type') - resources = [res_type] if res_type else res_map.keys() - ck_res = [] - for resource in resources: - resource_type = res_map[resource] - r = self._conn.metric.aggregation( - metrics=resource + ".cost", - resource_type=resource_type, - query=query, - start=begin, - stop=end, - granularity=self._period, - aggregation="sum", - needed_overlap=0, - groupby=["type", "id"]) - for resource_measures in r: - resource_data = None - for measure in resource_measures["measures"]: - if not resource_data: - resource_data = self._get_resource_data( - res_type=resource, - resource_id=resource_measures["group"]["id"], - begin=begin, - end=end) - ck_res.append( - self._to_cloudkitty( - res_type=resource, - resource_data=resource_data, - measure=measure)) - return ck_res diff --git a/cloudkitty/storage/gnocchi_hybrid/__init__.py b/cloudkitty/storage/gnocchi_hybrid/__init__.py deleted file mode 100644 index 5a121498..00000000 --- a/cloudkitty/storage/gnocchi_hybrid/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2015 Objectif Libre -# -# 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. -# -# @author: Stéphane Albert -# -import decimal - -from oslo_log import log - -from cloudkitty.storage.gnocchi_hybrid import migration -from cloudkitty.storage.gnocchi_hybrid import models -from cloudkitty.storage import sqlalchemy as sql_storage - -LOG = log.getLogger(__name__) - - -class GnocchiHybridStorage(sql_storage.SQLAlchemyStorage): - """Gnocchi Hybrid Storage Backend - - Driver used to add support for gnocchi until the creation of custom - resources is supported in gnocchi. - """ - frame_model = models.HybridRatedDataframe - - @staticmethod - def init(): - migration.upgrade('head') - - def _append_time_frame(self, res_type, frame, tenant_id): - rating_dict = frame.get('rating', {}) - rate = rating_dict.get('price') - if not rate: - rate = decimal.Decimal(0) - if res_type == '_NO_DATA_': - resource_ref = res_type - else: - resource_ref = frame.get('resource_id') - if not resource_ref: - LOG.warning('Trying to store data collected outside of ' - 'gnocchi. This driver can only be used with ' - 'the gnocchi collector. Data not stored!') - return - self.add_time_frame(begin=self.usage_start_dt.get(tenant_id), - end=self.usage_end_dt.get(tenant_id), - tenant_id=tenant_id, - res_type=res_type, - resource_ref=resource_ref, - rate=rate) - - def add_time_frame(self, **kwargs): - """Create a new time frame. - - :param begin: Start of the dataframe. - :param end: End of the dataframe. - :param res_type: Type of the resource. - :param rate: Calculated rate for this dataframe. - :param tenant_id: tenant_id of the dataframe owner. - :param resource_ref: Reference to the gnocchi metric (UUID). - """ - super(GnocchiHybridStorage, self).add_time_frame(**kwargs) diff --git a/cloudkitty/storage/gnocchi_hybrid/alembic/__init__.py b/cloudkitty/storage/gnocchi_hybrid/alembic/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cloudkitty/storage/gnocchi_hybrid/alembic/env.py b/cloudkitty/storage/gnocchi_hybrid/alembic/env.py deleted file mode 100644 index 8aaf88f8..00000000 --- a/cloudkitty/storage/gnocchi_hybrid/alembic/env.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2015 Objectif Libre -# -# 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. -# -# @author: Stéphane Albert -# -from cloudkitty.common.db.alembic import env # noqa -from cloudkitty.storage.gnocchi_hybrid import models - -target_metadata = models.Base.metadata -version_table = 'storage_gnocchi_hybrid_alembic' - - -env.run_migrations_online(target_metadata, version_table) diff --git a/cloudkitty/storage/gnocchi_hybrid/alembic/script.py.mako b/cloudkitty/storage/gnocchi_hybrid/alembic/script.py.mako deleted file mode 100644 index 90568b6a..00000000 --- a/cloudkitty/storage/gnocchi_hybrid/alembic/script.py.mako +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright ${create_date.year} OpenStack Foundation -# -# 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. -# - -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision} -Create Date: ${create_date} - -""" - -# revision identifiers, used by Alembic. -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} - -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -def upgrade(): - ${upgrades if upgrades else "pass"} diff --git a/cloudkitty/storage/gnocchi_hybrid/alembic/versions/4c2f20df7491_initial_migration.py b/cloudkitty/storage/gnocchi_hybrid/alembic/versions/4c2f20df7491_initial_migration.py deleted file mode 100644 index 2a0d4863..00000000 --- a/cloudkitty/storage/gnocchi_hybrid/alembic/versions/4c2f20df7491_initial_migration.py +++ /dev/null @@ -1,42 +0,0 @@ -# -# 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. - -"""Initial migration. - -Revision ID: 4c2f20df7491 -Revises: None -Create Date: 2015-11-18 11:44:09.175326 - -""" - -# revision identifiers, used by Alembic. -revision = '4c2f20df7491' -down_revision = None - -from alembic import op -import sqlalchemy as sa - - -def upgrade(): - op.create_table( - 'ghybrid_dataframes', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('begin', sa.DateTime(), nullable=False), - sa.Column('end', sa.DateTime(), nullable=False), - sa.Column('res_type', sa.String(length=255), nullable=False), - sa.Column('rate', sa.Numeric(precision=20, scale=8), nullable=False), - sa.Column('resource_ref', sa.String(length=32), nullable=False), - sa.Column('tenant_id', sa.String(length=32), nullable=True), - sa.PrimaryKeyConstraint('id'), - mysql_charset='utf8', - mysql_engine='InnoDB') diff --git a/cloudkitty/storage/gnocchi_hybrid/alembic/versions/d39836d70aee_fixed_uuid_length_problem.py b/cloudkitty/storage/gnocchi_hybrid/alembic/versions/d39836d70aee_fixed_uuid_length_problem.py deleted file mode 100644 index ef22ded4..00000000 --- a/cloudkitty/storage/gnocchi_hybrid/alembic/versions/d39836d70aee_fixed_uuid_length_problem.py +++ /dev/null @@ -1,35 +0,0 @@ -# -# 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. - -"""Fixed UUID length problem. - -Revision ID: d39836d70aee -Revises: 4c2f20df7491 -Create Date: 2016-05-11 14:04:10.984006 - -""" - -# revision identifiers, used by Alembic. -revision = 'd39836d70aee' -down_revision = '4c2f20df7491' - -from alembic import op -import sqlalchemy as sa - - -def upgrade(): - with op.batch_alter_table('ghybrid_dataframes') as batch_op: - batch_op.alter_column( - 'resource_ref', - type_=sa.String(36), - existing_type=sa.String(32)) diff --git a/cloudkitty/storage/gnocchi_hybrid/migration.py b/cloudkitty/storage/gnocchi_hybrid/migration.py deleted file mode 100644 index ee72cf5e..00000000 --- a/cloudkitty/storage/gnocchi_hybrid/migration.py +++ /dev/null @@ -1,42 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2015 Objectif Libre -# -# 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. -# -# @author: Stéphane Albert -# -import os - -from cloudkitty.common.db.alembic import migration - -ALEMBIC_REPO = os.path.join(os.path.dirname(__file__), 'alembic') - - -def upgrade(revision): - config = migration.load_alembic_config(ALEMBIC_REPO) - return migration.upgrade(config, revision) - - -def version(): - config = migration.load_alembic_config(ALEMBIC_REPO) - return migration.version(config) - - -def revision(message, autogenerate): - config = migration.load_alembic_config(ALEMBIC_REPO) - return migration.revision(config, message, autogenerate) - - -def stamp(revision): - config = migration.load_alembic_config(ALEMBIC_REPO) - return migration.stamp(config, revision) diff --git a/cloudkitty/storage/gnocchi_hybrid/models.py b/cloudkitty/storage/gnocchi_hybrid/models.py deleted file mode 100644 index 81ffe3a6..00000000 --- a/cloudkitty/storage/gnocchi_hybrid/models.py +++ /dev/null @@ -1,86 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2015 Objectif Libre -# -# 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. -# -# @author: Stéphane Albert -# -from oslo_db.sqlalchemy import models -import sqlalchemy -from sqlalchemy.ext import declarative - -from cloudkitty import utils as ck_utils - -Base = declarative.declarative_base() - - -class HybridRatedDataframe(Base, models.ModelBase): - """A hybrid rated dataframe. - - """ - __table_args__ = {'mysql_charset': "utf8", - 'mysql_engine': "InnoDB"} - __tablename__ = 'ghybrid_dataframes' - - id = sqlalchemy.Column(sqlalchemy.Integer, - primary_key=True) - begin = sqlalchemy.Column(sqlalchemy.DateTime, - nullable=False) - end = sqlalchemy.Column(sqlalchemy.DateTime, - nullable=False) - res_type = sqlalchemy.Column(sqlalchemy.String(255), - nullable=False) - rate = sqlalchemy.Column(sqlalchemy.Numeric(20, 8), - nullable=False) - resource_ref = sqlalchemy.Column(sqlalchemy.String(36), - nullable=False) - tenant_id = sqlalchemy.Column(sqlalchemy.String(32), - nullable=True) - - def to_cloudkitty(self, collector=None): - if not collector: - raise Exception('Gnocchi storage needs a reference ' - 'to the collector.') - # Rating informations - rating_dict = {} - rating_dict['price'] = self.rate - - # Resource information from gnocchi - resource_data = collector.resource_info( - resource_name=self.res_type, - start=ck_utils.dt2ts(self.begin), - end=ck_utils.dt2ts(self.end), - project_id=self.tenant_id) - - # Encapsulate informations in a resource dict - res_dict = {} - resource_data = resource_data[0] - res_dict['desc'] = resource_data['desc'] - res_dict['vol'] = resource_data['vol'] - res_dict['rating'] = rating_dict - res_dict['tenant_id'] = self.tenant_id - - # Add resource to the usage dict - usage_dict = {} - usage_dict[self.res_type] = [res_dict] - - # Time informations - period_dict = {} - period_dict['begin'] = ck_utils.dt2iso(self.begin) - period_dict['end'] = ck_utils.dt2iso(self.end) - - # Add period to the resource informations - ck_dict = {} - ck_dict['period'] = period_dict - ck_dict['usage'] = usage_dict - return ck_dict diff --git a/doc/source/configuration/configuration.rst b/doc/source/configuration/configuration.rst index 7a94e1d2..d7bda9cd 100644 --- a/doc/source/configuration/configuration.rst +++ b/doc/source/configuration/configuration.rst @@ -139,27 +139,28 @@ The following shows the basic configuration items: The tenant named ``service`` is also commonly called ``services`` -It is now time to configure the storage backend. Four storage backends are -available: ``sqlalchemy``, ``hybrid``, ``gnocchihybrid``, and ``gnocchi``. +It is now time to configure the storage backend. Two storage backends are +available: ``sqlalchemy`` and ``hybrid`` (which will soon become the v2 +storage). .. code-block:: ini [storage] - backend = gnocchi + backend = hybrid As you will see in the following example, collector and storage backends sometimes need additional configuration sections. (The tenant fetcher works the -same way, but for now, only Keystone is supported). The section's name has the -following format: ``{backend_name}_{backend_type}`` (``gnocchi_collector`` for -example), except for ``storage_gnocchi``. +same way). The section's name has the following format: +``{backend_name}_{backend_type}`` (``gnocchi_collector`` for example), except +for ``storage_gnocchi``. .. note:: The section name format should become ``{backend_type}_{backend_name}`` for all sections in the future (``storage_gnocchi`` style). -If you want to use the pure gnocchi storage or the hybrid storage with a -gnocchi backend, add the following entry: +If you want to use the hybrid storage with a gnocchi backend, add the following +entry: .. code-block:: ini diff --git a/releasenotes/notes/remove-deprecated-storage-backends-158fbec099846ec7.yaml b/releasenotes/notes/remove-deprecated-storage-backends-158fbec099846ec7.yaml new file mode 100644 index 00000000..16702f44 --- /dev/null +++ b/releasenotes/notes/remove-deprecated-storage-backends-158fbec099846ec7.yaml @@ -0,0 +1,4 @@ +--- +deprecations: + - | + The gnocchi and gnocchihybrid storage backends have been removed. diff --git a/setup.cfg b/setup.cfg index 3168dbef..8c98bef9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -67,8 +67,6 @@ cloudkitty.rating.processors = cloudkitty.storage.backends = sqlalchemy = cloudkitty.storage.sqlalchemy:SQLAlchemyStorage - gnocchihybrid = cloudkitty.storage.gnocchi_hybrid:GnocchiHybridStorage - gnocchi = cloudkitty.storage.gnocchi:GnocchiStorage hybrid = cloudkitty.storage.hybrid:HybridStorage cloudkitty.storage.hybrid.backends =