From dd8e37a0b66314f57294d2768f7f2753deba37e6 Mon Sep 17 00:00:00 2001 From: Zhongyue Luo Date: Tue, 15 May 2012 09:28:56 +0000 Subject: [PATCH 01/11] Fix dough api Add query_monthly_report --- dough/api.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/dough/api.py b/dough/api.py index 682c252..ee3dabb 100644 --- a/dough/api.py +++ b/dough/api.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf8 -*- +from dateutil.relativedelta import relativedelta import iso8601 from nova import flags @@ -173,3 +174,44 @@ def query_usage_report(context, timestamp_from=None, item_usage = region_usage.setdefault(item_name, list()) item_usage.append(usage_datum) return {'data': usage_report} + +def query_monthly_report(context, timestamp_from=None, + timestamp_to=None, **kwargs): + + def find_timeframe(start, end, target): + current_frame = start + while current_frame < end: + next_frame = current_frame + relativedelta(months=1) + if current_frame <= target < next_frame: + break + current_frame = next_frame + assert(current_frame < end) + return current_frame.isoformat() + + monthly_report = dict() + datetime_from = iso8601.parse_date(timestamp_from) + datetime_to = iso8601.parse_date(timestamp_to) + subscriptions = list() + _subscriptions = db.subscription_get_all_by_project(context, + context.project_id) + for subscription in _subscriptions: + subscription_id = subscription['id'] + item_name = subscription['product']['item']['name'] + subscriptions.append([subscription_id, item_name]) + for subscription_id, item_name in subscriptions: + purchases = db.purchase_get_all_by_subscription_and_timeframe(context, + subscription_id, + datetime_from, + datetime_to) + if not purchases: + continue + for purchase in purchases: + line_total = purchase['line_total'] + timeframe = find_timeframe(datetime_from, + datetime_to, + purchase['created_at']) + region_usage = monthly_report.setdefault(region_name, dict()) + monthly_usage = region_usage.setdefault(timeframe, dict()) + monthly_usage.setdefault(item_name, 0) + monthly_usage[item_name] += line_total_sum + return {'data': monthly_report} From c0a1044a85503046b5b1830c492c680aa092da9b Mon Sep 17 00:00:00 2001 From: hokerffb Date: Tue, 12 Jun 2012 18:17:32 +0800 Subject: [PATCH 02/11] pyw's discovery --- bin/dough-api | 8 +- bin/dough-client | 77 ++++++++++++++++++ bin/dough-manager | 134 ++++++++++++++++++++++++++++++++ dough/api.py | 128 +++++++++++++++++++++--------- dough/billing/api.py | 2 +- dough/billing/driver/network.py | 20 +---- dough/client/__init__.py | 0 dough/client/dough_client.py | 107 +++++++++++++++++++++++++ dough/db/sqlalchemy/api.py | 10 +++ 9 files changed, 431 insertions(+), 55 deletions(-) create mode 100755 bin/dough-client create mode 100755 bin/dough-manager create mode 100644 dough/client/__init__.py create mode 100644 dough/client/dough_client.py diff --git a/bin/dough-api b/bin/dough-api index ffbe959..bbec55f 100755 --- a/bin/dough-api +++ b/bin/dough-api @@ -6,9 +6,9 @@ import sys import zmq +from nova import utils from nova import flags from nova import log as logging -from nova import utils from dough import api from dough import context as dough_context @@ -25,6 +25,7 @@ if __name__ == '__main__': # Socket to receive messages on handler = zmq_context.socket(zmq.REP) handler.bind("tcp://%s:%s" % (FLAGS.api_listen, FLAGS.api_listen_port)) + print "listen:", FLAGS.api_listen, FLAGS.api_listen_port poller = zmq.Poller() poller.register(handler, zmq.POLLIN) @@ -40,8 +41,13 @@ if __name__ == '__main__': method = msg_body['method'] args = msg_body['args'] context = dough_context.get_context(**args) + print "-"*60 + print "\033[0;31m" + method + "\033[0m:" + print args method_func = getattr(api, method) response = method_func(context, **args) + print "response:" + print response except Exception, e: print traceback.format_exc() cli_msg['code'] = 500 diff --git a/bin/dough-client b/bin/dough-client new file mode 100755 index 0000000..709edcd --- /dev/null +++ b/bin/dough-client @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2012 Sina Corporation +# All Rights Reserved. +# Author: YuWei Peng +# +# 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 sys +import time +import ConfigParser +import json +import zmq +from collections import OrderedDict +from dough.client.dough_client import * + +def show_usage(): + print "usage:" + print "\tdough_client" + print "\tdough_client [time_to] [period_hours]" + print "param:" + print "\t -m : query_monthly_report" + print "\t -d : query_report" + print "./dough-client -d d8462edac32e4d688ae20d67a51d529f 2012-01-01T00:00:00 2012-07-07T00:00:00 24" + +def show_result(data): + print data + +def main(): + if len(sys.argv) == 2: + show_usage() + return + if len(sys.argv) == 3: + if sys.argv[2] in ['--help', "-h", "?"]: + show_usage() + return + + data = None + client = DoughClient() + + if len(sys.argv) > 6: + tenant_id = sys.argv[3] + time_from = sys.argv[4] + time_to = sys.argv[5] + period = sys.argv[6] + if sys.argv[2] == '-d': + data = client.query_report(tenant_id, time_from, time_to, period) + else: + pass + + show_result(data) + return + elif len(sys.argv) > 5: + tenant_id = sys.argv[3] + time_from = sys.argv[4] + time_to = sys.argv[5] + if sys.argv[2] == '-m': + data = client.query_monthly_report(tenant_id, time_from, time_to) + else: + pass + show_result(data) + else: + pass + +if __name__ == '__main__': + main() diff --git a/bin/dough-manager b/bin/dough-manager new file mode 100755 index 0000000..dd43f5e --- /dev/null +++ b/bin/dough-manager @@ -0,0 +1,134 @@ +#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2012 Sina Corporation +# All Rights Reserved. +# Author: YuWei Peng +# +# 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 datetime +import sys +import time +import traceback + +#from dateutil.relativedelta import relativedelta +#import zmq + +from nova import flags +from nova import log as logging +from nova import utils + +#from dough import billing +# from dough.billing import api +from dough import context as dough_context +from dough import db +from dough import api +from nova.openstack.common import cfg +#from dough import exception + +utils.default_flagfile(filename='/etc/dough/dough.conf') +FLAGS = flags.FLAGS +#logging.setup() + +manager_opts = [ + cfg.StrOpt('resource_name', + short='r', + default='name1', + help='resource_name.'), + cfg.StrOpt('purchase', + short='p', + default='name1', + help='purchase.'), + cfg.StrOpt('help', + short='?', + default='', + help='help'), + ] + +FLAGS.register_cli_opts(manager_opts) +flags.FLAGS(sys.argv) + +def show_usage(): + print "dough-mansger -n " + +def get_subs(context, param): + print "filter: resource_name=", param + try: + sub = db.subscription_get_byname(context, resource_name=param) + subscription_id = sub['id'] + status = sub['status'] + tenant_id = sub['project_id'] + resource_uuid = sub['resource_uuid'] + created_at = sub['created_at'] + updated_at = sub['updated_at'] + expires_at = sub['expires_at'] + order_unit = sub['product']['order_unit'] + order_size = sub['product']['order_size'] + price = sub['product']['price'] + currency = sub['product']['currency'] + + region_name = sub['product']['region']['name'] + item_name = sub['product']['item']['name'] + pay_type = sub['product']['payment_type'] + interval_unit = pay_type['interval_unit'] + interval_size = pay_type['interval_size'] + is_prepaid = pay_type['is_prepaid'] + print '-'*60 + print "%24s : %s" % ("subscription_id", str(subscription_id)) + print "%24s : %s" % ("tenant_id", str(tenant_id)) + print "%24s : %s" % ("expires_at", str(expires_at)) + print "%24s : %s" % ("price", str(price)) + print "%24s : %s" % ("region_name", str(region_name)) + print "%24s : %s" % ("item_name", str(item_name)) +# print "%24s : %s" % ("pay_type", str(pay_type)) + print "%24s : %s" % ("created_at", str(created_at)) + except Exception, e: + print Exception, e + +def query_report(tenant_id, timestamp_from, timestamp_to): + datetime_from = iso8601.parse_date(timestamp_from) + datetime_to = iso8601.parse_date(timestamp_to) + context = dough_context.get_context(tenant_id=self.tenant_id) + data = api.query_report(self.context, + timestamp_from, + timestamp_to) + return data + +""" +select id, subscription_id, created_at, quantity from purchases where subscription_id =151 and created_at>"2012-06-03 00:00:00"; +""" + +def main(): + baselen = 2 + context = dough_context.get_admin_context() + if len(sys.argv) == baselen: + get_subs(context, sys.argv) + return + if len(sys.argv) == baselen: + if sys.argv[baselen-1] in ['--help', "-h", "?"]: + show_usage() + return + + if len(sys.argv) == baselen + 2: + param = sys.argv[baselen + 1] + if sys.argv[baselen][:2] == '-r': + get_subs(context, param) + elif len(sys.argv) == baselen + 4: + tenant_id = sys.argv[baselen + 1] + timestamp_from = sys.argv[baselen + 2] + timestamp_to = sys.argv[baselen + 3] + print query_report(tenant_id, timestamp_from, timestamp_to) + + +if __name__ == '__main__': + main() diff --git a/dough/api.py b/dough/api.py index ee3dabb..9d550ae 100644 --- a/dough/api.py +++ b/dough/api.py @@ -2,6 +2,7 @@ # -*- coding: utf8 -*- from dateutil.relativedelta import relativedelta +from dateutil import tz import iso8601 from nova import flags @@ -24,6 +25,9 @@ FLAGS = flags.FLAGS FLAGS.register_opts(api_opts) +UTC_TIMEZONE = tz.gettz('UTC') + + def _product_get_all(context, region=None, item=None, item_type=None, payment_type=None): """ @@ -44,6 +48,7 @@ def _product_get_all(context, region=None, item=None, item_type=None, raise return products + def subscribe_item(context, region=None, item=None, item_type=None, payment_type=None, resource_uuid=None, resource_name=None, **kwargs): @@ -99,33 +104,27 @@ def unsubscribe_item(context, region=None, item=None, return dict() -def query_payment_types(context, region=None, item=None, **kwargs): +def query_item_products(context, region=None, item=None, **kwargs): + product_info = dict() filters = dict() filters['region_id'] = db.region_get_by_name(context, region)['id'] filters['item_id'] = db.item_get_by_name(context, item)['id'] products = db.product_get_all(context, filters=filters) - payment_type_info = { - 'payment_type_names': map(lambda x: x['payment_type']['name'], - products), - } - return {'data': payment_type_info} - - -def query_product_price(context, region=None, item=None, item_type=None, - payment_type=None, resource_uuid=None, **kwargs): - try: - # filter to get product_id - products = _product_get_all(context, region=region, item=item, - item_type=item_type, - payment_type=payment_type) - # TODO(lzyeval): check if products size is not 1 - except Exception, e: - # TODO(lzyeval): report - raise - price_info = { - 'price': products[0]['price'], - } - return {'data': price_info} + for product in products: + item_type_name = product['item_type']['name'] + payment_type_name = product['payment_type']['name'] + order_unit = product['order_unit'] + order_size = product['order_size'] + price = product['price'] + currency = product['currency'] + item_type_info = product_info.setdefault(item_type_name, dict()) + item_type_info[payment_type_name] = { + 'order_unit': order_unit, + 'order_size': order_size, + 'price': price, + 'currency': currency, + } + return {'data': product_info} def query_usage_report(context, timestamp_from=None, @@ -167,9 +166,9 @@ def query_usage_report(context, timestamp_from=None, # TODO(lzyeval): remove #assert (line_total_sum == quantity_sum * price) usage_datum = (resource_uuid, resource_name, item_type_name, - order_unit, price, currency, quantity_sum, - line_total_sum, created_at.isoformat(), - expires_at.isoformat()) + order_unit, order_size, price, + currency, quantity_sum, line_total_sum, + created_at.isoformat(), expires_at.isoformat()) region_usage = usage_report.setdefault(region_name, dict()) item_usage = region_usage.setdefault(item_name, list()) item_usage.append(usage_datum) @@ -178,14 +177,18 @@ def query_usage_report(context, timestamp_from=None, def query_monthly_report(context, timestamp_from=None, timestamp_to=None, **kwargs): - def find_timeframe(start, end, target): - current_frame = start - while current_frame < end: - next_frame = current_frame + relativedelta(months=1) - if current_frame <= target < next_frame: + def find_timeframe(start_time, end_time, target): + target_utc = target.replace(tzinfo=UTC_TIMEZONE) + current_frame = start_time + month_cnt = 1 + while current_frame < end_time: + # 2012-05-10 00:00:00+00:00-->2012-06-10 00:00:00+00:00 + next_frame = start_time + relativedelta(months=month_cnt) + if current_frame <= target_utc < next_frame: break + month_cnt += 1 current_frame = next_frame - assert(current_frame < end) + assert(current_frame < end_time) return current_frame.isoformat() monthly_report = dict() @@ -196,9 +199,61 @@ def find_timeframe(start, end, target): context.project_id) for subscription in _subscriptions: subscription_id = subscription['id'] + region_name = subscription['product']['region']['name'] + item_name = subscription['product']['item']['name'] + subscriptions.append([subscription_id, region_name, item_name]) + for subscription_id, region_name, item_name in subscriptions: + purchases = db.purchase_get_all_by_subscription_and_timeframe(context, + subscription_id, + datetime_from, + datetime_to) + if not purchases: + continue + for purchase in purchases: + line_total = purchase['line_total'] + timeframe = find_timeframe(datetime_from, + datetime_to, + purchase['created_at']) + region_usage = monthly_report.setdefault(region_name, dict()) + monthly_usage = region_usage.setdefault(timeframe, dict()) + monthly_usage.setdefault(item_name, 0) + monthly_usage[item_name] += line_total +# monthly_usage["id"] = purchase['id'] + return {'data': monthly_report} + + +def query_report(context, timestamp_from=None, + timestamp_to=None, period=None, **kwargs): + """period='days' or 'hours'""" + print "query_report", timestamp_from, timestamp_to +# period = int(period) + + def find_timeframe(start_time, end_time, target): + target_utc = target.replace(tzinfo=UTC_TIMEZONE) + current_frame = start_time + cnt = 1 + while current_frame < end_time: + foo = {period: cnt} + next_frame = start_time + relativedelta(**foo) + if current_frame <= target_utc < next_frame: + break + cnt += 1 + current_frame = next_frame + assert(current_frame < end_time) + return current_frame.isoformat() + + monthly_report = dict() + datetime_from = iso8601.parse_date(timestamp_from) + datetime_to = iso8601.parse_date(timestamp_to) + subscriptions = list() + _subscriptions = db.subscription_get_all_by_project(context, + context.project_id) + for subscription in _subscriptions: + subscription_id = subscription['id'] + region_name = subscription['product']['region']['name'] item_name = subscription['product']['item']['name'] - subscriptions.append([subscription_id, item_name]) - for subscription_id, item_name in subscriptions: + subscriptions.append([subscription_id, region_name, item_name]) + for subscription_id, region_name, item_name in subscriptions: purchases = db.purchase_get_all_by_subscription_and_timeframe(context, subscription_id, datetime_from, @@ -210,8 +265,11 @@ def find_timeframe(start, end, target): timeframe = find_timeframe(datetime_from, datetime_to, purchase['created_at']) + print subscription_id, purchase['id'], region_name, "-->", timeframe region_usage = monthly_report.setdefault(region_name, dict()) monthly_usage = region_usage.setdefault(timeframe, dict()) monthly_usage.setdefault(item_name, 0) - monthly_usage[item_name] += line_total_sum + monthly_usage[item_name] += line_total + monthly_usage["id"] = purchase['id'] return {'data': monthly_report} + diff --git a/dough/billing/api.py b/dough/billing/api.py index 24b7051..5bc6875 100644 --- a/dough/billing/api.py +++ b/dough/billing/api.py @@ -98,7 +98,7 @@ def error(*args, **kwargs): def charge(context, tenant_id, subscription_id, quantity, order_size, price): if not quantity: return - line_total = order_size * price / quantity + line_total = price * quantity / order_size values = { 'subscription_id': subscription_id, 'quantity': quantity, diff --git a/dough/billing/driver/network.py b/dough/billing/driver/network.py index 3d087b3..aa2f118 100644 --- a/dough/billing/driver/network.py +++ b/dough/billing/driver/network.py @@ -16,8 +16,6 @@ # License for the specific language governing permissions and limitations # under the License. -import MySQLdb -import MySQLdb.cursors import zmq from nova import flags @@ -33,13 +31,6 @@ FLAGS.keystone_auth_url, service_type="compute") -MYSQL_CLIENT = MySQLdb.connect(host=FLAGS.mysql_host, - port=FLAGS.mysql_port, - user=FLAGS.mysql_user, - passwd=FLAGS.mysql_pwd, - db=FLAGS.nova_schema, - cursorclass=MySQLdb.cursors.DictCursor) - class Client(): @@ -95,18 +86,11 @@ def is_terminated(instance_uuid): def get_usage(instance_uuid, datetime_from, datetime_to, order_size): - cur = MYSQL_CLIENT.cursor() - cur.execute("SELECT id FROM instances WHERE uuid=%s", instance_uuid) - result = cur.fetchone() - cur.close() - if result is None: - raise Exception() - instance_id = result['id'] data = KANYUN_CLIENT.send({'method': 'query_usage_report', 'args': { - 'id': 'instance-%08x' % instance_id, + 'id': instance_uuid, 'metric': 'vmnetwork', - 'metric_param': 'vnet0', + 'metric_param': 'total', 'statistic': 'sum', 'period': 60, 'timestamp_from': datetime_from.isoformat(), diff --git a/dough/client/__init__.py b/dough/client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dough/client/dough_client.py b/dough/client/dough_client.py new file mode 100644 index 0000000..7291c85 --- /dev/null +++ b/dough/client/dough_client.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2012 Sina Corporation +# All Rights Reserved. +# Author: YuWei Peng +# +# 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 sys +import time +from md5 import md5 +import json +import zmq +import urllib2 +import datetime +import base64 +from collections import OrderedDict + +from nova import utils +from nova import flags +from nova.openstack.common import cfg +from nova import log as logging + +utils.default_flagfile(filename='/etc/dough/dough.conf') +logging.setup() + +api_opts = [ + cfg.StrOpt('api_host', + default='127.0.0.1', + help='IP address of dough API.'), + cfg.IntOpt('api_port', + default=8783, + help='Port of dough api.'), +# cfg.StrOpt('monthly_report', +# short='m', +# default='name1', +# help='monthly_report.'), + ] + +cli_opts = [ + cfg.StrOpt('monthly_report', + short='m', + default='name1', + help='monthly_report.'), + ] + +FLAGS = flags.FLAGS +FLAGS.register_cli_opts(cli_opts) +FLAGS.register_opts(api_opts) +flags.FLAGS(sys.argv) + +STANDARD_PROTOCOL = { + 'method': 'query_report', + 'args': { + 'user_id': '864bbc5d23ea47799ae2a702927920e9', + 'tenant_id': '864bbc5d23ea47799ae2a702927920e9', + 'timestamp_from': '2012-03-01T00:00:00', + 'timestamp_to': '2012-03-02T00:00:00', + } + } + +class DoughClient(): + + def __init__(self): + context = zmq.Context() + self.socket = context.socket(zmq.REQ) + connstr = "tcp://%(api_host)s:%(api_port)s" % FLAGS + # print connstr + self.socket.connect(connstr) + + def invoke(self, param): + self.socket.send_multipart (["client", "1", json.dumps(param)]) + msg_type, uuid, message = self.socket.recv_multipart() + return json.loads(message) + + def query_monthly_report(self, tenant_id, time_from, time_to): + request = STANDARD_PROTOCOL + request["method"] = "query_monthly_report" + request["args"]["tenant_id"] = tenant_id + request["args"]["timestamp_from"] = time_from + request["args"]["timestamp_to"] = time_to + + data = self.invoke(request) + return data + + def query_report(self, tenant_id, time_from, time_to, period): + request = STANDARD_PROTOCOL + request["method"] = "query_report" + request["args"]["tenant_id"] = tenant_id + request["args"]["timestamp_from"] = time_from + request["args"]["timestamp_to"] = time_to + request["args"]["period"] = period + + data = self.invoke(request) + return data + diff --git a/dough/db/sqlalchemy/api.py b/dough/db/sqlalchemy/api.py index 82cdd90..2355aae 100644 --- a/dough/db/sqlalchemy/api.py +++ b/dough/db/sqlalchemy/api.py @@ -241,6 +241,15 @@ def payment_type_get_by_name(context, payment_type_name): # products +def subscription_get_byname(context, resource_name): + result = model_query(context, models.Subscription).\ + filter_by(resource_name=resource_name).\ + first() + if not result: + return None + return result + + def product_get(context, product_id): result = model_query(context, models.Product).\ filter_by(id=product_id).\ @@ -352,6 +361,7 @@ def subscription_get_all_by_resource_uuid(context, resource_uuid): def subscription_get_all(context, filters=None): + """filters={project_id:1}""" filters = filters or dict() filters = dict(filter(lambda (x, y): x in ['project_id', 'product_id', From 90d3e780e20981e3ed5ca104e46201ebda5fe1b1 Mon Sep 17 00:00:00 2001 From: hokerffb Date: Fri, 15 Jun 2012 16:39:50 +0800 Subject: [PATCH 03/11] add bill detail interface: query_report() --- bin/dough-client | 32 +++++++++++++++--- bin/dough-farmer | 1 + dough/api.py | 65 +++++++++++++++++++++++++++++------- dough/billing/api.py | 6 ++++ dough/client/dough_client.py | 5 ++- 5 files changed, 92 insertions(+), 17 deletions(-) diff --git a/bin/dough-client b/bin/dough-client index 709edcd..2fefd94 100755 --- a/bin/dough-client +++ b/bin/dough-client @@ -32,10 +32,30 @@ def show_usage(): print "param:" print "\t -m : query_monthly_report" print "\t -d : query_report" - print "./dough-client -d d8462edac32e4d688ae20d67a51d529f 2012-01-01T00:00:00 2012-07-07T00:00:00 24" + print "./dough-client -d 1adfb274fee24dbd96ea89b57d110fc5 2012-06-01T00:00:00 2012-07-01T00:00:00 days network resource_name" def show_result(data): print data + +def show_report(data): +# print "ID=", data['data']['id'] + data = data['data']['default'] + line_total_sum = 0 + quantity_sum = 0 + count = 0 + + rs = OrderedDict(sorted(data.items(), key=lambda t: t[0])) + for k, i in rs.iteritems(): + count += 1 + print '-' * 60, count + print k + for kk, ii in i.iteritems(): + print "\t", kk, ii + if kk == 'line_total': + line_total_sum += ii + elif kk == 'quantity': + quantity_sum += ii + print "total[", count, "] = ", line_total_sum def main(): if len(sys.argv) == 2: @@ -49,17 +69,21 @@ def main(): data = None client = DoughClient() - if len(sys.argv) > 6: + if len(sys.argv) > 8: tenant_id = sys.argv[3] time_from = sys.argv[4] time_to = sys.argv[5] period = sys.argv[6] + item_name = sys.argv[7] + resource_name = sys.argv[8] if sys.argv[2] == '-d': - data = client.query_report(tenant_id, time_from, time_to, period) + data = client.query_report(tenant_id, time_from, time_to, period, + item_name, resource_name) else: pass - show_result(data) + show_report(data) + elif len(sys.argv) > 6: return elif len(sys.argv) > 5: tenant_id = sys.argv[3] diff --git a/bin/dough-farmer b/bin/dough-farmer index 58ac455..e891f03 100755 --- a/bin/dough-farmer +++ b/bin/dough-farmer @@ -29,6 +29,7 @@ if __name__ == '__main__': context = dough_context.get_admin_context() while True: current_time = utils.utcnow() + print "-" * 50, str(current_time) subscriptions = list() _subscriptions = db.subscription_get_all(context) for sub in _subscriptions: diff --git a/dough/api.py b/dough/api.py index 9d550ae..57a302d 100644 --- a/dough/api.py +++ b/dough/api.py @@ -214,19 +214,22 @@ def find_timeframe(start_time, end_time, target): timeframe = find_timeframe(datetime_from, datetime_to, purchase['created_at']) + monthly_report["id"] = purchase['id'] region_usage = monthly_report.setdefault(region_name, dict()) monthly_usage = region_usage.setdefault(timeframe, dict()) monthly_usage.setdefault(item_name, 0) monthly_usage[item_name] += line_total -# monthly_usage["id"] = purchase['id'] return {'data': monthly_report} -def query_report(context, timestamp_from=None, - timestamp_to=None, period=None, **kwargs): +def query_report(context, timestamp_from=None, timestamp_to=None, + period=None, item_name=None, resource_name=None, **kwargs): """period='days' or 'hours'""" - print "query_report", timestamp_from, timestamp_to + print "query_report", timestamp_from, timestamp_to, item_name, resource_name # period = int(period) + + if not period in ['days', 'hours', 'months']: + return {'data': None} def find_timeframe(start_time, end_time, target): target_utc = target.replace(tzinfo=UTC_TIMEZONE) @@ -243,33 +246,71 @@ def find_timeframe(start_time, end_time, target): return current_frame.isoformat() monthly_report = dict() + usage_report = dict() datetime_from = iso8601.parse_date(timestamp_from) datetime_to = iso8601.parse_date(timestamp_to) subscriptions = list() - _subscriptions = db.subscription_get_all_by_project(context, + _subscriptions = list() + + __subscriptions = db.subscription_get_all_by_project(context, context.project_id) + if not __subscriptions: + return {'data': None} +# print "context.project_id", context.project_id + for subscription in __subscriptions: +# print subscription['id'], subscription['resource_name'], subscription['product']['item']['name'] + if subscription['resource_name'] != resource_name: + continue + elif subscription['product']['item']['name'] != item_name: + continue + _subscriptions.append(subscription) + for subscription in _subscriptions: subscription_id = subscription['id'] + resource_uuid = subscription['resource_uuid'] + resource_name = subscription['resource_name'] + created_at = subscription['created_at'] + expires_at = subscription['expires_at'] region_name = subscription['product']['region']['name'] item_name = subscription['product']['item']['name'] - subscriptions.append([subscription_id, region_name, item_name]) - for subscription_id, region_name, item_name in subscriptions: + item_type_name = subscription['product']['item_type']['name'] + order_unit = subscription['product']['order_unit'] + order_size = subscription['product']['order_size'] + price = subscription['product']['price'] + currency = subscription['product']['currency'] + subscriptions.append([subscription_id, resource_uuid, resource_name, + created_at, expires_at, + region_name, item_name, item_type_name, + order_unit, order_size, price, currency]) + for (subscription_id, resource_uuid, resource_name, created_at, expires_at, + region_name, item_name, item_type_name, + order_unit, order_size, price, currency) in subscriptions: purchases = db.purchase_get_all_by_subscription_and_timeframe(context, subscription_id, datetime_from, datetime_to) if not purchases: continue + i = 0 for purchase in purchases: line_total = purchase['line_total'] + quantity = purchase['quantity'] timeframe = find_timeframe(datetime_from, datetime_to, purchase['created_at']) - print subscription_id, purchase['id'], region_name, "-->", timeframe +# print timeframe + i += 1 + usage_datum = (resource_uuid, resource_name, item_type_name, + order_unit, order_size, price, + currency, quantity, line_total, + created_at.isoformat(), expires_at.isoformat()) region_usage = monthly_report.setdefault(region_name, dict()) monthly_usage = region_usage.setdefault(timeframe, dict()) - monthly_usage.setdefault(item_name, 0) - monthly_usage[item_name] += line_total - monthly_usage["id"] = purchase['id'] + item = monthly_usage.setdefault(item_name, 0) + monthly_usage[item_name] = usage_datum + item = monthly_usage.setdefault("quantity", 0) + monthly_usage.setdefault("line_total", 0) + monthly_usage["quantity"] += quantity + monthly_usage["line_total"] += line_total + print "total:", i return {'data': monthly_report} - diff --git a/dough/billing/api.py b/dough/billing/api.py index 5bc6875..a853178 100644 --- a/dough/billing/api.py +++ b/dough/billing/api.py @@ -42,6 +42,8 @@ def creating(context, subscription_id, tenant_id, resource_uuid, quantity = conn.get_usage(resource_uuid, expires_at - relativedelta(**interval_info), expires_at, order_size) + print "creating", tenant_id, subscription_id, \ + quantity, order_size, "\033[1;33m", price, "\033[0m" charge(context, tenant_id, subscription_id, quantity, order_size, price) db.subscription_extend(context, subscription_id, @@ -67,6 +69,8 @@ def deleting(context, subscription_id, tenant_id, resource_uuid, quantity = conn.get_usage(resource_uuid, expires_at - relativedelta(**interval_info), expires_at, order_size) + print "deleting", tenant_id, subscription_id, \ + quantity, order_size, "\033[1;33m", price, "\033[0m" charge(context, tenant_id, subscription_id, quantity, order_size, price) @@ -85,6 +89,8 @@ def verified(context, subscription_id, tenant_id, resource_uuid, quantity = conn.get_usage(resource_uuid, expires_at - relativedelta(**interval_info), expires_at, order_size) + print "verified", tenant_id, subscription_id, \ + quantity, order_size, "\033[1;33m", price, "\033[0m" charge(context, tenant_id, subscription_id, quantity, order_size, price) db.subscription_extend(context, subscription_id, expires_at + relativedelta(**interval_info)) diff --git a/dough/client/dough_client.py b/dough/client/dough_client.py index 7291c85..cb709c6 100644 --- a/dough/client/dough_client.py +++ b/dough/client/dough_client.py @@ -94,13 +94,16 @@ def query_monthly_report(self, tenant_id, time_from, time_to): data = self.invoke(request) return data - def query_report(self, tenant_id, time_from, time_to, period): + def query_report(self, tenant_id, time_from, time_to, period, + item_name, resource_name): request = STANDARD_PROTOCOL request["method"] = "query_report" request["args"]["tenant_id"] = tenant_id request["args"]["timestamp_from"] = time_from request["args"]["timestamp_to"] = time_to request["args"]["period"] = period + request["args"]["item_name"] = item_name + request["args"]["resource_name"] = resource_name data = self.invoke(request) return data From 4c94b8ca8466be370bfccefedbb2b62c32d70731 Mon Sep 17 00:00:00 2001 From: hokerffb Date: Tue, 19 Jun 2012 11:46:46 +0800 Subject: [PATCH 04/11] db.purchases.quantity type change from float to BigInteger --- dough/api.py | 1 + dough/db/sqlalchemy/models.py | 4 ++-- tests/integration/db/test_api.py | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/dough/api.py b/dough/api.py index 20481b0..108a3ca 100644 --- a/dough/api.py +++ b/dough/api.py @@ -86,6 +86,7 @@ def unsubscribe_item(context, region=None, item=None, subscriptions = db.subscription_get_all_by_resource_uuid(context, resource_uuid) if not subscriptions: + print "unsubscribe_item.SubscriptionNotFoundByResourceUUID", resource_uuid raise exception.SubscriptionNotFoundByResourceUUID( resource_uuid=resource_uuid) for subscription in subscriptions: diff --git a/dough/db/sqlalchemy/models.py b/dough/db/sqlalchemy/models.py index 434a7ce..c0f4e01 100644 --- a/dough/db/sqlalchemy/models.py +++ b/dough/db/sqlalchemy/models.py @@ -22,7 +22,7 @@ from sqlalchemy.orm import relationship, backref -from sqlalchemy import Column, Integer, String +from sqlalchemy import Column, Integer, BigInteger, String from sqlalchemy import ForeignKey, DateTime, Boolean, Float from nova.db.sqlalchemy import models @@ -140,5 +140,5 @@ class Purchase(models.BASE, models.NovaBase): subscription_id = Column(Integer, ForeignKey(Subscription.id), nullable=False) - quantity = Column(Float, nullable=False) + quantity = Column(BigInteger, nullable=False) line_total = Column(Float, nullable=False) diff --git a/tests/integration/db/test_api.py b/tests/integration/db/test_api.py index 72a8bd0..faf0097 100644 --- a/tests/integration/db/test_api.py +++ b/tests/integration/db/test_api.py @@ -656,7 +656,7 @@ def test_purchase_create(self): subscription_ref = db.subscription_create(self.context, values) values = { 'subscription_id': subscription_ref.id, - 'quantity': 1.56, + 'quantity': 1, 'line_total': 1.56 * product_ref.price, } expect = db.purchase_create(self.context, values) @@ -700,7 +700,7 @@ def test_purchase_destroy(self): subscription_ref = db.subscription_create(self.context, values) values = { 'subscription_id': subscription_ref.id, - 'quantity': 1.56, + 'quantity': 1, 'line_total': 1.56 * product_ref.price, } expect = db.purchase_create(self.context, values) @@ -746,7 +746,7 @@ def test_purchase_get_all_by_subscription_and_timeframe(self): subscription_ref = db.subscription_create(self.context, values) values = { 'subscription_id': subscription_ref.id, - 'quantity': 1.56, + 'quantity': 1, 'line_total': 1.56 * product_ref.price, } expect = db.purchase_create(self.context, values) From 5007d5bc9cd2ae5e85470757293a77566126f08b Mon Sep 17 00:00:00 2001 From: hokerffb Date: Thu, 21 Jun 2012 12:08:09 +0800 Subject: [PATCH 05/11] fix product.region.name none bug --- dough/api.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dough/api.py b/dough/api.py index 108a3ca..a5a2249 100644 --- a/dough/api.py +++ b/dough/api.py @@ -137,6 +137,11 @@ def query_usage_report(context, timestamp_from=None, _subscriptions = db.subscription_get_all_by_project(context, context.project_id) for subscription in _subscriptions: + if subscription is None \ + or subscription['product'] is None \ + or subscription['product']['region'] is None \ + or subscription['product']['region']['name'] is None: + continue subscription_id = subscription['id'] resource_uuid = subscription['resource_uuid'] resource_name = subscription['resource_name'] @@ -201,6 +206,11 @@ def find_timeframe(start_time, end_time, target): context.project_id) for subscription in _subscriptions: subscription_id = subscription['id'] + if subscription is None \ + or subscription['product'] is None \ + or subscription['product']['region'] is None \ + or subscription['product']['region']['name'] is None: + continue region_name = subscription['product']['region']['name'] item_name = subscription['product']['item']['name'] subscriptions.append([subscription_id, region_name, item_name]) From cec479f22c5f50a99281eb477b85c344785bffb3 Mon Sep 17 00:00:00 2001 From: hokerffb Date: Wed, 27 Jun 2012 17:31:46 +0800 Subject: [PATCH 06/11] fix subscribe failed bug --- bin/dough-client | 75 +++++++++++++++++++++++++++--------- dough/api.py | 38 ++++++++++++------ dough/client/dough_client.py | 65 +++++++++++++++++++++++++------ dough/db/sqlalchemy/api.py | 3 +- 4 files changed, 137 insertions(+), 44 deletions(-) diff --git a/bin/dough-client b/bin/dough-client index 2fefd94..9daf78d 100755 --- a/bin/dough-client +++ b/bin/dough-client @@ -18,12 +18,13 @@ # under the License. import sys -import time -import ConfigParser -import json -import zmq +#import time +#import ConfigParser +#import json +#import zmq from collections import OrderedDict -from dough.client.dough_client import * +from dough.client.dough_client import DoughClient + def show_usage(): print "usage:" @@ -33,17 +34,23 @@ def show_usage(): print "\t -m : query_monthly_report" print "\t -d : query_report" print "./dough-client -d 1adfb274fee24dbd96ea89b57d110fc5 2012-06-01T00:00:00 2012-07-01T00:00:00 days network resource_name" + print "\t -u : unsubscribe_item" + print "./dough-client -u user_id, tenant_id, region, resource_uuid, item, timestamp" + print "\t -s : subscribe_item" + print "./dough-client -s user_id tenant_id resource_uuid resource_name region item item_type payment_type timestamp" + def show_result(data): print data + def show_report(data): # print "ID=", data['data']['id'] data = data['data']['default'] line_total_sum = 0 quantity_sum = 0 count = 0 - + rs = OrderedDict(sorted(data.items(), key=lambda t: t[0])) for k, i in rs.iteritems(): count += 1 @@ -56,7 +63,8 @@ def show_report(data): elif kk == 'quantity': quantity_sum += ii print "total[", count, "] = ", line_total_sum - + + def main(): if len(sys.argv) == 2: show_usage() @@ -65,23 +73,51 @@ def main(): if sys.argv[2] in ['--help', "-h", "?"]: show_usage() return - + data = None client = DoughClient() - + + if len(sys.argv) == 12: + if sys.argv[2] == '-s': + user_id = sys.argv[3] + tenant_id = sys.argv[4] + resource_uuid = sys.argv[5] + resource_name = sys.argv[6] + region = sys.argv[7] + item = sys.argv[8] + item_type = sys.argv[9] + payment_type = sys.argv[10] + timestamp = sys.argv[11] + data = client.subscribe_item(user_id, tenant_id, + resource_uuid, resource_name, + region, item, + item_type, payment_type, + timestamp) + print data + if len(sys.argv) > 8: - tenant_id = sys.argv[3] - time_from = sys.argv[4] - time_to = sys.argv[5] - period = sys.argv[6] - item_name = sys.argv[7] - resource_name = sys.argv[8] if sys.argv[2] == '-d': - data = client.query_report(tenant_id, time_from, time_to, period, + tenant_id = sys.argv[3] + time_from = sys.argv[4] + time_to = sys.argv[5] + period = sys.argv[6] + item_name = sys.argv[7] + resource_name = sys.argv[8] + data = client.query_report(tenant_id, time_from, time_to, period, item_name, resource_name) + elif sys.argv[2] == '-u': + user_id = sys.argv[3] + tenant_id = sys.argv[4] + region = sys.argv[5] + resource_uuid = sys.argv[6] + item = sys.argv[7] + timestamp = sys.argv[8] + data = client.unsubscribe_item(user_id, tenant_id, region, + resource_uuid, item, timestamp) + print data else: pass - + show_report(data) elif len(sys.argv) > 6: return @@ -90,12 +126,13 @@ def main(): time_from = sys.argv[4] time_to = sys.argv[5] if sys.argv[2] == '-m': - data = client.query_monthly_report(tenant_id, time_from, time_to) + data = client.query_monthly_report(tenant_id, time_from, time_to) else: pass show_result(data) else: pass - + + if __name__ == '__main__': main() diff --git a/dough/api.py b/dough/api.py index a5a2249..7353512 100644 --- a/dough/api.py +++ b/dough/api.py @@ -6,7 +6,7 @@ import iso8601 from nova import flags -from nova import utils +#from nova import utils from nova.openstack.common import cfg from dough import db @@ -45,6 +45,7 @@ def _product_get_all(context, region=None, item=None, item_type=None, products = db.product_get_all(context, filters=filters) except Exception, e: # TODO(lzyeval): report + print e raise return products @@ -67,12 +68,16 @@ def subscribe_item(context, region=None, item=None, item_type=None, payment_type=payment_type) # TODO(lzyeval): check if products size is not 1 values['product_id'] = products[0]['id'] + values['status'] = "verified" + print "subscription_create", values subscription_ref = db.subscription_create(context, values) db.subscription_extend(context, subscription_ref['id'], subscription_ref['created_at']) + print "item subscribed." except Exception, e: # TODO(lzyeval): report + print "subscribe failed:", Exception, e raise return dict() @@ -90,17 +95,25 @@ def unsubscribe_item(context, region=None, item=None, raise exception.SubscriptionNotFoundByResourceUUID( resource_uuid=resource_uuid) for subscription in subscriptions: - if subscription['product']['region']['name'] != region: + product = subscription['product'] + if product['region']['name'] != region: continue - elif subscription['product']['item']['name'] != item: + elif product['item']['name'] != item: + continue + # TODO: status==verified + print subscription['status'] + if "floating_ip" == product['item']['name'] and "verified" != subscription['status']: continue subscription_id = subscription['id'] + break if not subscription_id: + print "subscription_get_by_resource_uuid", resource_uuid, "item=", item, "region=", region raise exception.SubscriptionNotFoundByRegionOrItem(region=region, item=item) db.subscription_destroy(context, subscription_id) except Exception, e: # TODO(lzyeval): report + print e raise return dict() @@ -180,6 +193,7 @@ def query_usage_report(context, timestamp_from=None, item_usage.append(usage_datum) return {'data': usage_report} + def query_monthly_report(context, timestamp_from=None, timestamp_to=None, **kwargs): @@ -232,7 +246,8 @@ def find_timeframe(start_time, end_time, target): monthly_usage[item_name] += line_total return {'data': monthly_report} -def query_report(context, timestamp_from=None, timestamp_to=None, + +def query_report(context, timestamp_from=None, timestamp_to=None, period=None, item_name=None, resource_name=None, **kwargs): """period='days' or 'hours'""" print "query_report", timestamp_from, timestamp_to, item_name, resource_name @@ -240,7 +255,7 @@ def query_report(context, timestamp_from=None, timestamp_to=None, if not period in ['days', 'hours', 'months']: return {'data': None} - + def find_timeframe(start_time, end_time, target): target_utc = target.replace(tzinfo=UTC_TIMEZONE) current_frame = start_time @@ -256,12 +271,12 @@ def find_timeframe(start_time, end_time, target): return current_frame.isoformat() monthly_report = dict() - usage_report = dict() + #usage_report = dict() datetime_from = iso8601.parse_date(timestamp_from) datetime_to = iso8601.parse_date(timestamp_to) subscriptions = list() _subscriptions = list() - + __subscriptions = db.subscription_get_all_by_project(context, context.project_id) if not __subscriptions: @@ -274,7 +289,7 @@ def find_timeframe(start_time, end_time, target): elif subscription['product']['item']['name'] != item_name: continue _subscriptions.append(subscription) - + for subscription in _subscriptions: subscription_id = subscription['id'] resource_uuid = subscription['resource_uuid'] @@ -312,16 +327,15 @@ def find_timeframe(start_time, end_time, target): i += 1 usage_datum = (resource_uuid, resource_name, item_type_name, order_unit, order_size, price, - currency, quantity, line_total, + currency, quantity, line_total, created_at.isoformat(), expires_at.isoformat()) region_usage = monthly_report.setdefault(region_name, dict()) monthly_usage = region_usage.setdefault(timeframe, dict()) - item = monthly_usage.setdefault(item_name, 0) + monthly_usage.setdefault(item_name, 0) monthly_usage[item_name] = usage_datum - item = monthly_usage.setdefault("quantity", 0) + monthly_usage.setdefault("quantity", 0) monthly_usage.setdefault("line_total", 0) monthly_usage["quantity"] += quantity monthly_usage["line_total"] += line_total print "total:", i return {'data': monthly_report} - diff --git a/dough/client/dough_client.py b/dough/client/dough_client.py index cb709c6..cfad8f2 100644 --- a/dough/client/dough_client.py +++ b/dough/client/dough_client.py @@ -18,14 +18,14 @@ # under the License. import sys -import time -from md5 import md5 +#import time +#from md5 import md5 import json import zmq -import urllib2 -import datetime -import base64 -from collections import OrderedDict +#import urllib2 +#import datetime +#import base64 +#from collections import OrderedDict from nova import utils from nova import flags @@ -47,12 +47,20 @@ # default='name1', # help='monthly_report.'), ] - + cli_opts = [ cfg.StrOpt('monthly_report', short='m', default='name1', help='monthly_report.'), + cfg.StrOpt('subscribe_item', + short='s', + default='default1', + help='subscribe_item.'), + cfg.StrOpt('unsubscribe_item', + short='u', + default='default1', + help='unsubscribe_item.'), ] FLAGS = flags.FLAGS @@ -70,6 +78,7 @@ } } + class DoughClient(): def __init__(self): @@ -78,12 +87,12 @@ def __init__(self): connstr = "tcp://%(api_host)s:%(api_port)s" % FLAGS # print connstr self.socket.connect(connstr) - + def invoke(self, param): - self.socket.send_multipart (["client", "1", json.dumps(param)]) + self.socket.send_multipart(["client", "1", json.dumps(param)]) msg_type, uuid, message = self.socket.recv_multipart() return json.loads(message) - + def query_monthly_report(self, tenant_id, time_from, time_to): request = STANDARD_PROTOCOL request["method"] = "query_monthly_report" @@ -93,7 +102,7 @@ def query_monthly_report(self, tenant_id, time_from, time_to): data = self.invoke(request) return data - + def query_report(self, tenant_id, time_from, time_to, period, item_name, resource_name): request = STANDARD_PROTOCOL @@ -107,4 +116,36 @@ def query_report(self, tenant_id, time_from, time_to, period, data = self.invoke(request) return data - + + def subscribe_item(self, user_id, tenant_id, + resource_uuid, resource_name, region, item, + item_type, payment_type, timestamp): + request = STANDARD_PROTOCOL + request["method"] = "subscribe_item" + request["args"]["user_id"] = user_id + + request["args"]["tenant_id"] = tenant_id + request["args"]["resource_name"] = resource_name + request["args"]["region"] = region + request["args"]["resource_uuid"] = resource_uuid + request["args"]["item"] = item + request["args"]["item_type"] = item_type + request["args"]["payment_type"] = payment_type + request["args"]["timestamp"] = timestamp + + data = self.invoke(request) + return data + + def unsubscribe_item(self, user_id, tenant_id, region, resource_uuid, item, timestamp): + request = STANDARD_PROTOCOL + request["method"] = "unsubscribe_item" + + request["args"]["user_id"] = user_id + request["args"]["tenant_id"] = tenant_id + request["args"]["region"] = region + request["args"]["resource_uuid"] = resource_uuid + request["args"]["item"] = item + request["args"]["timestamp"] = timestamp + + data = self.invoke(request) + return data diff --git a/dough/db/sqlalchemy/api.py b/dough/db/sqlalchemy/api.py index 2355aae..bf33e6b 100644 --- a/dough/db/sqlalchemy/api.py +++ b/dough/db/sqlalchemy/api.py @@ -248,7 +248,7 @@ def subscription_get_byname(context, resource_name): if not result: return None return result - + def product_get(context, product_id): result = model_query(context, models.Product).\ @@ -365,6 +365,7 @@ def subscription_get_all(context, filters=None): filters = filters or dict() filters = dict(filter(lambda (x, y): x in ['project_id', 'product_id', + 'status', 'resource_uuid'], filters.items())) return model_query(context, models.Subscription).filter_by(**filters).all() From e4f9eb931c48a5ea70417e68b50f2c3091d52cae Mon Sep 17 00:00:00 2001 From: hokerffb Date: Mon, 9 Jul 2012 16:55:02 +0800 Subject: [PATCH 07/11] new_lb_protocol/add log --- bin/dough-client | 56 ++++++++++++++++++--------- bin/dough-farmer | 23 +++++++---- dough/billing/api.py | 20 +++++++--- dough/billing/driver/load_balancer.py | 32 ++++++++++----- dough/client/dough_client.py | 37 +++++++++++++----- dough/db/sqlalchemy/api.py | 21 +++++----- 6 files changed, 128 insertions(+), 61 deletions(-) diff --git a/bin/dough-client b/bin/dough-client index 9daf78d..e6115cd 100755 --- a/bin/dough-client +++ b/bin/dough-client @@ -18,10 +18,6 @@ # under the License. import sys -#import time -#import ConfigParser -#import json -#import zmq from collections import OrderedDict from dough.client.dough_client import DoughClient @@ -31,13 +27,18 @@ def show_usage(): print "\tdough_client" print "\tdough_client [time_to] [period_hours]" print "param:" - print "\t -m : query_monthly_report" - print "\t -d : query_report" - print "./dough-client -d 1adfb274fee24dbd96ea89b57d110fc5 2012-06-01T00:00:00 2012-07-01T00:00:00 days network resource_name" - print "\t -u : unsubscribe_item" - print "./dough-client -u user_id, tenant_id, region, resource_uuid, item, timestamp" - print "\t -s : subscribe_item" - print "./dough-client -s user_id tenant_id resource_uuid resource_name region item item_type payment_type timestamp" + print "\t -m : query_monthly_report" + print "\t -d : query_report" + print "\t -u : unsubscribe_item" + print "\t -s : subscribe_item" + print "\t -l : get_load_balancers" + print "example:" + print "\t./dough-client -s user_id tenant_id resource_uuid resource_name region item item_type payment_type timestamp" + print "\t./dough-client -u user_id, tenant_id, region, resource_uuid, item, timestamp" + print "\t./dough-client -d 1adfb274fee24dbd96ea89b57d110fc5 2012-06-01T00:00:00 2012-07-01T00:00:00 days network resource_name" + print "\tget_all: ./dough-client -l user_id tenant_id" + print "\tget_by_uuid: ./dough-client -l user_id tenant_id load_balancer_uuid" + print "\tisrunning?: ./dough-client -l load_balancer_uuid" def show_result(data): @@ -121,15 +122,34 @@ def main(): show_report(data) elif len(sys.argv) > 6: return - elif len(sys.argv) > 5: - tenant_id = sys.argv[3] - time_from = sys.argv[4] - time_to = sys.argv[5] - if sys.argv[2] == '-m': - data = client.query_monthly_report(tenant_id, time_from, time_to) + elif len(sys.argv) >= 3: + if sys.argv[2] == '-l': + if len(sys.argv) == 5: + user_id = sys.argv[3] + tenant_id = sys.argv[4] + data = client.load_balancer_get_all(user_id, tenant_id) + print data + elif len(sys.argv) == 6: + user_id = sys.argv[3] + tenant_id = sys.argv[4] + lb_id = sys.argv[5] + data = client.load_balancer_get(user_id, tenant_id, lb_id) + print data + elif len(sys.argv) == 4: + lb_id = sys.argv[3] + data = client.load_balancer_is_running(lb_id) + print data + else: + pass + elif sys.argv[2] == '-m': + if len(sys.argv) == 6: + tenant_id = sys.argv[3] + time_from = sys.argv[4] + time_to = sys.argv[5] + data = client.query_monthly_report(tenant_id, time_from, time_to) + show_result(data) else: pass - show_result(data) else: pass diff --git a/bin/dough-farmer b/bin/dough-farmer index e891f03..6508478 100755 --- a/bin/dough-farmer +++ b/bin/dough-farmer @@ -1,22 +1,23 @@ #!/usr/bin/env python # -*- coding: utf8 -*- -import datetime +#import datetime import sys import time -import traceback +#import traceback -from dateutil.relativedelta import relativedelta -import zmq +#from dateutil.relativedelta import relativedelta +#import zmq from nova import flags from nova import log as logging from nova import utils -from dough import billing +#from dough import billing from dough import context as dough_context from dough import db -from dough import exception +#from dough import exception +from kanyun.common.app import App utils.default_flagfile(filename='/etc/dough/dough.conf') flags.FLAGS(sys.argv) @@ -26,10 +27,13 @@ FLAGS = flags.FLAGS if __name__ == '__main__': from dough.billing import api + app = App(conf="dough/dough.conf", name="farmer") context = dough_context.get_admin_context() + context.app = app while True: current_time = utils.utcnow() - print "-" * 50, str(current_time) + print "-" * 30, str(current_time) + app.info("------------ farming ----------") subscriptions = list() _subscriptions = db.subscription_get_all(context) for sub in _subscriptions: @@ -64,9 +68,14 @@ if __name__ == '__main__': created_at, updated_at, expires_at, order_unit, order_size, price, currency, region_name, item_name, interval_unit, interval_size, is_prepaid) in subscriptions: + print "-" * 20 + print "farming:subid=", subscription_id, "resid=", resource_uuid, "tid=", tenant_id + print "item_name=", item_name + action = getattr(api, status) action(context, subscription_id, tenant_id, resource_uuid, created_at, updated_at, expires_at, order_unit, order_size, price, currency, region_name, item_name, interval_unit, interval_size, is_prepaid) + time.sleep(600) diff --git a/dough/billing/api.py b/dough/billing/api.py index a853178..7f4b473 100644 --- a/dough/billing/api.py +++ b/dough/billing/api.py @@ -17,7 +17,6 @@ # under the License. from dateutil.relativedelta import relativedelta - from nova import utils from dough import db @@ -26,8 +25,9 @@ def creating(context, subscription_id, tenant_id, resource_uuid, created_at, updated_at, expires_at, - order_unit, order_size, price, currency, region_name, + order_unit, order_size, price, currency, region_name, item_name, interval_unit, interval_size, is_prepaid): + app = context.app conn = driver.get_connection(item_name) if not conn.is_running(resource_uuid): if created_at + relativedelta(minutes=10) < utils.utcnow(): @@ -44,6 +44,7 @@ def creating(context, subscription_id, tenant_id, resource_uuid, expires_at, order_size) print "creating", tenant_id, subscription_id, \ quantity, order_size, "\033[1;33m", price, "\033[0m" + app.info("creating %s:subid=%s,tid=%s,price=%s" % (item_name, subscription_id, tenant_id, str(price))) charge(context, tenant_id, subscription_id, quantity, order_size, price) db.subscription_extend(context, subscription_id, @@ -52,8 +53,9 @@ def creating(context, subscription_id, tenant_id, resource_uuid, def deleting(context, subscription_id, tenant_id, resource_uuid, created_at, updated_at, expires_at, - order_unit, order_size, price, currency, region_name, + order_unit, order_size, price, currency, region_name, item_name, interval_unit, interval_size, is_prepaid): + app = context.app conn = driver.get_connection(item_name) if not conn.is_terminated(resource_uuid): if updated_at + relativedelta(minutes=10) < utils.utcnow(): @@ -71,14 +73,16 @@ def deleting(context, subscription_id, tenant_id, resource_uuid, expires_at, order_size) print "deleting", tenant_id, subscription_id, \ quantity, order_size, "\033[1;33m", price, "\033[0m" + app.info("deleting %s:subid=%s,tid=%s,price=%s" % (item_name, subscription_id, tenant_id, str(price))) charge(context, tenant_id, subscription_id, quantity, order_size, price) def verified(context, subscription_id, tenant_id, resource_uuid, created_at, updated_at, expires_at, - order_unit, order_size, price, currency, region_name, + order_unit, order_size, price, currency, region_name, item_name, interval_unit, interval_size, is_prepaid): + app = context.app conn = driver.get_connection(item_name) if not conn.is_running(resource_uuid): # FIXME(lzyeval): raise Exception() @@ -91,23 +95,27 @@ def verified(context, subscription_id, tenant_id, resource_uuid, expires_at, order_size) print "verified", tenant_id, subscription_id, \ quantity, order_size, "\033[1;33m", price, "\033[0m" + app.info("verified %s:subid=%s,tid=%s,price=%s" % (item_name, subscription_id, tenant_id, str(price))) charge(context, tenant_id, subscription_id, quantity, order_size, price) db.subscription_extend(context, subscription_id, expires_at + relativedelta(**interval_info)) -def error(*args, **kwargs): +def error(context, *args, **kwargs): # TODO(lzyeval): report + print "error", args, kwargs return def charge(context, tenant_id, subscription_id, quantity, order_size, price): if not quantity: return - line_total = price * quantity / order_size + line_total = price * quantity / order_size values = { 'subscription_id': subscription_id, 'quantity': quantity, 'line_total': line_total, } + print "purchase_create, subid=", subscription_id, values + context.app.info("purchase_create:subid=%s, line_total=%s" % (subscription_id, str(line_total))) db.purchase_create(context, values) diff --git a/dough/billing/driver/load_balancer.py b/dough/billing/driver/load_balancer.py index 31f3bd3..cfdd15b 100644 --- a/dough/billing/driver/load_balancer.py +++ b/dough/billing/driver/load_balancer.py @@ -16,7 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. -import zmq +import zmq from nova import flags from nova import utils @@ -32,6 +32,7 @@ def __init__(self, protocol="tcp", host="localhost", port="80"): context = zmq.Context() self.handler = context.socket(zmq.REQ) self.handler.connect(url) + print "connect", url def __del__(self): self.handler.close() @@ -44,22 +45,35 @@ def send(self, msg_body): r_msg_type, r_msg_uuid, r_msg_body = self.handler.recv_multipart() assert (all([x == y for x, y in zip([msg_type, msg_uuid], [r_msg_type, r_msg_uuid])])) - result = utils.loads(r_msg_body)['msg'] + #result = utils.loads(r_msg_body)['msg'] + #if result['code'] == 500: + result = utils.loads(r_msg_body) if result['code'] == 500: - raise Exception() + return None else: - return result['load_balancer_ids'] - + #return result['load_balancer_ids'] + return result['data'] +print "connect load_balancer:", FLAGS.demux_host, FLAGS.demux_port DEMUX_CLIENT = Client(host=FLAGS.demux_host, port=FLAGS.demux_port) def is_running(load_balancer_uuid): # TODO(lzyeval): handle error - load_balancers = DEMUX_CLIENT.send({'cmd': 'read_load_balancer_id_all', - 'msg': {'user_name': 'foo', - 'tenant': 'bar',}}) - return load_balancer_uuid in load_balancers + #load_balancers = DEMUX_CLIENT.send({'cmd': 'read_load_balancer_id_all', + # 'msg': {'user_name': 'foo', + # 'tenant': 'bar'}}) + tenant_id = None + user_id = None + load_balancer = DEMUX_CLIENT.send({'method': 'get_load_balancer', + 'args': {'load_balancer_uuid': load_balancer_uuid, + 'tenant_id': tenant_id, + 'user_id': user_id}}) + print load_balancer_uuid, "load_balancer is_running:", load_balancer + if load_balancer is None: + return False + return 'state' in load_balancer \ + and load_balancer['state'] == "active" def is_terminated(load_balancer_uuid): diff --git a/dough/client/dough_client.py b/dough/client/dough_client.py index cfad8f2..ae2e2be 100644 --- a/dough/client/dough_client.py +++ b/dough/client/dough_client.py @@ -18,14 +18,8 @@ # under the License. import sys -#import time -#from md5 import md5 import json import zmq -#import urllib2 -#import datetime -#import base64 -#from collections import OrderedDict from nova import utils from nova import flags @@ -35,6 +29,7 @@ utils.default_flagfile(filename='/etc/dough/dough.conf') logging.setup() + api_opts = [ cfg.StrOpt('api_host', default='127.0.0.1', @@ -42,10 +37,6 @@ cfg.IntOpt('api_port', default=8783, help='Port of dough api.'), -# cfg.StrOpt('monthly_report', -# short='m', -# default='name1', -# help='monthly_report.'), ] cli_opts = [ @@ -61,6 +52,10 @@ short='u', default='default1', help='unsubscribe_item.'), + cfg.StrOpt('load_balancer', + short='l', + default='default1', + help='load_balancer.'), ] FLAGS = flags.FLAGS @@ -68,6 +63,8 @@ FLAGS.register_opts(api_opts) flags.FLAGS(sys.argv) +from dough.billing.driver import load_balancer + STANDARD_PROTOCOL = { 'method': 'query_report', 'args': { @@ -149,3 +146,23 @@ def unsubscribe_item(self, user_id, tenant_id, region, resource_uuid, item, time data = self.invoke(request) return data + + def load_balancer_get_all(self, user_id, tenant_id): + data = load_balancer.DEMUX_CLIENT.send({'method': 'get_all_load_balancers', + 'args': {'user_id': user_id, + 'tenant_id': tenant_id}}) + + return data + + def load_balancer_get(self, user_id, tenant_id, lb_id): + data = load_balancer.DEMUX_CLIENT.send({'method': 'get_load_balancer', + 'args': {'user_id': user_id, + 'tenant_id': tenant_id, + 'load_balancer_uuid': lb_id, + }}) + + return data + + def load_balancer_is_running(self, uuid): + ret = load_balancer.is_running(uuid) + return ret diff --git a/dough/db/sqlalchemy/api.py b/dough/db/sqlalchemy/api.py index bf33e6b..eee06a2 100644 --- a/dough/db/sqlalchemy/api.py +++ b/dough/db/sqlalchemy/api.py @@ -18,16 +18,14 @@ """Implementation of SQLAlchemy backend.""" -import datetime - -from sqlalchemy import and_ -from sqlalchemy import or_ -from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import joinedload -from sqlalchemy.orm import joinedload_all -from sqlalchemy.sql import func -from sqlalchemy.sql.expression import asc -from sqlalchemy.sql.expression import desc +#from sqlalchemy import and_ +#from sqlalchemy import or_ +#from sqlalchemy.exc import IntegrityError +#from sqlalchemy.orm import joinedload +#from sqlalchemy.orm import joinedload_all +#from sqlalchemy.sql import func +#from sqlalchemy.sql.expression import asc +#from sqlalchemy.sql.expression import desc from sqlalchemy.sql.expression import literal_column from nova import utils @@ -288,8 +286,8 @@ def product_get_all(context, filters=None): filters.items())) return model_query(context, models.Product).filter_by(**filters).all() -# subscriptions +# subscriptions def subscription_get(context, subscription_id): result = model_query(context, models.Subscription).\ filter_by(id=subscription_id).\ @@ -300,6 +298,7 @@ def subscription_get(context, subscription_id): def subscription_create(context, values): + print "subscription_create", values subscriptions = subscription_get_all(context, values) if subscriptions: raise Exception() From 8899377e7db609d006f27a30e64abf6f6271f697 Mon Sep 17 00:00:00 2001 From: pyw Date: Fri, 13 Jul 2012 17:42:22 +0800 Subject: [PATCH 08/11] add log --- bin/dough-api | 6 +++++- bin/dough-farmer | 2 +- dough/api.py | 8 ++++++++ etc/dough/dough.conf.sample | 8 ++++---- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/bin/dough-api b/bin/dough-api index bbec55f..e64e6ef 100755 --- a/bin/dough-api +++ b/bin/dough-api @@ -12,6 +12,7 @@ from nova import log as logging from dough import api from dough import context as dough_context +from kanyun.common.app import App utils.default_flagfile(filename='/etc/dough/dough.conf') flags.FLAGS(sys.argv) @@ -21,6 +22,7 @@ FLAGS = flags.FLAGS if __name__ == '__main__': zmq_context = zmq.Context() + app = App(conf="dough/dough.conf", name="dough-api") # Socket to receive messages on handler = zmq_context.socket(zmq.REP) @@ -41,7 +43,8 @@ if __name__ == '__main__': method = msg_body['method'] args = msg_body['args'] context = dough_context.get_context(**args) - print "-"*60 + context.app = app + print "-" * 60 print "\033[0;31m" + method + "\033[0m:" print args method_func = getattr(api, method) @@ -50,6 +53,7 @@ if __name__ == '__main__': print response except Exception, e: print traceback.format_exc() + app.error(method + ": " + str(e)) cli_msg['code'] = 500 cli_msg['message'] = str(e) response.update(cli_msg) diff --git a/bin/dough-farmer b/bin/dough-farmer index 6508478..72293c9 100755 --- a/bin/dough-farmer +++ b/bin/dough-farmer @@ -78,4 +78,4 @@ if __name__ == '__main__': order_size, price, currency, region_name, item_name, interval_unit, interval_size, is_prepaid) - time.sleep(600) + time.sleep(60 * 5) diff --git a/dough/api.py b/dough/api.py index 7353512..002d286 100644 --- a/dough/api.py +++ b/dough/api.py @@ -70,6 +70,11 @@ def subscribe_item(context, region=None, item=None, item_type=None, values['product_id'] = products[0]['id'] values['status'] = "verified" print "subscription_create", values + app = context.app + app.info("subscribe_item:proj_id=" + \ + str(context.project_id) + \ + str(resource_name) + \ + str(resource_uuid)) subscription_ref = db.subscription_create(context, values) db.subscription_extend(context, subscription_ref['id'], @@ -87,6 +92,8 @@ def unsubscribe_item(context, region=None, item=None, """ """ try: + app = context.app + app.info("unsubscribe_item:" + str(region) + "/" + str(item) + "/" + str(resource_uuid)) subscription_id = 0 subscriptions = db.subscription_get_all_by_resource_uuid(context, resource_uuid) @@ -110,6 +117,7 @@ def unsubscribe_item(context, region=None, item=None, print "subscription_get_by_resource_uuid", resource_uuid, "item=", item, "region=", region raise exception.SubscriptionNotFoundByRegionOrItem(region=region, item=item) + app.info("unsubscribe_item:subs_id=" + str(subscription_id)) db.subscription_destroy(context, subscription_id) except Exception, e: # TODO(lzyeval): report diff --git a/etc/dough/dough.conf.sample b/etc/dough/dough.conf.sample index 78cba15..8eb9cde 100644 --- a/etc/dough/dough.conf.sample +++ b/etc/dough/dough.conf.sample @@ -1,17 +1,17 @@ [DEFAULT] sql_connection=mysql://someuser:somepasswd@localhost/dough api_listen=localhost -api_listen_port=5557 +api_listen_port=5554 farmer_listen=localhost -farmer_listen_port=5558 +farmer_listen_port=5553 keystone_username=someusername keystone_password=somepassword keystone_tenant_name=sometenant keystone_auth_url=someurl demux_host=localhost -demux_port=5559 +demux_port=5556 kanyun_host=localhost -kanyun_port=5560 +kanyun_port=5552 mysql_host=localhost mysql_port=3306 mysql_user=nova From 5bc115b0782845f850fc1b8dbc25bea9256386b8 Mon Sep 17 00:00:00 2001 From: pyw Date: Mon, 16 Jul 2012 11:49:25 +0800 Subject: [PATCH 09/11] old version support --- dough/api.py | 13 +++++++------ dough/billing/api.py | 8 +++++--- dough/billing/driver/load_balancer.py | 2 ++ dough/db/sqlalchemy/api.py | 1 - etc/dough/dough.conf.sample | 6 ++++++ 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/dough/api.py b/dough/api.py index 002d286..26b2983 100644 --- a/dough/api.py +++ b/dough/api.py @@ -69,12 +69,13 @@ def subscribe_item(context, region=None, item=None, item_type=None, # TODO(lzyeval): check if products size is not 1 values['product_id'] = products[0]['id'] values['status'] = "verified" - print "subscription_create", values + print "subscription_create", item, payment_type, values app = context.app - app.info("subscribe_item:proj_id=" + \ - str(context.project_id) + \ - str(resource_name) + \ - str(resource_uuid)) + app.info("subscribe_item:proj_id=" + str(context.project_id) + \ + " name:" + str(resource_name) + \ + "/" + str(item) + \ + "/" + str(payment_type) + \ + "/" + str(resource_uuid)) subscription_ref = db.subscription_create(context, values) db.subscription_extend(context, subscription_ref['id'], @@ -117,7 +118,7 @@ def unsubscribe_item(context, region=None, item=None, print "subscription_get_by_resource_uuid", resource_uuid, "item=", item, "region=", region raise exception.SubscriptionNotFoundByRegionOrItem(region=region, item=item) - app.info("unsubscribe_item:subs_id=" + str(subscription_id)) + app.info("\tsubs_id=" + str(subscription_id)) db.subscription_destroy(context, subscription_id) except Exception, e: # TODO(lzyeval): report diff --git a/dough/billing/api.py b/dough/billing/api.py index 7f4b473..2df581c 100644 --- a/dough/billing/api.py +++ b/dough/billing/api.py @@ -30,6 +30,7 @@ def creating(context, subscription_id, tenant_id, resource_uuid, app = context.app conn = driver.get_connection(item_name) if not conn.is_running(resource_uuid): + print "creating wait running", tenant_id, subscription_id if created_at + relativedelta(minutes=10) < utils.utcnow(): db.subscription_error(context, subscription_id) # TODO(lzyeval): report @@ -42,7 +43,7 @@ def creating(context, subscription_id, tenant_id, resource_uuid, quantity = conn.get_usage(resource_uuid, expires_at - relativedelta(**interval_info), expires_at, order_size) - print "creating", tenant_id, subscription_id, \ + print "creating and is running", tenant_id, subscription_id, \ quantity, order_size, "\033[1;33m", price, "\033[0m" app.info("creating %s:subid=%s,tid=%s,price=%s" % (item_name, subscription_id, tenant_id, str(price))) charge(context, tenant_id, subscription_id, quantity, @@ -116,6 +117,7 @@ def charge(context, tenant_id, subscription_id, quantity, order_size, price): 'quantity': quantity, 'line_total': line_total, } - print "purchase_create, subid=", subscription_id, values - context.app.info("purchase_create:subid=%s, line_total=%s" % (subscription_id, str(line_total))) + print "purchase_create, tenant_id=%s, subid=%s" % (tenant_id, subscription_id) + print values + context.app.info("purchase_create:tenant_id=%s, subid=%s, line_total=%s" % (tenant_id, subscription_id, str(line_total))) db.purchase_create(context, values) diff --git a/dough/billing/driver/load_balancer.py b/dough/billing/driver/load_balancer.py index cfdd15b..4292a0e 100644 --- a/dough/billing/driver/load_balancer.py +++ b/dough/billing/driver/load_balancer.py @@ -48,6 +48,8 @@ def send(self, msg_body): #result = utils.loads(r_msg_body)['msg'] #if result['code'] == 500: result = utils.loads(r_msg_body) + if "msg" in result: # FIXME: old version support + result = result["msg"] if result['code'] == 500: return None else: diff --git a/dough/db/sqlalchemy/api.py b/dough/db/sqlalchemy/api.py index eee06a2..4105a24 100644 --- a/dough/db/sqlalchemy/api.py +++ b/dough/db/sqlalchemy/api.py @@ -298,7 +298,6 @@ def subscription_get(context, subscription_id): def subscription_create(context, values): - print "subscription_create", values subscriptions = subscription_get_all(context, values) if subscriptions: raise Exception() diff --git a/etc/dough/dough.conf.sample b/etc/dough/dough.conf.sample index 8eb9cde..03b09ee 100644 --- a/etc/dough/dough.conf.sample +++ b/etc/dough/dough.conf.sample @@ -1,3 +1,9 @@ +[dough-api] +log: /var/log/dough-api.log + +[farmer] +log: /var/log/dough-farmer.log + [DEFAULT] sql_connection=mysql://someuser:somepasswd@localhost/dough api_listen=localhost From 4da078ead98b0aa43acab415b8747a80df03db56 Mon Sep 17 00:00:00 2001 From: pyw Date: Mon, 16 Jul 2012 12:07:13 +0800 Subject: [PATCH 10/11] add try for the farmer --- bin/dough-farmer | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/dough-farmer b/bin/dough-farmer index 72293c9..4e8f3f7 100755 --- a/bin/dough-farmer +++ b/bin/dough-farmer @@ -73,9 +73,11 @@ if __name__ == '__main__': print "item_name=", item_name action = getattr(api, status) - action(context, subscription_id, tenant_id, resource_uuid, + try: + action(context, subscription_id, tenant_id, resource_uuid, created_at, updated_at, expires_at, order_unit, order_size, price, currency, region_name, item_name, interval_unit, interval_size, is_prepaid) - + except Exception, e: + print e, subscription_id, item_name time.sleep(60 * 5) From beda4fa5ce34b6e21f93b61aaec41decc103c972 Mon Sep 17 00:00:00 2001 From: pyw Date: Wed, 18 Jul 2012 14:59:27 +0800 Subject: [PATCH 11/11] fix call load_balance bug:uuid/tenant_id;add log; --- bin/dough-farmer | 7 ++-- bin/dough-manager | 52 +++++++++++++-------------- dough/billing/api.py | 33 +++++++++++------ dough/billing/driver/floating_ip.py | 2 +- dough/billing/driver/instance.py | 2 +- dough/billing/driver/load_balancer.py | 9 +++-- dough/billing/driver/network.py | 2 +- dough/db/sqlalchemy/api.py | 1 + 8 files changed, 63 insertions(+), 45 deletions(-) diff --git a/bin/dough-farmer b/bin/dough-farmer index 4e8f3f7..796fac3 100755 --- a/bin/dough-farmer +++ b/bin/dough-farmer @@ -74,10 +74,11 @@ if __name__ == '__main__': action = getattr(api, status) try: - action(context, subscription_id, tenant_id, resource_uuid, + action(context, subscription_id, tenant_id, item_name, resource_uuid, created_at, updated_at, expires_at, order_unit, order_size, price, currency, region_name, - item_name, interval_unit, interval_size, is_prepaid) + interval_unit, interval_size, is_prepaid) except Exception, e: print e, subscription_id, item_name - time.sleep(60 * 5) + print "" + time.sleep(60 * 2) diff --git a/bin/dough-manager b/bin/dough-manager index dd43f5e..6ca9449 100755 --- a/bin/dough-manager +++ b/bin/dough-manager @@ -16,25 +16,19 @@ # 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 datetime import sys -import time -import traceback - +import iso8601 #from dateutil.relativedelta import relativedelta #import zmq from nova import flags -from nova import log as logging from nova import utils -#from dough import billing -# from dough.billing import api from dough import context as dough_context from dough import db from dough import api from nova.openstack.common import cfg -#from dough import exception +from dough.billing import driver utils.default_flagfile(filename='/etc/dough/dough.conf') FLAGS = flags.FLAGS @@ -58,9 +52,11 @@ manager_opts = [ FLAGS.register_cli_opts(manager_opts) flags.FLAGS(sys.argv) -def show_usage(): + +def show_usage(): print "dough-mansger -n " - + + def get_subs(context, param): print "filter: resource_name=", param try: @@ -68,22 +64,23 @@ def get_subs(context, param): subscription_id = sub['id'] status = sub['status'] tenant_id = sub['project_id'] - resource_uuid = sub['resource_uuid'] + #resource_uuid = sub['resource_uuid'] created_at = sub['created_at'] - updated_at = sub['updated_at'] + #updated_at = sub['updated_at'] expires_at = sub['expires_at'] - order_unit = sub['product']['order_unit'] - order_size = sub['product']['order_size'] + #order_unit = sub['product']['order_unit'] + #order_size = sub['product']['order_size'] price = sub['product']['price'] - currency = sub['product']['currency'] + #currency = sub['product']['currency'] region_name = sub['product']['region']['name'] item_name = sub['product']['item']['name'] - pay_type = sub['product']['payment_type'] - interval_unit = pay_type['interval_unit'] - interval_size = pay_type['interval_size'] - is_prepaid = pay_type['is_prepaid'] - print '-'*60 + #pay_type = sub['product']['payment_type'] + #interval_unit = pay_type['interval_unit'] + #interval_size = pay_type['interval_size'] + #is_prepaid = pay_type['is_prepaid'] + print '-' * 60 + print "%24s : %s" % ("status", str(status)) print "%24s : %s" % ("subscription_id", str(subscription_id)) print "%24s : %s" % ("tenant_id", str(tenant_id)) print "%24s : %s" % ("expires_at", str(expires_at)) @@ -95,11 +92,13 @@ def get_subs(context, param): except Exception, e: print Exception, e + def query_report(tenant_id, timestamp_from, timestamp_to): datetime_from = iso8601.parse_date(timestamp_from) datetime_to = iso8601.parse_date(timestamp_to) - context = dough_context.get_context(tenant_id=self.tenant_id) - data = api.query_report(self.context, + print datetime_from, datetime_to + context = dough_context.get_context(tenant_id=tenant_id) + data = api.query_report(context, timestamp_from, timestamp_to) return data @@ -108,6 +107,7 @@ def query_report(tenant_id, timestamp_from, timestamp_to): select id, subscription_id, created_at, quantity from purchases where subscription_id =151 and created_at>"2012-06-03 00:00:00"; """ + def main(): baselen = 2 context = dough_context.get_admin_context() @@ -115,10 +115,10 @@ def main(): get_subs(context, sys.argv) return if len(sys.argv) == baselen: - if sys.argv[baselen-1] in ['--help', "-h", "?"]: + if sys.argv[baselen - 1] in ['--help', "-h", "?"]: show_usage() return - + if len(sys.argv) == baselen + 2: param = sys.argv[baselen + 1] if sys.argv[baselen][:2] == '-r': @@ -128,7 +128,7 @@ def main(): timestamp_from = sys.argv[baselen + 2] timestamp_to = sys.argv[baselen + 3] print query_report(tenant_id, timestamp_from, timestamp_to) - - + + if __name__ == '__main__': main() diff --git a/dough/billing/api.py b/dough/billing/api.py index 2df581c..0ffd3c9 100644 --- a/dough/billing/api.py +++ b/dough/billing/api.py @@ -23,21 +23,23 @@ from dough.billing import driver -def creating(context, subscription_id, tenant_id, resource_uuid, +def creating(context, subscription_id, tenant_id, item_name, resource_uuid, created_at, updated_at, expires_at, order_unit, order_size, price, currency, region_name, - item_name, interval_unit, interval_size, is_prepaid): + interval_unit, interval_size, is_prepaid): app = context.app conn = driver.get_connection(item_name) if not conn.is_running(resource_uuid): - print "creating wait running", tenant_id, subscription_id + app.info("wait:%s creating, but %s not running." % (str(subscription_id), item_name)) if created_at + relativedelta(minutes=10) < utils.utcnow(): + app.info("%s(%s) status creating-->error" % (str(subscription_id), item_name)) db.subscription_error(context, subscription_id) # TODO(lzyeval): report else: interval_info = { interval_unit: interval_size, } + app.info("%s(%s) status creating-->verify" % (str(subscription_id), item_name)) db.subscription_verify(context, subscription_id) if is_prepaid: quantity = conn.get_usage(resource_uuid, @@ -48,22 +50,27 @@ def creating(context, subscription_id, tenant_id, resource_uuid, app.info("creating %s:subid=%s,tid=%s,price=%s" % (item_name, subscription_id, tenant_id, str(price))) charge(context, tenant_id, subscription_id, quantity, order_size, price) + else: + app.info("%s/%s/%s is_prepaid" % (tenant_id, str(subscription_id), item_name)) db.subscription_extend(context, subscription_id, expires_at + relativedelta(**interval_info)) -def deleting(context, subscription_id, tenant_id, resource_uuid, +def deleting(context, subscription_id, tenant_id, item_name, resource_uuid, created_at, updated_at, expires_at, order_unit, order_size, price, currency, region_name, - item_name, interval_unit, interval_size, is_prepaid): + interval_unit, interval_size, is_prepaid): app = context.app conn = driver.get_connection(item_name) if not conn.is_terminated(resource_uuid): + app.info("wait:%s deleting, but %s not terminated." % (str(subscription_id), item_name)) if updated_at + relativedelta(minutes=10) < utils.utcnow(): + app.info("%s(%s) status deleting-->error" % (str(subscription_id), item_name)) db.subscription_error(context, subscription_id) # TODO(lzyeval): report else: # TODO(lzyeval): implement + app.info("%s(%s) status deleting-->terminated" % (str(subscription_id), item_name)) db.subscription_terminate(context, subscription_id) if not is_prepaid: interval_info = { @@ -74,19 +81,22 @@ def deleting(context, subscription_id, tenant_id, resource_uuid, expires_at, order_size) print "deleting", tenant_id, subscription_id, \ quantity, order_size, "\033[1;33m", price, "\033[0m" - app.info("deleting %s:subid=%s,tid=%s,price=%s" % (item_name, subscription_id, tenant_id, str(price))) + app.info("deleting %s(%s),tid=%s,price=%s" % (subscription_id, item_name, tenant_id, str(price))) charge(context, tenant_id, subscription_id, quantity, order_size, price) + else: + app.info("%s/%s/%s is_prepaid" % (tenant_id, str(subscription_id), item_name)) -def verified(context, subscription_id, tenant_id, resource_uuid, +def verified(context, subscription_id, tenant_id, item_name, resource_uuid, created_at, updated_at, expires_at, order_unit, order_size, price, currency, region_name, - item_name, interval_unit, interval_size, is_prepaid): + interval_unit, interval_size, is_prepaid): app = context.app conn = driver.get_connection(item_name) - if not conn.is_running(resource_uuid): + if not conn.is_running(resource_uuid, tenant_id=tenant_id): # FIXME(lzyeval): raise Exception() + app.info("%s verified, but %s not running." % (str(subscription_id), item_name)) return interval_info = { interval_unit: interval_size, @@ -102,9 +112,10 @@ def verified(context, subscription_id, tenant_id, resource_uuid, expires_at + relativedelta(**interval_info)) -def error(context, *args, **kwargs): +def error(context, subscription_id, tenant_id, item_name, *args, **kwargs): # TODO(lzyeval): report - print "error", args, kwargs +# print "[BillingAPI]error", args, kwargs +# context.app.info("error:%s(%s)" % (subscription_id, item_name)) return diff --git a/dough/billing/driver/floating_ip.py b/dough/billing/driver/floating_ip.py index 5394fa2..3f6a8ad 100644 --- a/dough/billing/driver/floating_ip.py +++ b/dough/billing/driver/floating_ip.py @@ -29,7 +29,7 @@ service_type="compute") -def is_running(floating_ip_uuid): +def is_running(floating_ip_uuid, **kwargs): return not is_terminated(floating_ip_uuid) diff --git a/dough/billing/driver/instance.py b/dough/billing/driver/instance.py index f39c289..f805cec 100644 --- a/dough/billing/driver/instance.py +++ b/dough/billing/driver/instance.py @@ -29,7 +29,7 @@ service_type="compute") -def is_running(instance_uuid): +def is_running(instance_uuid, **kwargs): try: instance = NOVA_CLIENT.servers.get(instance_uuid) except Exception: diff --git a/dough/billing/driver/load_balancer.py b/dough/billing/driver/load_balancer.py index 4292a0e..93d38d5 100644 --- a/dough/billing/driver/load_balancer.py +++ b/dough/billing/driver/load_balancer.py @@ -60,15 +60,20 @@ def send(self, msg_body): DEMUX_CLIENT = Client(host=FLAGS.demux_host, port=FLAGS.demux_port) -def is_running(load_balancer_uuid): +def is_running(load_balancer_uuid, **kwargs): # TODO(lzyeval): handle error #load_balancers = DEMUX_CLIENT.send({'cmd': 'read_load_balancer_id_all', # 'msg': {'user_name': 'foo', # 'tenant': 'bar'}}) + load_balancer = None tenant_id = None user_id = None + if "tenant_id" in kwargs: + tenant_id = kwargs['tenant_id'] + if "user_id" in kwargs: + user_id = kwargs['user_id'] load_balancer = DEMUX_CLIENT.send({'method': 'get_load_balancer', - 'args': {'load_balancer_uuid': load_balancer_uuid, + 'args': {'uuid': load_balancer_uuid, 'tenant_id': tenant_id, 'user_id': user_id}}) print load_balancer_uuid, "load_balancer is_running:", load_balancer diff --git a/dough/billing/driver/network.py b/dough/billing/driver/network.py index aa2f118..f09d954 100644 --- a/dough/billing/driver/network.py +++ b/dough/billing/driver/network.py @@ -69,7 +69,7 @@ def send(self, msg_body): KANYUN_CLIENT = Client(host=FLAGS.kanyun_host, port=FLAGS.kanyun_port) -def is_running(instance_uuid): +def is_running(instance_uuid, **kwargs): try: instance = NOVA_CLIENT.servers.get(instance_uuid) except Exception: diff --git a/dough/db/sqlalchemy/api.py b/dough/db/sqlalchemy/api.py index 4105a24..9ac222c 100644 --- a/dough/db/sqlalchemy/api.py +++ b/dough/db/sqlalchemy/api.py @@ -345,6 +345,7 @@ def subscription_error(context, subscription_id): def subscription_extend(context, subscription_id, datetime_to): + print "[DB]", subscription_id, "extend to", datetime_to session = get_session() with session.begin(): session.query(models.Subscription).\