From 5ef009cc9bb15b5bc9590eb59114976059d578cd Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Sun, 9 Feb 2020 12:12:15 +0100 Subject: [PATCH] Begin to phase in features and processing orders --- uncloud/hack/db.py | 8 ++- uncloud/hack/main.py | 13 ++++- uncloud/hack/product.py | 119 ++++++++++++++++++++++++++++------------ uncloud/hack/vm.py | 6 ++ 4 files changed, 108 insertions(+), 38 deletions(-) diff --git a/uncloud/hack/db.py b/uncloud/hack/db.py index 3e9a3c6..7798bd2 100644 --- a/uncloud/hack/db.py +++ b/uncloud/hack/db.py @@ -81,9 +81,13 @@ class DB(object): @readable_errors def get_prefix(self, key, as_json=False, **kwargs): - key_range = self._db_clients[0].get_prefix(self.realkey(key), **kwargs) + for value, meta in self._db_clients[0].get_prefix(self.realkey(key), **kwargs): + k = meta.key.decode("utf-8") + value = value.decode("utf-8") + if as_json: + value = json.loads(value) - return key_range + yield (k, value) @readable_errors diff --git a/uncloud/hack/main.py b/uncloud/hack/main.py index e3e6dc4..a76d210 100644 --- a/uncloud/hack/main.py +++ b/uncloud/hack/main.py @@ -43,6 +43,8 @@ arg_parser.add_argument('--hackprefix', help="hackprefix, if you need it you kno # order based commands => later to be shifted below "order" arg_parser.add_argument('--order', action='store_true') arg_parser.add_argument('--list-orders', help="List all orders", action='store_true') +arg_parser.add_argument('--process-orders', help="Process all (pending) orders", action='store_true') + arg_parser.add_argument('--product', choices=["dualstack-vm"]) arg_parser.add_argument('--os-image-name', help="Name of OS image (successor to --image)") arg_parser.add_argument('--os-image-size', help="Size of OS image in GB", type=int, default=10) @@ -51,6 +53,10 @@ arg_parser.add_argument('--username') arg_parser.add_argument('--password') arg_parser.add_argument('--api', help="Run the API") +arg_parser.add_argument('--mode', + choices=["direct", "api", "client"], + default="client", + help="Directly manipulate etcd, spawn the API server or behave as a client") @@ -101,7 +107,12 @@ def main(arguments): if arguments['list_orders']: p = ProductOrder(config) - p.list_orders() + for product_order in p.list_orders(): + print("Order {}: {}".format(product_order.db_entry['uuid'], product_order.db_entry)) + + if arguments['process_orders']: + p = ProductOrder(config) + p.process_orders() if arguments['create_vm']: vm = VM(config) diff --git a/uncloud/hack/product.py b/uncloud/hack/product.py index 925fcdc..97f64f0 100755 --- a/uncloud/hack/product.py +++ b/uncloud/hack/product.py @@ -20,52 +20,40 @@ import json import uuid +import logging from uncloud import UncloudException from uncloud.hack.db import DB +log = logging.getLogger(__name__) + class ProductOrder(object): - def __init__(self, config): + def __init__(self, config, product_entry=None, db_entry=None): self.config = config self.db = DB(self.config, prefix="/orders") - - def list_orders(self, filter_key=None, filter_regexp_value=None): - for k,m in self.db.get_prefix(""): - print("{} {}".format(k,m)) - - -class Product(object): - def __init__(self, config, product_name, db_entry=None): - self.config = config - self.db = DB(self.config, prefix="/orders") - self.db_entry = {} - self.db_entry["product_name"] = product_name self.db_entry["db_version"] = 1 + self.db_entry["product"] = product_entry - # Existing product? Read in db_entry + + # Overwrite if we are loading an existing product order if db_entry: self.db_entry = db_entry + # FIXME: this should return a list of our class! + def list_orders(self, filter_key=None, filter_regexp_value=None): + """List all orders with - filtering not yet implemented """ - @staticmethod - def define_feature(self, - name, - feature, - one_time_price, - recurring_price, - recurring_period, - minimum_period): - feature = {} - feature[name] = {} + for k,v in self.db.get_prefix("", as_json=True): + log.debug("{} {}".format(k,v)) - def valid_status(self): - if "status" in self.db_entry: - if self.db_entry["status"] in [ "NEW", "SCHEDULED", "CREATED", "DELETED" ]: - return False - return True + yield self.__class__(self.config, db_entry=v) - def validate_product(self): + def process_orders(self): + for orders in self.list_orders(): + pass + + def set_required_values(self): if not "uuid" in self.db_entry: self.db_entry["uuid"] = str(uuid.uuid4()) if not "status" in self.db_entry: @@ -73,14 +61,75 @@ class Product(object): if not "owner" in self.db_entry: self.db_entry["owner"] = "UNKNOWN" + def validate_status(self): + if "status" in self.db_entry: + if self.db_entry["status"] in [ "NEW", "SCHEDULED", "CREATED", "DELETED", "REJECTED" ]: + return False + return True + + def order(self): + if not self.db_entry["status"] == "NEW": + raise UncloudException("Cannot re-order same order. Status: {}".format(self.db_entry["status"])) + + +class Product(object): + def __init__(self, + config, + product_name, + db_entry=None): + self.config = config + self.db = DB(self.config, prefix="/orders") + + self.db_entry = {} + self.db_entry["product_name"] = product_name + self.db_entry["db_version"] = 1 + self.db_entry["features"] = {} + + # Existing product? Read in db_entry + if db_entry: + self.db_entry = db_entry + + self.valid_periods = [ "per_year", "per_month", "per_week", + "per_day", "per_hour", + "per_minute", "per_second" ] + + def define_feature(self, + name, + one_time_price, + recurring_price, + recurring_period, + minimum_period): + + self.db_entry['features'][name] = {} + self.db_entry['features'][name]['one_time_price'] = one_time_price + self.db_entry['features'][name]['recurring_price'] = recurring_price + + if not recurring_period in self.valid_periods: + raise UncloudException("Invalid recurring period: {}".format(recurring_period)) + + self.db_entry['features'][name]['recurring_period'] = recurring_period + + if not minimum_period in self.valid_periods: + raise UncloudException("Invalid recurring period: {}".format(recurring_period)) + + recurring_index = self.valid_periods.index(recurring_period) + minimum_index = self.valid_periods.index(minimum_period) + + if minimum_index < recurring_index: + raise UncloudException("Minimum period for product '{}' feature '{}' must be shorter or equal than/as recurring period: {} > {}".format(self.db_entry['product_name'], name, minimum_period, recurring_period)) + + self.db_entry['features'][name]['minimum_period'] = minimum_period + + + def validate_product(self): + for feature in self.db_entry['features']: + pass def place_order(self): """ Schedule creating the product in etcd """ - self.validate_product() - - # FIXME: very status - if not self.db_entry["status"] == "NEW": - raise UncloudException("Cannot re-order product") + order = ProductOrder(self.config, self.db_entry) + order.set_required_values() + order.order() self.db.set(self.db_entry["uuid"], str(self)) diff --git a/uncloud/hack/vm.py b/uncloud/hack/vm.py index 695e33b..981b519 100755 --- a/uncloud/hack/vm.py +++ b/uncloud/hack/vm.py @@ -90,6 +90,12 @@ class VM(object): self.vm = {} self.product = Product(config, product_name="dualstack-vm") + self.product.define_feature(name="base", + one_time_price=0, + recurring_price=9, + recurring_period="per_month", + minimum_period="per_hour") + self.features = [] # self.features.append(self.define_feature(