202 lines
6.9 KiB
Python
202 lines
6.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2014 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.
|
|
#
|
|
import decimal
|
|
|
|
from oslo_db.sqlalchemy import utils
|
|
import sqlalchemy
|
|
|
|
from cloudkitty import db
|
|
from cloudkitty.storage import NoTimeFrame
|
|
from cloudkitty.storage import v1 as storage
|
|
from cloudkitty.storage.v1.sqlalchemy import migration
|
|
from cloudkitty.storage.v1.sqlalchemy import models
|
|
from cloudkitty import utils as ck_utils
|
|
from cloudkitty.utils import json
|
|
|
|
|
|
class SQLAlchemyStorage(storage.BaseStorage):
|
|
"""SQLAlchemy Storage Backend
|
|
|
|
"""
|
|
frame_model = models.RatedDataFrame
|
|
|
|
def __init__(self, **kwargs):
|
|
super(SQLAlchemyStorage, self).__init__(**kwargs)
|
|
self._session = {}
|
|
|
|
@staticmethod
|
|
def init():
|
|
migration.upgrade('head')
|
|
|
|
def _pre_commit(self, tenant_id):
|
|
self._check_session(tenant_id)
|
|
if not self._has_data.get(tenant_id):
|
|
empty_frame = {'vol': {'qty': 0, 'unit': 'None'},
|
|
'rating': {'price': 0}, 'desc': ''}
|
|
self._append_time_frame('_NO_DATA_', empty_frame, tenant_id)
|
|
|
|
def _commit(self, tenant_id):
|
|
self._session[tenant_id].commit()
|
|
|
|
def _post_commit(self, tenant_id):
|
|
super(SQLAlchemyStorage, self)._post_commit(tenant_id)
|
|
del self._session[tenant_id]
|
|
|
|
def _check_session(self, tenant_id):
|
|
session = self._session.get(tenant_id)
|
|
if not session:
|
|
self._session[tenant_id] = db.get_session()
|
|
self._session[tenant_id].begin()
|
|
|
|
def _dispatch(self, data, tenant_id):
|
|
self._check_session(tenant_id)
|
|
for service in data:
|
|
for frame in data[service]:
|
|
self._append_time_frame(service, frame, tenant_id)
|
|
self._has_data[tenant_id] = True
|
|
|
|
def get_state(self, tenant_id=None):
|
|
session = db.get_session()
|
|
q = utils.model_query(
|
|
self.frame_model,
|
|
session)
|
|
if tenant_id:
|
|
q = q.filter(
|
|
self.frame_model.tenant_id == tenant_id)
|
|
q = q.order_by(
|
|
self.frame_model.begin.desc())
|
|
r = q.first()
|
|
if r:
|
|
return r.begin
|
|
|
|
def get_total(self, begin=None, end=None, tenant_id=None, service=None,
|
|
groupby=None):
|
|
session = db.get_session()
|
|
querymodels = [
|
|
sqlalchemy.func.sum(self.frame_model.rate).label('rate')
|
|
]
|
|
|
|
if not begin:
|
|
begin = ck_utils.get_month_start_timestamp()
|
|
if not end:
|
|
end = ck_utils.get_next_month_timestamp()
|
|
# Boundary calculation
|
|
if tenant_id:
|
|
querymodels.append(self.frame_model.tenant_id)
|
|
if service:
|
|
querymodels.append(self.frame_model.res_type)
|
|
if groupby:
|
|
groupbyfields = groupby.split(",")
|
|
for field in groupbyfields:
|
|
field_obj = self.frame_model.__dict__.get(field, None)
|
|
if field_obj and field_obj not in querymodels:
|
|
querymodels.append(field_obj)
|
|
|
|
q = session.query(*querymodels)
|
|
if tenant_id:
|
|
q = q.filter(
|
|
self.frame_model.tenant_id == tenant_id)
|
|
if service:
|
|
q = q.filter(
|
|
self.frame_model.res_type == service)
|
|
q = q.filter(
|
|
self.frame_model.begin >= begin,
|
|
self.frame_model.end <= end,
|
|
self.frame_model.res_type != '_NO_DATA_')
|
|
if groupby:
|
|
q = q.group_by(sqlalchemy.sql.text(groupby))
|
|
|
|
# Order by sum(rate)
|
|
q = q.order_by(sqlalchemy.func.sum(self.frame_model.rate))
|
|
results = q.all()
|
|
totallist = []
|
|
for r in results:
|
|
total = {model.name: value for model, value in zip(querymodels, r)}
|
|
total["begin"] = begin
|
|
total["end"] = end
|
|
totallist.append(total)
|
|
|
|
return totallist
|
|
|
|
def get_tenants(self, begin, end):
|
|
session = db.get_session()
|
|
q = utils.model_query(
|
|
self.frame_model,
|
|
session)
|
|
q = q.filter(
|
|
self.frame_model.begin >= begin,
|
|
self.frame_model.end <= end)
|
|
tenants = q.distinct().values(
|
|
self.frame_model.tenant_id)
|
|
return [tenant.tenant_id for tenant in tenants]
|
|
|
|
def get_time_frame(self, begin, end, **filters):
|
|
if not begin:
|
|
begin = ck_utils.get_month_start()
|
|
if not end:
|
|
end = ck_utils.get_next_month()
|
|
session = db.get_session()
|
|
q = utils.model_query(
|
|
self.frame_model,
|
|
session)
|
|
q = q.filter(
|
|
self.frame_model.begin >= begin,
|
|
self.frame_model.end <= end)
|
|
for filter_name, filter_value in filters.items():
|
|
if filter_value:
|
|
q = q.filter(
|
|
getattr(self.frame_model, filter_name) == filter_value)
|
|
if not filters.get('res_type'):
|
|
q = q.filter(self.frame_model.res_type != '_NO_DATA_')
|
|
count = q.count()
|
|
if not count:
|
|
raise NoTimeFrame()
|
|
r = q.all()
|
|
return [entry.to_cloudkitty(self._collector) for entry in r]
|
|
|
|
def _append_time_frame(self, res_type, frame, tenant_id):
|
|
vol_dict = frame['vol']
|
|
qty = vol_dict['qty']
|
|
unit = vol_dict['unit']
|
|
rating_dict = frame.get('rating', {})
|
|
rate = rating_dict.get('price')
|
|
if not rate:
|
|
rate = decimal.Decimal(0)
|
|
desc = json.dumps(frame['desc'])
|
|
self.add_time_frame(begin=self.usage_start_dt.get(tenant_id),
|
|
end=self.usage_end_dt.get(tenant_id),
|
|
tenant_id=tenant_id,
|
|
unit=unit,
|
|
qty=qty,
|
|
res_type=res_type,
|
|
rate=rate,
|
|
desc=desc)
|
|
|
|
def add_time_frame(self, **kwargs):
|
|
"""Create a new time frame.
|
|
|
|
:param begin: Start of the dataframe.
|
|
:param end: End of the dataframe.
|
|
:param tenant_id: tenant_id of the dataframe owner.
|
|
:param unit: Unit of the metric.
|
|
:param qty: Quantity of the metric.
|
|
:param res_type: Type of the resource.
|
|
:param rate: Calculated rate for this dataframe.
|
|
:param desc: Resource description (metadata).
|
|
"""
|
|
frame = self.frame_model(**kwargs)
|
|
self._session[kwargs.get('tenant_id')].add(frame)
|