2023-01-17 18:37:45 +03:00
from datetime import datetime , timedelta
2022-12-22 14:57:06 +03:00
2024-06-13 20:53:57 +03:00
from dateutil . relativedelta import relativedelta
2023-01-30 12:22:18 +03:00
from sqlalchemy import Column , VARCHAR , CHAR , ForeignKey , DATETIME , update , and_ , inspect , text
2022-12-28 13:05:41 +03:00
from sqlalchemy . engine import Engine
2023-01-30 12:23:09 +03:00
from sqlalchemy . orm import sessionmaker , declarative_base
2022-12-22 14:57:06 +03:00
2024-06-21 20:01:33 +03:00
from util import NV
2022-12-22 14:57:06 +03:00
Base = declarative_base ( )
class Origin ( Base ) :
__tablename__ = " origin "
origin_ref = Column ( CHAR ( length = 36 ) , primary_key = True , unique = True , index = True ) # uuid4
2023-01-03 16:20:13 +03:00
# service_instance_xid = Column(CHAR(length=36), nullable=False, index=True) # uuid4 # not necessary, we only support one service_instance_xid ('INSTANCE_REF')
2022-12-22 14:57:06 +03:00
hostname = Column ( VARCHAR ( length = 256 ) , nullable = True )
guest_driver_version = Column ( VARCHAR ( length = 10 ) , nullable = True )
os_platform = Column ( VARCHAR ( length = 256 ) , nullable = True )
os_version = Column ( VARCHAR ( length = 256 ) , nullable = True )
def __repr__ ( self ) :
return f ' Origin(origin_ref= { self . origin_ref } , hostname= { self . hostname } ) '
2022-12-29 11:00:52 +03:00
def serialize ( self ) - > dict :
2024-06-21 20:01:33 +03:00
_ = NV ( ) . find ( self . guest_driver_version )
2022-12-29 11:00:52 +03:00
return {
' origin_ref ' : self . origin_ref ,
2023-01-03 16:20:13 +03:00
# 'service_instance_xid': self.service_instance_xid,
2022-12-29 11:00:52 +03:00
' hostname ' : self . hostname ,
' guest_driver_version ' : self . guest_driver_version ,
' os_platform ' : self . os_platform ,
' os_version ' : self . os_version ,
2024-06-21 20:01:33 +03:00
' $driver ' : _ if _ is not None else None ,
2022-12-29 11:00:52 +03:00
}
2022-12-22 14:57:06 +03:00
@staticmethod
def create_statement ( engine : Engine ) :
from sqlalchemy . schema import CreateTable
return CreateTable ( Origin . __table__ ) . compile ( engine )
@staticmethod
def create_or_update ( engine : Engine , origin : " Origin " ) :
2022-12-29 09:09:39 +03:00
session = sessionmaker ( bind = engine ) ( )
2022-12-22 14:57:06 +03:00
entity = session . query ( Origin ) . filter ( Origin . origin_ref == origin . origin_ref ) . first ( )
if entity is None :
session . add ( origin )
else :
2022-12-29 11:40:36 +03:00
x = dict (
2022-12-23 10:16:58 +03:00
hostname = origin . hostname ,
guest_driver_version = origin . guest_driver_version ,
os_platform = origin . os_platform ,
2022-12-29 11:40:36 +03:00
os_version = origin . os_version
2022-12-23 10:16:58 +03:00
)
2022-12-29 11:40:36 +03:00
session . execute ( update ( Origin ) . where ( Origin . origin_ref == origin . origin_ref ) . values ( * * x ) )
2022-12-29 09:09:39 +03:00
session . commit ( )
2022-12-22 14:57:06 +03:00
session . flush ( )
session . close ( )
2022-12-29 11:57:37 +03:00
@staticmethod
2023-01-23 09:12:02 +03:00
def delete ( engine : Engine , origin_refs : [ str ] = None ) - > int :
2022-12-29 11:57:37 +03:00
session = sessionmaker ( bind = engine ) ( )
2023-01-23 09:12:02 +03:00
if origin_refs is None :
2022-12-29 11:57:37 +03:00
deletions = session . query ( Origin ) . delete ( )
else :
2023-01-23 09:12:02 +03:00
deletions = session . query ( Origin ) . filter ( Origin . origin_ref in origin_refs ) . delete ( )
2022-12-29 11:57:37 +03:00
session . commit ( )
session . close ( )
return deletions
2022-12-22 14:57:06 +03:00
class Lease ( Base ) :
__tablename__ = " lease "
2022-12-23 10:22:21 +03:00
lease_ref = Column ( CHAR ( length = 36 ) , primary_key = True , nullable = False , index = True ) # uuid4
2022-12-29 11:57:37 +03:00
origin_ref = Column ( CHAR ( length = 36 ) , ForeignKey ( Origin . origin_ref , ondelete = ' CASCADE ' ) , nullable = False , index = True ) # uuid4
2023-01-03 16:20:13 +03:00
# scope_ref = Column(CHAR(length=36), nullable=False, index=True) # uuid4 # not necessary, we only support one scope_ref ('ALLOTMENT_REF')
2022-12-22 14:57:06 +03:00
lease_created = Column ( DATETIME ( ) , nullable = False )
lease_expires = Column ( DATETIME ( ) , nullable = False )
lease_updated = Column ( DATETIME ( ) , nullable = False )
def __repr__ ( self ) :
2023-01-03 16:52:31 +03:00
return f ' Lease(origin_ref= { self . origin_ref } , lease_ref= { self . lease_ref } , expires= { self . lease_expires } ) '
2022-12-22 14:57:06 +03:00
2023-01-17 18:37:45 +03:00
def serialize ( self , renewal_period : float , renewal_delta : timedelta ) - > dict :
lease_renewal = int ( Lease . calculate_renewal ( renewal_period , renewal_delta ) . total_seconds ( ) )
2023-01-17 19:27:26 +03:00
lease_renewal = self . lease_updated + relativedelta ( seconds = lease_renewal )
2023-01-17 18:37:45 +03:00
2022-12-29 11:00:52 +03:00
return {
' lease_ref ' : self . lease_ref ,
' origin_ref ' : self . origin_ref ,
2023-01-03 16:09:19 +03:00
# 'scope_ref': self.scope_ref,
2022-12-29 11:00:52 +03:00
' lease_created ' : self . lease_created . isoformat ( ) ,
' lease_expires ' : self . lease_expires . isoformat ( ) ,
' lease_updated ' : self . lease_updated . isoformat ( ) ,
2023-01-17 18:37:45 +03:00
' lease_renewal ' : lease_renewal . isoformat ( ) ,
2022-12-29 11:00:52 +03:00
}
2022-12-22 14:57:06 +03:00
@staticmethod
def create_statement ( engine : Engine ) :
from sqlalchemy . schema import CreateTable
return CreateTable ( Lease . __table__ ) . compile ( engine )
@staticmethod
def create_or_update ( engine : Engine , lease : " Lease " ) :
2022-12-29 09:09:39 +03:00
session = sessionmaker ( bind = engine ) ( )
2022-12-29 11:40:36 +03:00
entity = session . query ( Lease ) . filter ( Lease . lease_ref == lease . lease_ref ) . first ( )
2022-12-22 14:57:06 +03:00
if entity is None :
2022-12-27 21:57:58 +03:00
if lease . lease_updated is None :
lease . lease_updated = lease . lease_created
2022-12-22 14:57:06 +03:00
session . add ( lease )
else :
2022-12-29 11:40:36 +03:00
x = dict ( origin_ref = lease . origin_ref , lease_expires = lease . lease_expires , lease_updated = lease . lease_updated )
session . execute ( update ( Lease ) . where ( Lease . lease_ref == lease . lease_ref ) . values ( * * x ) )
2022-12-29 09:09:39 +03:00
session . commit ( )
2022-12-22 14:57:06 +03:00
session . flush ( )
session . close ( )
@staticmethod
def find_by_origin_ref ( engine : Engine , origin_ref : str ) - > [ " Lease " ] :
2022-12-29 09:09:39 +03:00
session = sessionmaker ( bind = engine ) ( )
2022-12-22 14:57:06 +03:00
entities = session . query ( Lease ) . filter ( Lease . origin_ref == origin_ref ) . all ( )
session . close ( )
return entities
2022-12-29 21:03:09 +03:00
@staticmethod
def find_by_lease_ref ( engine : Engine , lease_ref : str ) - > " Lease " :
session = sessionmaker ( bind = engine ) ( )
entity = session . query ( Lease ) . filter ( Lease . lease_ref == lease_ref ) . first ( )
session . close ( )
return entity
2022-12-22 14:57:06 +03:00
@staticmethod
def find_by_origin_ref_and_lease_ref ( engine : Engine , origin_ref : str , lease_ref : str ) - > " Lease " :
2022-12-29 09:09:39 +03:00
session = sessionmaker ( bind = engine ) ( )
2022-12-22 14:57:06 +03:00
entity = session . query ( Lease ) . filter ( and_ ( Lease . origin_ref == origin_ref , Lease . lease_ref == lease_ref ) ) . first ( )
session . close ( )
return entity
@staticmethod
2023-01-17 18:37:45 +03:00
def renew ( engine : Engine , lease : " Lease " , lease_expires : datetime , lease_updated : datetime ) :
2022-12-29 09:09:39 +03:00
session = sessionmaker ( bind = engine ) ( )
2022-12-29 21:00:14 +03:00
x = dict ( lease_expires = lease_expires , lease_updated = lease_updated )
2022-12-29 11:40:36 +03:00
session . execute ( update ( Lease ) . where ( and_ ( Lease . origin_ref == lease . origin_ref , Lease . lease_ref == lease . lease_ref ) ) . values ( * * x ) )
2022-12-29 09:09:39 +03:00
session . commit ( )
2022-12-22 14:57:06 +03:00
session . close ( )
@staticmethod
def cleanup ( engine : Engine , origin_ref : str ) - > int :
2022-12-29 09:09:39 +03:00
session = sessionmaker ( bind = engine ) ( )
2022-12-27 22:28:09 +03:00
deletions = session . query ( Lease ) . filter ( Lease . origin_ref == origin_ref ) . delete ( )
2022-12-29 09:09:39 +03:00
session . commit ( )
2022-12-22 14:57:06 +03:00
session . close ( )
return deletions
2022-12-23 15:17:19 +03:00
2022-12-29 11:57:37 +03:00
@staticmethod
def delete ( engine : Engine , lease_ref : str ) - > int :
session = sessionmaker ( bind = engine ) ( )
deletions = session . query ( Lease ) . filter ( Lease . lease_ref == lease_ref ) . delete ( )
session . commit ( )
session . close ( )
return deletions
2023-06-12 11:48:00 +03:00
@staticmethod
def delete_expired ( engine : Engine ) - > int :
session = sessionmaker ( bind = engine ) ( )
deletions = session . query ( Lease ) . filter ( Lease . lease_expires < = datetime . utcnow ( ) ) . delete ( )
session . commit ( )
session . close ( )
return deletions
2023-01-17 13:49:56 +03:00
@staticmethod
2023-01-17 18:37:45 +03:00
def calculate_renewal ( renewal_period : float , delta : timedelta ) - > timedelta :
2023-01-17 13:49:56 +03:00
"""
2023-01-17 18:37:45 +03:00
import datetime
2023-01-17 13:49:56 +03:00
LEASE_RENEWAL_PERIOD = 0.2 # 20%
delta = datetime . timedelta ( days = 1 )
renew = delta . total_seconds ( ) * LEASE_RENEWAL_PERIOD
2023-01-17 18:37:45 +03:00
renew = datetime . timedelta ( seconds = renew )
2023-01-17 13:49:56 +03:00
expires = delta - renew # 19.2
2023-01-19 09:25:44 +03:00
import datetime
LEASE_RENEWAL_PERIOD = 0.15 # 15%
delta = datetime . timedelta ( days = 90 )
renew = delta . total_seconds ( ) * LEASE_RENEWAL_PERIOD
renew = datetime . timedelta ( seconds = renew )
expires = delta - renew # 76 days, 12:00:00 hours
2023-01-17 13:49:56 +03:00
"""
renew = delta . total_seconds ( ) * renewal_period
2023-01-17 18:37:45 +03:00
renew = timedelta ( seconds = renew )
2023-01-17 19:27:26 +03:00
return renew
2023-01-17 13:49:56 +03:00
2022-12-23 15:17:19 +03:00
def init ( engine : Engine ) :
tables = [ Origin , Lease ]
db = inspect ( engine )
session = sessionmaker ( bind = engine ) ( )
for table in tables :
if not db . dialect . has_table ( engine . connect ( ) , table . __tablename__ ) :
2023-01-30 12:22:18 +03:00
session . execute ( text ( str ( table . create_statement ( engine ) ) ) )
2022-12-29 09:09:39 +03:00
session . commit ( )
2022-12-23 15:17:19 +03:00
session . close ( )
2022-12-29 11:40:36 +03:00
def migrate ( engine : Engine ) :
db = inspect ( engine )
def upgrade_1_0_to_1_1 ( ) :
x = db . dialect . get_columns ( engine . connect ( ) , Lease . __tablename__ )
x = next ( _ for _ in x if _ [ ' name ' ] == ' origin_ref ' )
if x [ ' primary_key ' ] > 0 :
print ( ' Found old database schema with " origin_ref " as primary-key in " lease " table. Dropping table! ' )
print ( ' Your leases are recreated on next renewal! ' )
print ( ' If an error message appears on the client, you can ignore it. ' )
Lease . __table__ . drop ( bind = engine )
2022-12-29 14:34:25 +03:00
init ( engine )
2022-12-29 11:40:36 +03:00
2023-01-03 16:09:19 +03:00
# def upgrade_1_2_to_1_3():
# x = db.dialect.get_columns(engine.connect(), Lease.__tablename__)
# x = next((_ for _ in x if _['name'] == 'scope_ref'), None)
# if x is None:
# Lease.scope_ref.compile()
# column_name = Lease.scope_ref.name
# column_type = Lease.scope_ref.type.compile(engine.dialect)
# engine.execute(f'ALTER TABLE "{Lease.__tablename__}" ADD COLUMN "{column_name}" {column_type}')
2023-01-03 15:05:05 +03:00
2022-12-29 11:40:36 +03:00
upgrade_1_0_to_1_1 ( )
2023-01-03 16:09:19 +03:00
# upgrade_1_2_to_1_3()