Add filtering support:

(venv) [12:54] diamond:uncloud% ./bin/uncloud-run-reinstall hack --product 'dualstack-vm' --os-image-name alpine311 --username nicocustomer --password '...' --hackprefix ~/vcs/uncloud/uncloud/hack/hackcloud/ --etcd-host etcd1.ungleich.ch --etcd-ca-cert ~/vcs/ungleich-dot-cdist/files/etcd/ca.pem --etcd-cert-cert ~/vcs/ungleich-dot-cdist/files/etcd/nico.pem --etcd-cert-key ~/vcs/ungleich-dot-cdist/files/etcd/nico-key.pem --list-orders --filter-order-key "status" --filter-order-regexp NEW
This commit is contained in:
Nico Schottelius 2020-02-09 12:54:52 +01:00
parent 5ef009cc9b
commit a80a279ba5
5 changed files with 59 additions and 16 deletions

View file

@ -0,0 +1 @@

View file

@ -23,12 +23,20 @@
import etcd3 import etcd3
import json import json
import logging import logging
import datetime
from functools import wraps from functools import wraps
from uncloud import UncloudException from uncloud import UncloudException
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def db_logentry(message):
timestamp = datetime.datetime.now()
return {
"timestamp": str(timestamp),
"message": message
}
def readable_errors(func): def readable_errors(func):
@wraps(func) @wraps(func)
@ -99,6 +107,8 @@ class DB(object):
# FIXME: iterate over clients in case of failure ? # FIXME: iterate over clients in case of failure ?
return self._db_clients[0].put(self.realkey(key), value, **kwargs) return self._db_clients[0].put(self.realkey(key), value, **kwargs)
@readable_errors @readable_errors
def increment(self, key, **kwargs): def increment(self, key, **kwargs):
print(self.realkey(key)) print(self.realkey(key))

View file

@ -1,8 +1,10 @@
import argparse import argparse
import logging import logging
import re
import ldap3 import ldap3
from uncloud.hack.vm import VM from uncloud.hack.vm import VM
from uncloud.hack.config import Config from uncloud.hack.config import Config
from uncloud.hack.mac import MAC 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" # order based commands => later to be shifted below "order"
arg_parser.add_argument('--order', action='store_true') 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('--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('--process-orders', help="Process all (pending) orders", action='store_true')
arg_parser.add_argument('--product', choices=["dualstack-vm"]) arg_parser.add_argument('--product', choices=["dualstack-vm"])
@ -107,7 +112,8 @@ def main(arguments):
if arguments['list_orders']: if arguments['list_orders']:
p = ProductOrder(config) 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)) print("Order {}: {}".format(product_order.db_entry['uuid'], product_order.db_entry))
if arguments['process_orders']: if arguments['process_orders']:

View file

@ -21,9 +21,10 @@
import json import json
import uuid import uuid
import logging import logging
import re
from uncloud import UncloudException from uncloud import UncloudException
from uncloud.hack.db import DB from uncloud.hack.db import DB, db_logentry
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -32,38 +33,45 @@ class ProductOrder(object):
self.config = config self.config = config
self.db = DB(self.config, prefix="/orders") self.db = DB(self.config, prefix="/orders")
self.db_entry = {} self.db_entry = {}
self.db_entry["db_version"] = 1
self.db_entry["product"] = product_entry self.db_entry["product"] = product_entry
# Overwrite if we are loading an existing product order # Overwrite if we are loading an existing product order
if db_entry: if db_entry:
self.db_entry = db_entry self.db_entry = db_entry
# FIXME: this should return a list of our class! # 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 """ """List all orders with - filtering not yet implemented """
for k,v in self.db.get_prefix("", as_json=True): for k,v in self.db.get_prefix("", as_json=True):
log.debug("{} {}".format(k,v)) log.debug("{} {}".format(k,v))
if filter_key and filter_regexp:
yield self.__class__(self.config, db_entry=v) if filter_key in v:
if re.match(filter_regexp, v[filter_key]):
def process_orders(self): yield self.__class__(self.config, db_entry=v)
for orders in self.list_orders(): else:
pass yield self.__class__(self.config, db_entry=v)
def set_required_values(self): def set_required_values(self):
"""Set values that are required to make the db entry valid"""
if not "uuid" in self.db_entry: if not "uuid" in self.db_entry:
self.db_entry["uuid"] = str(uuid.uuid4()) self.db_entry["uuid"] = str(uuid.uuid4())
if not "status" in self.db_entry: if not "status" in self.db_entry:
self.db_entry["status"] = "NEW" self.db_entry["status"] = "NEW"
if not "owner" in self.db_entry: if not "owner" in self.db_entry:
self.db_entry["owner"] = "UNKNOWN" 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): def validate_status(self):
if "status" in self.db_entry: 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 False
return True return True
@ -71,6 +79,25 @@ class ProductOrder(object):
if not self.db_entry["status"] == "NEW": if not self.db_entry["status"] == "NEW":
raise UncloudException("Cannot re-order same order. Status: {}".format(self.db_entry["status"])) 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): class Product(object):
def __init__(self, def __init__(self,
@ -83,6 +110,7 @@ class Product(object):
self.db_entry = {} self.db_entry = {}
self.db_entry["product_name"] = product_name self.db_entry["product_name"] = product_name
self.db_entry["db_version"] = 1 self.db_entry["db_version"] = 1
self.db_entry["log"] = []
self.db_entry["features"] = {} self.db_entry["features"] = {}
# Existing product? Read in db_entry # Existing product? Read in db_entry
@ -127,12 +155,9 @@ class Product(object):
def place_order(self): def place_order(self):
""" Schedule creating the product in etcd """ """ 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.set_required_values()
order.order() order.order()
self.db.set(self.db_entry["uuid"], str(self))
def __str__(self): def __str__(self):
return json.dumps(self.db_entry) return json.dumps(self.db_entry)

View file

@ -101,6 +101,7 @@ class VM(object):
# self.features.append(self.define_feature( # self.features.append(self.define_feature(
# self.super().__init__( # self.super().__init__(
def get_qemu_args(self): def get_qemu_args(self):
command = ( command = (
"-name {owner}-{name}" "-name {owner}-{name}"