diff --git a/uncloud/hack/__init__.py b/uncloud/hack/__init__.py index e69de29..8b13789 100644 --- a/uncloud/hack/__init__.py +++ b/uncloud/hack/__init__.py @@ -0,0 +1 @@ + diff --git a/uncloud/hack/db.py b/uncloud/hack/db.py index 7798bd2..a4395de 100644 --- a/uncloud/hack/db.py +++ b/uncloud/hack/db.py @@ -23,12 +23,20 @@ import etcd3 import json import logging +import datetime from functools import wraps from uncloud import UncloudException log = logging.getLogger(__name__) +def db_logentry(message): + timestamp = datetime.datetime.now() + return { + "timestamp": str(timestamp), + "message": message + } + def readable_errors(func): @wraps(func) @@ -99,6 +107,8 @@ class DB(object): # FIXME: iterate over clients in case of failure ? return self._db_clients[0].put(self.realkey(key), value, **kwargs) + + @readable_errors def increment(self, key, **kwargs): print(self.realkey(key)) diff --git a/uncloud/hack/main.py b/uncloud/hack/main.py index a76d210..c454b03 100644 --- a/uncloud/hack/main.py +++ b/uncloud/hack/main.py @@ -1,8 +1,10 @@ import argparse import logging +import re import ldap3 + from uncloud.hack.vm import VM from uncloud.hack.config import Config from uncloud.hack.mac import MAC @@ -43,6 +45,9 @@ 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('--filter-order-key', help="Which key to filter on") +arg_parser.add_argument('--filter-order-regexp', help="Which regexp the value should match") + arg_parser.add_argument('--process-orders', help="Process all (pending) orders", action='store_true') arg_parser.add_argument('--product', choices=["dualstack-vm"]) @@ -107,7 +112,8 @@ def main(arguments): if arguments['list_orders']: p = ProductOrder(config) - for product_order in p.list_orders(): + for product_order in p.list_orders(filter_key=arguments['filter_order_key'], + filter_regexp=arguments['filter_order_regexp']): print("Order {}: {}".format(product_order.db_entry['uuid'], product_order.db_entry)) if arguments['process_orders']: diff --git a/uncloud/hack/product.py b/uncloud/hack/product.py index 97f64f0..668b8ea 100755 --- a/uncloud/hack/product.py +++ b/uncloud/hack/product.py @@ -21,9 +21,10 @@ import json import uuid import logging +import re from uncloud import UncloudException -from uncloud.hack.db import DB +from uncloud.hack.db import DB, db_logentry log = logging.getLogger(__name__) @@ -32,38 +33,45 @@ class ProductOrder(object): self.config = config self.db = DB(self.config, prefix="/orders") self.db_entry = {} - self.db_entry["db_version"] = 1 self.db_entry["product"] = product_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): + def list_orders(self, filter_key=None, filter_regexp=None): """List all orders with - filtering not yet implemented """ for k,v in self.db.get_prefix("", as_json=True): log.debug("{} {}".format(k,v)) - - yield self.__class__(self.config, db_entry=v) - - def process_orders(self): - for orders in self.list_orders(): - pass + if filter_key and filter_regexp: + if filter_key in v: + if re.match(filter_regexp, v[filter_key]): + yield self.__class__(self.config, db_entry=v) + else: + yield self.__class__(self.config, db_entry=v) def set_required_values(self): + """Set values that are required to make the db entry valid""" if not "uuid" in self.db_entry: self.db_entry["uuid"] = str(uuid.uuid4()) if not "status" in self.db_entry: self.db_entry["status"] = "NEW" if not "owner" in self.db_entry: self.db_entry["owner"] = "UNKNOWN" + if not "log" in self.db_entry: + self.db_entry["log"] = [] + if not "db_version" in self.db_entry: + self.db_entry["db_version"] = 1 def validate_status(self): if "status" in self.db_entry: - if self.db_entry["status"] in [ "NEW", "SCHEDULED", "CREATED", "DELETED", "REJECTED" ]: + if self.db_entry["status"] in [ "NEW", + "SCHEDULED", + "CREATED_ACTIVE", + "CANCELLED", + "REJECTED" ]: return False return True @@ -71,6 +79,25 @@ class ProductOrder(object): if not self.db_entry["status"] == "NEW": raise UncloudException("Cannot re-order same order. Status: {}".format(self.db_entry["status"])) + def process_orders(self): + for order in self.list_orders(): + if order.db_entry["status"] == "NEW": + log.info("Handling new order: {}".format(order)) + + # FIXME: these all should be a transactions! -> fix concurrent access! ! + if not "log" in order.db_entry: + order.db_entry['log'] = [] + + for must_attribute in [ "owner", "product" ]: + if not must_attribute in order.db_entry: + order.db_entry['log'].append(db_logentry("Missing {} entry, rejecting order".format(must_attribute))) + order.db_entry['status'] = "REJECTED" + self.db.set(order.db_entry['uuid'], order.db_entry, as_json=True) + + + + def __str__(self): + return str(self.db_entry) class Product(object): def __init__(self, @@ -83,6 +110,7 @@ class Product(object): self.db_entry = {} self.db_entry["product_name"] = product_name self.db_entry["db_version"] = 1 + self.db_entry["log"] = [] self.db_entry["features"] = {} # Existing product? Read in db_entry @@ -127,12 +155,9 @@ class Product(object): def place_order(self): """ Schedule creating the product in etcd """ - order = ProductOrder(self.config, self.db_entry) + order = ProductOrder(self.config, product_entry=self.db_entry) order.set_required_values() order.order() - self.db.set(self.db_entry["uuid"], str(self)) - - def __str__(self): return json.dumps(self.db_entry) diff --git a/uncloud/hack/vm.py b/uncloud/hack/vm.py index 981b519..6bbe29a 100755 --- a/uncloud/hack/vm.py +++ b/uncloud/hack/vm.py @@ -101,6 +101,7 @@ class VM(object): # self.features.append(self.define_feature( # self.super().__init__( + def get_qemu_args(self): command = ( "-name {owner}-{name}"