Remove gnocchi and gnocchihybrid storage
As announced during the Queens cycle, the gnocchi and gnocchihybrid storage backends have been removed. Change-Id: I7654721cfaf7a48be8789ae4eb6939b4910ec9db Task: 6294 Story: 2001503
This commit is contained in:
parent
acf1978421
commit
0943330153
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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"}
|
|
@ -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')
|
|
@ -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))
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
deprecations:
|
||||
- |
|
||||
The gnocchi and gnocchihybrid storage backends have been removed.
|
|
@ -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 =
|
||||
|
|
Loading…
Reference in New Issue