Add consumptions API support.

Change-Id: I56a60b98801fce9de6a792371d3ca60ed5712c56
This commit is contained in:
lvdongbing 2016-08-03 21:07:06 -04:00
parent ff26ed98e7
commit 711c54e6d2
7 changed files with 214 additions and 8 deletions

View File

@ -13,6 +13,7 @@
import routes
from bilean.api.openstack.v1 import consumptions
from bilean.api.openstack.v1 import events
from bilean.api.openstack.v1 import policies
from bilean.api.openstack.v1 import resources
@ -172,4 +173,21 @@ class API(wsgi.Router):
action="index",
conditions={'method': 'GET'})
# Consumptions
cons_resource = consumptions.create_resource(conf)
cons_path = "/consumptions"
with mapper.submapper(controller=cons_resource,
path_prefix=cons_path) as cons_mapper:
# Consumption collection
cons_mapper.connect("consumption_index",
"",
action="index",
conditions={'method': 'GET'})
cons_mapper.connect("consumption_statistics",
"/statistics",
action="statistics",
conditions={'method': 'GET'})
super(API, self).__init__(mapper)

View File

@ -0,0 +1,88 @@
#
# 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.
from bilean.api.openstack.v1 import util
from bilean.common import consts
from bilean.common import serializers
from bilean.common import utils
from bilean.common import wsgi
from bilean.rpc import client as rpc_client
class ConsumptionController(object):
"""WSGI controller for Consumptions in Bilean v1 API."""
# Define request scope (must match what is in policy.json)
REQUEST_SCOPE = 'consumptions'
def __init__(self, options):
self.options = options
self.rpc_client = rpc_client.EngineClient()
@util.policy_enforce
def index(self, req):
"""Lists all consumptions."""
filter_whitelist = {
'resource_type': 'mixed',
}
param_whitelist = {
'user_id': 'single',
'start_time': 'single',
'end_time': 'single',
'limit': 'single',
'marker': 'single',
'sort_dir': 'single',
'sort_keys': 'multi',
}
params = util.get_allowed_params(req.params, param_whitelist)
filters = util.get_allowed_params(req.params, filter_whitelist)
key = consts.PARAM_LIMIT
if key in params:
params[key] = utils.parse_int_param(key, params[key])
if not filters:
filters = None
consumptions = self.rpc_client.consumption_list(req.context,
filters=filters,
**params)
return {'consumptions': consumptions}
@util.policy_enforce
def statistics(self, req):
'''Consumptions statistics.'''
filter_whitelist = {
'resource_type': 'mixed',
}
param_whitelist = {
'user_id': 'single',
'start_time': 'single',
'end_time': 'single',
}
params = util.get_allowed_params(req.params, param_whitelist)
filters = util.get_allowed_params(req.params, filter_whitelist)
if not filters:
filters = None
statistics = self.rpc_client.consumption_statistics(req.context,
filters=filters,
**params)
return {'statistics': statistics}
def create_resource(ops):
"""Consumption resource factory method."""
deserializer = wsgi.JSONRequestDeserializer()
serializer = serializers.JSONResponseSerializer()
return wsgi.Resource(ConsumptionController(ops), deserializer, serializer)

View File

@ -312,9 +312,12 @@ def consumption_get(context, consumption_id, project_safe=True):
project_safe=project_safe)
def consumption_get_all(context, limit=None, marker=None, sort_keys=None,
sort_dir=None, filters=None, project_safe=True):
return IMPL.consumption_get_all(context, limit=limit,
def consumption_get_all(context, user_id=None, limit=None, marker=None,
sort_keys=None, sort_dir=None, filters=None,
project_safe=True):
return IMPL.consumption_get_all(context,
user_id=user_id,
limit=limit,
marker=marker,
sort_keys=sort_keys,
sort_dir=sort_dir,

View File

@ -881,14 +881,17 @@ def consumption_get(context, consumption_id, project_safe=True):
return consumption
def consumption_get_all(context, limit=None, marker=None, sort_keys=None,
sort_dir=None, filters=None, project_safe=True):
def consumption_get_all(context, user_id=None, limit=None, marker=None,
sort_keys=None, sort_dir=None, filters=None,
project_safe=True):
query = model_query(context, models.Consumption)
if context.is_admin:
project_safe = False
if project_safe:
query = query.filter_by(user_id=context.project)
elif user_id:
query = query.filter_by(user_id=user_id)
if filters is None:
filters = {}

View File

@ -67,11 +67,14 @@ class Consumption(object):
return cls.from_db_record(record)
@classmethod
def load_all(cls, context, limit=None, marker=None, sort_keys=None,
sort_dir=None, filters=None, project_safe=True):
def load_all(cls, context, user_id=None, limit=None, marker=None,
sort_keys=None, sort_dir=None, filters=None,
project_safe=True):
'''Retrieve all consumptions from database.'''
records = db_api.consumption_get_all(context, limit=limit,
records = db_api.consumption_get_all(context,
user_id=user_id,
limit=limit,
marker=marker,
filters=filters,
sort_keys=sort_keys,

View File

@ -34,6 +34,7 @@ from bilean.common import schema
from bilean.common import utils
from bilean.db import api as db_api
from bilean.engine.actions import base as action_mod
from bilean.engine import consumption as cons_mod
from bilean.engine import dispatcher
from bilean.engine import environment
from bilean.engine import event as event_mod
@ -679,3 +680,71 @@ class EngineService(service.Service):
self.TG.start_action(self.engine_id, action_id=action_id)
LOG.info(_LI('User settle_account action queued: %s'), action_id)
@request_context
def consumption_list(self, cnxt, user_id=None, limit=None,
marker=None, sort_keys=None, sort_dir=None,
filters=None, project_safe=True):
if limit is not None:
limit = utils.parse_int_param('limit', limit)
consumptions = cons_mod.Consumption.load_all(cnxt,
user_id=user_id,
limit=limit,
marker=marker,
sort_keys=sort_keys,
sort_dir=sort_dir,
filters=filters,
project_safe=project_safe)
return [c.to_dict() for c in consumptions]
@request_context
def consumption_statistics(self, cnxt, user_id=None, filters=None,
start_time=None, end_time=None,
project_safe=True):
result = {}
if start_time is None:
start_time = 0
else:
start_time = utils.format_time_to_seconds(start_time)
start_time = utils.make_decimal(start_time)
now_time = utils.format_time_to_seconds(timeutils.utcnow())
now_time = utils.make_decimal(now_time)
if end_time is None:
end_time = now_time
else:
end_time = utils.format_time_to_seconds(end_time)
end_time = utils.make_decimal(end_time)
consumptions = cons_mod.Consumption.load_all(cnxt, user_id=user_id,
project_safe=project_safe)
for cons in consumptions:
if cons.start_time > end_time or cons.end_time < start_time:
continue
et = min(cons.end_time, end_time)
st = max(cons.start_time, start_time)
seconds = et - st
cost = cons.rate * seconds
if cons.resource_type not in result:
result[cons.resource_type] = cost
else:
result[cons.resource_type] += cost
resources = plugin_base.Resource.load_all(cnxt, user_id=user_id,
project_safe=project_safe)
for res in resources:
if res.last_bill > end_time or now_time < start_time:
continue
et = min(now_time, end_time)
st = max(res.last_bill, start_time)
seconds = et - st
cost = res.rate * seconds
if res.resource_type not in result:
result[res.resource_type] = cost
else:
result[res.resource_type] += cost
for key in six.iterkeys(result):
result[key] = utils.dec2str(result[key])
return result

View File

@ -210,3 +210,25 @@ class EngineClient(object):
return self.call(ctxt, self.make_msg('settle_account',
user_id=user_id,
task=task))
# consumptions
def consumption_list(self, ctxt, user_id=None, limit=None, marker=None,
sort_keys=None, sort_dir=None, filters=None,
project_safe=True):
return self.call(ctxt, self.make_msg('consumption_list',
user_id=user_id,
limit=limit, marker=marker,
sort_keys=sort_keys,
sort_dir=sort_dir,
filters=filters,
project_safe=project_safe))
def consumption_statistics(self, ctxt, user_id=None, filters=None,
start_time=None, end_time=None,
project_safe=True):
return self.call(ctxt, self.make_msg('consumption_statistics',
user_id=user_id,
filters=filters,
start_time=start_time,
end_time=end_time,
project_safe=project_safe))