from flask import Flask, request from flask_restful import Resource, Api import etcd3 import json import logging from functools import wraps def readable_errors(func): @wraps(func) def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except etcd3.exceptions.ConnectionFailedError as e: raise UncloudException('Cannot connect to etcd: is etcd running and reachable? {}'.format(e)) except etcd3.exceptions.ConnectionTimeoutError as e: raise UncloudException('etcd connection timeout. {}'.format(e)) return wrapper class DB(object): def __init__(self, config, prefix="/"): self.config = config # Root for everything self.base_prefix= '/nicohack' # Can be set from outside self.prefix = prefix self.connect() @readable_errors def connect(self): self._db_clients = [] for endpoint in self.config.etcd_hosts: client = etcd3.client(host=endpoint, **self.config.etcd_args) self._db_clients.append(client) def realkey(self, key): return "{}{}/{}".format(self.base_prefix, self.prefix, key) @readable_errors def get(self, key, as_json=False, **kwargs): value, _ = self._db_clients[0].get(self.realkey(key), **kwargs) if as_json: value = json.loads(value) return value @readable_errors def set(self, key, value, as_json=False, **kwargs): if as_json: value = json.dumps(value) # FIXME: iterate over clients in case of failure ? return self._db_clients[0].put(self.realkey(key), value, **kwargs) class Membership(Resource): def __init__(self, config): self.config = config def get(self): data = request.get_json(silent=True) or {} print("{} {}".format(data, config)) return {'message': 'Order successful' }, 200 def post(self): data = request.get_json(silent=True) or {} print("{} {}".format(data, config)) return {'message': 'Order 2x successful' }, 200 class Order(Resource): def __init__(self, config): self.config = config @staticmethod def post(): data = request.get_json(silent=True) or {} print("{} {}".format(data, config)) class Product(Resource): def __init__(self, config): self.config = config self.products = [] self.products.append( { "name": "membership-free", "description": """ This membership gives you access to the API and includes a VPN with 1 IPv6 address. See https://redmine.ungleich.ch/issues/7747? """, "uuid": "a3883466-0012-4d01-80ff-cbf7469957af", "recurring": True, "recurring_time_frame": "per_year", "features": [ { "name": "membership", "price_one_time": 0, "price_recurring": 0 } ] } ) self.products.append( { "name": "membership-standard", "description": """ This membership gives you access to the API and includes an IPv6-VPN with one IPv6 address ("Road warrior") See https://redmine.ungleich.ch/issues/7747? """, "uuid": "1d85296b-0863-4dd6-a543-a6d5a4fbe4a6", "recurring": True, "recurring_time_frame": "per_month", "features": [ { "name": "membership", "price_one_time": 0, "price_recurring": 5 } ] } ) self.products.append( { "name": "membership-premium", "description": """ This membership gives you access to the API and includes an IPv6-VPN with a /48 IPv6 network. See https://redmine.ungleich.ch/issues/7747? """, "uuid": "bfd63fd2-d227-436f-a8b8-600de74dd6ce", "recurring": True, "recurring_time_frame": "per_month", "features": [ { "name": "membership", "price_one_time": 0, "price_recurring": 5 } ] } ) @staticmethod def post(): data = request.get_json(silent=True) or {} print("{} {}".format(data, config)) def get(self): data = request.get_json(silent=True) or {} print("{} {}".format(data, config)) return self.products if __name__ == '__main__': app = Flask(__name__) config = {} config['etcd_url']="https://etcd1.ungleich.ch" config['ldap_url']="ldaps://ldap1.ungleich.ch" api = Api(app) api.add_resource(Order, '/orders', resource_class_args=( config, )) api.add_resource(Product, '/products', resource_class_args=( config, )) api.add_resource(Membership, '/membership', resource_class_args=( config, )) app.run(host='::', port=5000, debug=True)