2020-02-09 08:36:50 +00:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# 2020 Nico Schottelius (nico.schottelius at ungleich.ch)
#
# This file is part of uncloud.
#
# uncloud is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# uncloud is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with uncloud. If not, see <http://www.gnu.org/licenses/>.
import json
import uuid
2020-02-09 11:12:15 +00:00
import logging
2020-02-09 11:54:52 +00:00
import re
2020-02-09 08:36:50 +00:00
from uncloud import UncloudException
2020-02-09 11:54:52 +00:00
from uncloud . hack . db import DB , db_logentry
2020-02-09 08:36:50 +00:00
2020-02-09 11:12:15 +00:00
log = logging . getLogger ( __name__ )
2020-02-09 10:14:50 +00:00
class ProductOrder ( object ) :
2020-02-09 11:12:15 +00:00
def __init__ ( self , config , product_entry = None , db_entry = None ) :
2020-02-09 10:14:50 +00:00
self . config = config
self . db = DB ( self . config , prefix = " /orders " )
2020-02-09 11:12:15 +00:00
self . db_entry = { }
self . db_entry [ " product " ] = product_entry
2020-02-09 10:14:50 +00:00
2020-02-09 11:12:15 +00:00
# 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!
2020-02-09 11:54:52 +00:00
def list_orders ( self , filter_key = None , filter_regexp = None ) :
2020-02-09 11:12:15 +00:00
""" List all orders with - filtering not yet implemented """
for k , v in self . db . get_prefix ( " " , as_json = True ) :
log . debug ( " {} {} " . format ( k , v ) )
2020-02-09 11:54:52 +00:00
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 )
2020-02-09 11:12:15 +00:00
def set_required_values ( self ) :
2020-02-09 11:54:52 +00:00
""" Set values that are required to make the db entry valid """
2020-02-09 11:12:15 +00:00
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 "
2020-02-09 11:54:52 +00:00
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
2020-02-09 11:12:15 +00:00
def validate_status ( self ) :
if " status " in self . db_entry :
2020-02-09 11:54:52 +00:00
if self . db_entry [ " status " ] in [ " NEW " ,
" SCHEDULED " ,
" CREATED_ACTIVE " ,
" CANCELLED " ,
" REJECTED " ] :
2020-02-09 11:12:15 +00:00
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 " ] ) )
2020-02-09 08:36:50 +00:00
2020-02-09 11:54:52 +00:00
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 )
2020-02-09 08:36:50 +00:00
class Product ( object ) :
2020-02-09 11:12:15 +00:00
def __init__ ( self ,
config ,
product_name ,
db_entry = None ) :
2020-02-09 08:36:50 +00:00
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
2020-02-09 11:54:52 +00:00
self . db_entry [ " log " ] = [ ]
2020-02-09 11:12:15 +00:00
self . db_entry [ " features " ] = { }
2020-02-09 08:36:50 +00:00
# Existing product? Read in db_entry
if db_entry :
self . db_entry = db_entry
2020-02-09 11:12:15 +00:00
self . valid_periods = [ " per_year " , " per_month " , " per_week " ,
" per_day " , " per_hour " ,
" per_minute " , " per_second " ]
2020-02-09 08:36:50 +00:00
def define_feature ( self ,
name ,
one_time_price ,
recurring_price ,
recurring_period ,
minimum_period ) :
2020-02-09 11:12:15 +00:00
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
2020-02-09 08:36:50 +00:00
2020-02-09 11:12:15 +00:00
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 ) )
2020-02-09 10:14:50 +00:00
2020-02-09 11:12:15 +00:00
self . db_entry [ ' features ' ] [ name ] [ ' minimum_period ' ] = minimum_period
def validate_product ( self ) :
for feature in self . db_entry [ ' features ' ] :
pass
2020-02-09 08:36:50 +00:00
def place_order ( self ) :
""" Schedule creating the product in etcd """
2020-02-09 11:54:52 +00:00
order = ProductOrder ( self . config , product_entry = self . db_entry )
2020-02-09 11:12:15 +00:00
order . set_required_values ( )
order . order ( )
2020-02-09 08:36:50 +00:00
def __str__ ( self ) :
2020-02-09 10:14:50 +00:00
return json . dumps ( self . db_entry )