Added support for total on API

Added a generic function to load storage backend
Added storage hooks to API
Added total calculation on backends
Moved NoTimeFrame exception to base code

Change-Id: I917335aaeb01b58c1cbb86ae63b260d3f91a7ba7
This commit is contained in:
Stéphane Albert 2014-11-07 17:24:03 +01:00
parent f41fd4c6d7
commit 734c2fae8d
5 changed files with 65 additions and 19 deletions

View File

@ -28,6 +28,7 @@ from cloudkitty.api import hooks
from cloudkitty.common import rpc
from cloudkitty import config # noqa
from cloudkitty.openstack.common import log as logging
from cloudkitty import storage
LOG = logging.getLogger(__name__)
@ -70,8 +71,11 @@ def setup_app(pecan_config=None, extra_hooks=None):
client = rpc.get_client(target)
storage_backend = storage.get_storage()
app_hooks = [
hooks.RPCHook(client)
hooks.RPCHook(client),
hooks.StorageHook(storage_backend),
]
return pecan.make_app(

View File

@ -250,8 +250,12 @@ class ReportController(rest.RestController):
"""Return the amount to pay for the current month.
"""
# TODO(sheeprine): Get current total from DB
return 10.0
storage = pecan.request.storage_backend
# FIXME(sheeprine): We should filter on user id.
# Use keystone token information by default but make it overridable and
# enforce it by policy engine
total = storage.get_total()
return total
class V1Controller(rest.RestController):

View File

@ -24,3 +24,11 @@ class RPCHook(hooks.PecanHook):
def before(self, state):
state.request.rpc_client = self._rpc_client
class StorageHook(hooks.PecanHook):
def __init__(self, storage_backend):
self._storage_backend = storage_backend
def before(self, state):
state.request.storage_backend = self._storage_backend

View File

@ -20,8 +20,9 @@ import datetime
from oslo.config import cfg
import six
from stevedore import driver
STORAGES_NAMESPACE = 'cloudkitty.storage.backends'
storage_opts = [
cfg.StrOpt('backend',
default='sqlalchemy',
@ -31,6 +32,25 @@ storage_opts = [
cfg.CONF.register_opts(storage_opts, group='storage')
def get_storage():
cfg.CONF.import_opt('period', 'cloudkitty.config', 'collect')
storage_args = {'period': cfg.CONF.collect.period}
backend = driver.DriverManager(
STORAGES_NAMESPACE,
cfg.CONF.storage.backend,
invoke_on_load=True,
invoke_kwds=storage_args).driver
return backend
class NoTimeFrame(Exception):
"""Raised when there is no time frame available."""
def __init__(self):
super(NoTimeFrame, self).__init__(
"No time frame available")
@six.add_metaclass(abc.ABCMeta)
class BaseStorage(object):
"""Base Storage class:
@ -103,7 +123,9 @@ class BaseStorage(object):
@abc.abstractmethod
def get_total(self):
pass
"""Return the current total.
"""
@abc.abstractmethod
def get_time_frame(self, begin, end, **filters):

View File

@ -15,9 +15,11 @@
#
# @author: Stéphane Albert
#
import datetime
import json
from oslo.db.sqlalchemy import utils
import sqlalchemy
from cloudkitty import db
from cloudkitty import storage
@ -26,14 +28,6 @@ from cloudkitty.storage.sqlalchemy import models
from cloudkitty import utils as ck_utils
class NoTimeFrame(Exception):
"""Raised when there is no time frame available."""
def __init__(self):
super(NoTimeFrame, self).__init__(
"No time frame available")
class SQLAlchemyStorage(storage.BaseStorage):
"""SQLAlchemy Storage Backend
@ -73,7 +67,20 @@ class SQLAlchemyStorage(storage.BaseStorage):
return ck_utils.dt2ts(r.begin)
def get_total(self):
pass
model = models.RatedDataFrame
# Boundary calculation
month_start = ck_utils.get_this_month()
month_end = ck_utils.get_next_month()
session = db.get_session()
rate = session.query(
sqlalchemy.func.sum(model.rate).label('rate')
).filter(
model.begin >= month_start,
model.end <= month_end
).scalar()
return rate
def get_time_frame(self, begin, end, **filters):
"""Return a list of time frames.
@ -89,14 +96,15 @@ class SQLAlchemyStorage(storage.BaseStorage):
model,
session
).filter(
model.begin >= begin,
model.end <= end
model.begin >= datetime.datetime.fromtimestamp(begin),
model.end <= datetime.datetime.fromtimestamp(end)
)
for cur_filter in filters:
q = q.filter(getattr(model, cur_filter) == filters[cur_filter])
if not q:
raise NoTimeFrame()
return q.to_cloudkitty()
r = q.all()
if not r:
raise storage.NoTimeFrame()
return [entry.to_cloudkitty() for entry in r]
def _append_time_frame(self, res_type, frame):
vol_dict = frame['vol']