forked from shaba/openuds
Refactoring models (without DB modification) to better type checking
This commit is contained in:
parent
52f524eb61
commit
b8e7dc07c3
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -32,17 +32,14 @@
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
# Utility imports
|
||||||
|
from .util import getSqlDatetime, getSqlDatetimeAsUnix, NEVER, NEVER_UNIX
|
||||||
|
|
||||||
|
# Imports all models so they are available for migrations
|
||||||
|
|
||||||
# Permissions
|
# Permissions
|
||||||
from .permissions import Permissions
|
from .permissions import Permissions
|
||||||
|
|
||||||
# Utility
|
|
||||||
from .util import (
|
|
||||||
getSqlDatetime,
|
|
||||||
getSqlDatetimeAsUnix,
|
|
||||||
NEVER,
|
|
||||||
NEVER_UNIX
|
|
||||||
)
|
|
||||||
|
|
||||||
# Services
|
# Services
|
||||||
from .provider import Provider
|
from .provider import Provider
|
||||||
from .service import Service
|
from .service import Service
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -42,17 +42,22 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# Not imported at runtime, just for type checking
|
# Not imported at runtime, just for type checking
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from uds.models import UserService
|
from uds.models import UserService, AccountUsage
|
||||||
|
|
||||||
|
|
||||||
class Account(UUIDModel, TaggingMixin): # type: ignore
|
class Account(UUIDModel, TaggingMixin): # type: ignore
|
||||||
"""
|
"""
|
||||||
Account storing on DB model
|
Account storing on DB model
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = models.CharField(max_length=128, unique=False, db_index=True)
|
name = models.CharField(max_length=128, unique=False, db_index=True)
|
||||||
time_mark = models.DateTimeField(default=NEVER)
|
time_mark = models.DateTimeField(default=NEVER)
|
||||||
comments = models.CharField(max_length=256)
|
comments = models.CharField(max_length=256)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Account]'
|
||||||
|
usages: 'models.QuerySet[AccountUsage]'
|
||||||
|
|
||||||
def startUsageAccounting(self, userService: 'UserService') -> None:
|
def startUsageAccounting(self, userService: 'UserService') -> None:
|
||||||
if hasattr(userService, 'accounting'): # Already has an account
|
if hasattr(userService, 'accounting'): # Already has an account
|
||||||
return None
|
return None
|
||||||
@ -65,18 +70,20 @@ class Account(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
else:
|
else:
|
||||||
userName = '??????'
|
userName = '??????'
|
||||||
userUuid = '00000000-0000-0000-0000-000000000000'
|
userUuid = '00000000-0000-0000-0000-000000000000'
|
||||||
return self.usages.create(
|
|
||||||
|
self.usages.create(
|
||||||
user_service=userService,
|
user_service=userService,
|
||||||
user_name=userName,
|
user_name=userName,
|
||||||
user_uuid=userUuid,
|
user_uuid=userUuid,
|
||||||
pool_name=userService.deployed_service.name,
|
pool_name=userService.deployed_service.name,
|
||||||
pool_uuid=userService.deployed_service.uuid,
|
pool_uuid=userService.deployed_service.uuid,
|
||||||
start=start,
|
start=start,
|
||||||
end=start
|
end=start,
|
||||||
)
|
)
|
||||||
|
|
||||||
def stopUsageAccounting(self, userService: 'UserService') -> None:
|
def stopUsageAccounting(self, userService: 'UserService') -> None:
|
||||||
if hasattr(userService, 'accounting') is False:
|
# if one to one does not exists, attr is not there
|
||||||
|
if not hasattr(userService, 'accounting'):
|
||||||
return
|
return
|
||||||
|
|
||||||
tmp = userService.accounting
|
tmp = userService.accounting
|
||||||
@ -88,6 +95,7 @@ class Account(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
"""
|
"""
|
||||||
Meta class to declare the name of the table at database
|
Meta class to declare the name of the table at database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_accounts'
|
db_table = 'uds_accounts'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -28,6 +28,7 @@
|
|||||||
"""
|
"""
|
||||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
"""
|
"""
|
||||||
|
import typing
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
@ -39,6 +40,10 @@ from .account import Account
|
|||||||
from .user_service import UserService
|
from .user_service import UserService
|
||||||
from .util import NEVER
|
from .util import NEVER
|
||||||
|
|
||||||
|
# Not imported at runtime, just for type checking
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
from uds.models import UserService, Account
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -46,21 +51,35 @@ class AccountUsage(UUIDModel):
|
|||||||
"""
|
"""
|
||||||
AccountUsage storing on DB model
|
AccountUsage storing on DB model
|
||||||
This is intended for small images (i will limit them to 128x128), so storing at db is fine
|
This is intended for small images (i will limit them to 128x128), so storing at db is fine
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[AccountUsage]'
|
||||||
|
|
||||||
user_name = models.CharField(max_length=128, db_index=True, default='')
|
user_name = models.CharField(max_length=128, db_index=True, default='')
|
||||||
user_uuid = models.CharField(max_length=50, db_index=True, default='')
|
user_uuid = models.CharField(max_length=50, db_index=True, default='')
|
||||||
pool_name = models.CharField(max_length=128, db_index=True, default='')
|
pool_name = models.CharField(max_length=128, db_index=True, default='')
|
||||||
pool_uuid = models.CharField(max_length=50, db_index=True, default='')
|
pool_uuid = models.CharField(max_length=50, db_index=True, default='')
|
||||||
start = models.DateTimeField(default=NEVER)
|
start = models.DateTimeField(default=NEVER)
|
||||||
end = models.DateTimeField(default=NEVER)
|
end = models.DateTimeField(default=NEVER)
|
||||||
user_service: UserService = models.OneToOneField(UserService, null=True, blank=True, related_name='accounting', on_delete=models.SET_NULL)
|
user_service: 'models.OneToOneField[AccountUsage, UserService]' = (
|
||||||
account: Account = models.ForeignKey(Account, related_name='usages', on_delete=models.CASCADE)
|
models.OneToOneField(
|
||||||
|
UserService,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name='accounting',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
account: 'models.ForeignKey[AccountUsage, Account]' = models.ForeignKey(
|
||||||
|
Account, related_name='usages', on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare the name of the table at database
|
Meta class to declare the name of the table at database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_acc_usage'
|
db_table = 'uds_acc_usage'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
@ -93,4 +112,6 @@ class AccountUsage(UUIDModel):
|
|||||||
return secondsToTimeString(self.elapsed_seconds_timemark)
|
return secondsToTimeString(self.elapsed_seconds_timemark)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'AccountUsage id {}, pool {}, name {}, start {}, end {}'.format(self.id, self.pool_name, self.user_name, self.start, self.end)
|
return 'AccountUsage id {}, pool {}, name {}, start {}, end {}'.format(
|
||||||
|
self.id, self.pool_name, self.user_name, self.start, self.end
|
||||||
|
)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -30,10 +30,12 @@
|
|||||||
'''
|
'''
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class ActorToken(models.Model):
|
class ActorToken(models.Model):
|
||||||
"""
|
"""
|
||||||
UDS Actors tokens on DB
|
UDS Actors tokens on DB
|
||||||
"""
|
"""
|
||||||
|
|
||||||
username = models.CharField(max_length=128)
|
username = models.CharField(max_length=128)
|
||||||
ip_from = models.CharField(max_length=128)
|
ip_from = models.CharField(max_length=128)
|
||||||
ip = models.CharField(max_length=128)
|
ip = models.CharField(max_length=128)
|
||||||
@ -47,5 +49,10 @@ class ActorToken(models.Model):
|
|||||||
token = models.CharField(max_length=48, db_index=True, unique=True)
|
token = models.CharField(max_length=48, db_index=True, unique=True)
|
||||||
stamp = models.DateTimeField() # Date creation or validation of this entry
|
stamp = models.DateTimeField() # Date creation or validation of this entry
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[ActorToken]'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '<ActorToken {} created on {} by {} from {}/{}>'.format(self.token, self.stamp, self.username, self.hostname, self.ip_from)
|
return '<ActorToken {} created on {} by {} from {}/{}>'.format(
|
||||||
|
self.token, self.stamp, self.username, self.hostname, self.ip_from
|
||||||
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -47,8 +47,7 @@ from .util import NEVER
|
|||||||
|
|
||||||
# Not imported at runtime, just for type checking
|
# Not imported at runtime, just for type checking
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from .user import User
|
from uds.models import User, Group
|
||||||
from django.db.models import QuerySet # pylint: disable=ungrouped-imports
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -64,10 +63,16 @@ class Authenticator(ManagedObjectModel, TaggingMixin):
|
|||||||
small_name = models.CharField(max_length=32, default='', db_index=True)
|
small_name = models.CharField(max_length=32, default='', db_index=True)
|
||||||
visible = models.BooleanField(default=True)
|
visible = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
# "fake" relations declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Authenticator]'
|
||||||
|
users: 'models.QuerySet[User]'
|
||||||
|
groups: 'models.QuerySet[Group]'
|
||||||
|
|
||||||
class Meta(ManagedObjectModel.Meta): # pylint: disable=too-few-public-methods
|
class Meta(ManagedObjectModel.Meta): # pylint: disable=too-few-public-methods
|
||||||
"""
|
"""
|
||||||
Meta class to declare default order
|
Meta class to declare default order
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
@ -87,7 +92,9 @@ class Authenticator(ManagedObjectModel, TaggingMixin):
|
|||||||
Raises:
|
Raises:
|
||||||
"""
|
"""
|
||||||
if self.id is None:
|
if self.id is None:
|
||||||
return auths.Authenticator(self, environment.Environment.getTempEnv(), values)
|
return auths.Authenticator(
|
||||||
|
self, environment.Environment.getTempEnv(), values
|
||||||
|
)
|
||||||
|
|
||||||
auType = self.getType()
|
auType = self.getType()
|
||||||
env = self.getEnvironment()
|
env = self.getEnvironment()
|
||||||
@ -109,7 +116,9 @@ class Authenticator(ManagedObjectModel, TaggingMixin):
|
|||||||
# If type is not registered (should be, but maybe a database inconsistence), consider this a "base empty auth"
|
# If type is not registered (should be, but maybe a database inconsistence), consider this a "base empty auth"
|
||||||
return auths.factory().lookup(self.data_type) or auths.Authenticator
|
return auths.factory().lookup(self.data_type) or auths.Authenticator
|
||||||
|
|
||||||
def getOrCreateUser(self, username: str, realName: typing.Optional[str] = None) -> 'User':
|
def getOrCreateUser(
|
||||||
|
self, username: str, realName: typing.Optional[str] = None
|
||||||
|
) -> 'User':
|
||||||
"""
|
"""
|
||||||
Used to get or create a new user at database associated with this authenticator.
|
Used to get or create a new user at database associated with this authenticator.
|
||||||
|
|
||||||
@ -139,8 +148,17 @@ class Authenticator(ManagedObjectModel, TaggingMixin):
|
|||||||
"""
|
"""
|
||||||
user: 'User'
|
user: 'User'
|
||||||
realName = realName if realName is None else username
|
realName = realName if realName is None else username
|
||||||
user, _ = self.users.get_or_create(name=username, defaults={'real_name': realName, 'last_access': NEVER, 'state': State.ACTIVE})
|
user, _ = self.users.get_or_create(
|
||||||
if (user.real_name.strip() == '' or user.name.strip() == user.real_name.strip()) and realName != user.real_name:
|
name=username,
|
||||||
|
defaults={
|
||||||
|
'real_name': realName,
|
||||||
|
'last_access': NEVER,
|
||||||
|
'state': State.ACTIVE,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
user.real_name.strip() == '' or user.name.strip() == user.real_name.strip()
|
||||||
|
) and realName != user.real_name:
|
||||||
user.real_name = realName
|
user.real_name = realName
|
||||||
user.save(update_fields=['real_name'])
|
user.save(update_fields=['real_name'])
|
||||||
|
|
||||||
@ -169,14 +187,14 @@ class Authenticator(ManagedObjectModel, TaggingMixin):
|
|||||||
return falseIfNotExists
|
return falseIfNotExists
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def all() -> 'QuerySet':
|
def all() -> 'models.QuerySet[Authenticator]':
|
||||||
"""
|
"""
|
||||||
Returns all authenticators ordered by priority
|
Returns all authenticators ordered by priority
|
||||||
"""
|
"""
|
||||||
return Authenticator.objects.all().order_by('priority')
|
return Authenticator.objects.all().order_by('priority')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getByTag(tag=None) -> typing.List['Authenticator']:
|
def getByTag(tag=None) -> typing.Iterable['Authenticator']:
|
||||||
"""
|
"""
|
||||||
Gets authenticator by tag name.
|
Gets authenticator by tag name.
|
||||||
Special tag name "disabled" is used to exclude customAuth
|
Special tag name "disabled" is used to exclude customAuth
|
||||||
@ -184,7 +202,9 @@ class Authenticator(ManagedObjectModel, TaggingMixin):
|
|||||||
from uds.core.util.config import GlobalConfig
|
from uds.core.util.config import GlobalConfig
|
||||||
|
|
||||||
if tag is not None:
|
if tag is not None:
|
||||||
authsList: 'QuerySet' = Authenticator.objects.filter(small_name=tag).order_by('priority', 'name')
|
authsList = Authenticator.objects.filter(small_name=tag).order_by(
|
||||||
|
'priority', 'name'
|
||||||
|
)
|
||||||
if not authsList:
|
if not authsList:
|
||||||
authsList = Authenticator.objects.all().order_by('priority', 'name')
|
authsList = Authenticator.objects.all().order_by('priority', 'name')
|
||||||
# If disallow global login (use all auths), get just the first by priority/name
|
# If disallow global login (use all auths), get just the first by priority/name
|
||||||
@ -194,10 +214,12 @@ class Authenticator(ManagedObjectModel, TaggingMixin):
|
|||||||
else:
|
else:
|
||||||
authsList = Authenticator.objects.all().order_by('priority', 'name')
|
authsList = Authenticator.objects.all().order_by('priority', 'name')
|
||||||
|
|
||||||
return [auth for auth in authsList if auth.getType() and (auth.getType().isCustom() is False or tag != 'disabled')]
|
for auth in authsList:
|
||||||
|
if auth.getType() and (not auth.getType().isCustom() or tag != 'disabled'):
|
||||||
|
yield auth
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def beforeDelete(sender, **kwargs):
|
def beforeDelete(sender, **kwargs) -> None:
|
||||||
"""
|
"""
|
||||||
Used to invoke the Service class "Destroy" before deleting it from database.
|
Used to invoke the Service class "Destroy" before deleting it from database.
|
||||||
|
|
||||||
@ -207,6 +229,7 @@ class Authenticator(ManagedObjectModel, TaggingMixin):
|
|||||||
:note: If destroy raises an exception, the deletion is not taken.
|
:note: If destroy raises an exception, the deletion is not taken.
|
||||||
"""
|
"""
|
||||||
from uds.core.util.permissions import clean
|
from uds.core.util.permissions import clean
|
||||||
|
|
||||||
toDelete = kwargs['instance']
|
toDelete = kwargs['instance']
|
||||||
|
|
||||||
logger.debug('Before delete auth %s', toDelete)
|
logger.debug('Before delete auth %s', toDelete)
|
||||||
@ -228,4 +251,4 @@ class Authenticator(ManagedObjectModel, TaggingMixin):
|
|||||||
|
|
||||||
|
|
||||||
# Connects a pre deletion signal to Authenticator
|
# Connects a pre deletion signal to Authenticator
|
||||||
signals.pre_delete.connect(Authenticator.beforeDelete, sender=Authenticator)
|
models.signals.pre_delete.connect(Authenticator.beforeDelete, sender=Authenticator)
|
||||||
|
@ -45,16 +45,23 @@ class Cache(models.Model):
|
|||||||
"""
|
"""
|
||||||
General caching model. This model is managed via uds.core.util.cache.Cache class
|
General caching model. This model is managed via uds.core.util.cache.Cache class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
owner = models.CharField(max_length=128, db_index=True)
|
owner = models.CharField(max_length=128, db_index=True)
|
||||||
key = models.CharField(max_length=64, primary_key=True)
|
key = models.CharField(max_length=64, primary_key=True)
|
||||||
value = models.TextField(default='')
|
value = models.TextField(default='')
|
||||||
created = models.DateTimeField() # Date creation or validation of this entry. Set at write time
|
created = (
|
||||||
|
models.DateTimeField()
|
||||||
|
) # Date creation or validation of this entry. Set at write time
|
||||||
validity = models.IntegerField(default=60) # Validity of this entry, in seconds
|
validity = models.IntegerField(default=60) # Validity of this entry, in seconds
|
||||||
|
|
||||||
|
# "fake" relations declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Cache]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare the name of the table at database
|
Meta class to declare the name of the table at database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_utility_cache'
|
db_table = 'uds_utility_cache'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2016-2020 Virtual Cable S.L.
|
# Copyright (c) 2016-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -51,8 +51,8 @@ class Calendar(UUIDModel, TaggingMixin):
|
|||||||
comments = models.CharField(max_length=256, default='')
|
comments = models.CharField(max_length=256, default='')
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
# Sobmodels
|
# "fake" declarations for type checking
|
||||||
# "fake" relations declarations for type checking
|
objects: 'models.BaseManager[Calendar]'
|
||||||
rules: 'models.QuerySet[CalendarRule]'
|
rules: 'models.QuerySet[CalendarRule]'
|
||||||
calendaraction_set: 'models.QuerySet[CalendarAction]'
|
calendaraction_set: 'models.QuerySet[CalendarAction]'
|
||||||
|
|
||||||
@ -60,10 +60,17 @@ class Calendar(UUIDModel, TaggingMixin):
|
|||||||
"""
|
"""
|
||||||
Meta class to declare db table
|
Meta class to declare db table
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_calendar'
|
db_table = 'uds_calendar'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
def save(self, force_insert: bool = False, force_update: bool = False, using: bool = None, update_fields: bool = None):
|
def save(
|
||||||
|
self,
|
||||||
|
force_insert: bool = False,
|
||||||
|
force_update: bool = False,
|
||||||
|
using: bool = None,
|
||||||
|
update_fields: bool = None,
|
||||||
|
):
|
||||||
logger.debug('Saving calendar')
|
logger.debug('Saving calendar')
|
||||||
|
|
||||||
res = UUIDModel.save(self, force_insert, force_update, using, update_fields)
|
res = UUIDModel.save(self, force_insert, force_update, using, update_fields)
|
||||||
@ -78,4 +85,6 @@ class Calendar(UUIDModel, TaggingMixin):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Calendar "{}" modified on {} with {} rules'.format(self.name, self.modified, self.rules.count())
|
return 'Calendar "{}" modified on {} with {} rules'.format(
|
||||||
|
self.name, self.modified, self.rules.count()
|
||||||
|
)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Model based on https://github.com/llazzaro/django-scheduler
|
# Model based on https://github.com/llazzaro/django-scheduler
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2020 Virtual Cable S.L.
|
# Copyright (c) 2016-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -47,15 +47,23 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class CalendarAccess(UUIDModel):
|
class CalendarAccess(UUIDModel):
|
||||||
calendar: 'models.ForeignKey[CalendarAccess, Calendar]' = models.ForeignKey(Calendar, on_delete=models.CASCADE)
|
calendar: 'models.ForeignKey[CalendarAccess, Calendar]' = models.ForeignKey(
|
||||||
service_pool: 'models.ForeignKey[CalendarAccess, ServicePool]' = models.ForeignKey(ServicePool, related_name='calendarAccess', on_delete=models.CASCADE)
|
Calendar, on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
service_pool: 'models.ForeignKey[CalendarAccess, ServicePool]' = models.ForeignKey(
|
||||||
|
ServicePool, related_name='calendarAccess', on_delete=models.CASCADE
|
||||||
|
)
|
||||||
access = models.CharField(max_length=8, default=states.action.DENY)
|
access = models.CharField(max_length=8, default=states.action.DENY)
|
||||||
priority = models.IntegerField(default=0, db_index=True)
|
priority = models.IntegerField(default=0, db_index=True)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[CalendarAccess]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare db table
|
Meta class to declare db table
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_cal_access'
|
db_table = 'uds_cal_access'
|
||||||
ordering = ('priority',)
|
ordering = ('priority',)
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
@ -66,14 +74,20 @@ class CalendarAccess(UUIDModel):
|
|||||||
|
|
||||||
class CalendarAccessMeta(UUIDModel):
|
class CalendarAccessMeta(UUIDModel):
|
||||||
calendar = models.ForeignKey(Calendar, on_delete=models.CASCADE)
|
calendar = models.ForeignKey(Calendar, on_delete=models.CASCADE)
|
||||||
meta_pool = models.ForeignKey(MetaPool, related_name='calendarAccess', on_delete=models.CASCADE)
|
meta_pool = models.ForeignKey(
|
||||||
|
MetaPool, related_name='calendarAccess', on_delete=models.CASCADE
|
||||||
|
)
|
||||||
access = models.CharField(max_length=8, default=states.action.DENY)
|
access = models.CharField(max_length=8, default=states.action.DENY)
|
||||||
priority = models.IntegerField(default=0, db_index=True)
|
priority = models.IntegerField(default=0, db_index=True)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[CalendarAccessMeta]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare db table
|
Meta class to declare db table
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_cal_maccess'
|
db_table = 'uds_cal_maccess'
|
||||||
ordering = ('priority',)
|
ordering = ('priority',)
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Model based on https://github.com/llazzaro/django-scheduler
|
# Model based on https://github.com/llazzaro/django-scheduler
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2019 Virtual Cable S.L.
|
# Copyright (c) 2016-2020 Virtual Cable S.L.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -50,53 +50,171 @@ from .util import getSqlDatetime
|
|||||||
from .service_pool import ServicePool
|
from .service_pool import ServicePool
|
||||||
from .transport import Transport
|
from .transport import Transport
|
||||||
from .authenticator import Authenticator
|
from .authenticator import Authenticator
|
||||||
|
|
||||||
# from django.utils.translation import ugettext_lazy as _, ugettext
|
# from django.utils.translation import ugettext_lazy as _, ugettext
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Current posible actions
|
# Current posible actions
|
||||||
# Each line describes:
|
|
||||||
#
|
#
|
||||||
CALENDAR_ACTION_PUBLISH: typing.Dict[str, typing.Any] = {'id': 'PUBLISH', 'description': _('Publish'), 'params': ()}
|
CALENDAR_ACTION_PUBLISH: typing.Dict[str, typing.Any] = {
|
||||||
CALENDAR_ACTION_CACHE_L1: typing.Dict[str, typing.Any] = {'id': 'CACHEL1', 'description': _('Set cache size'), 'params': ({'type': 'numeric', 'name': 'size', 'description': _('Cache size'), 'default': '1'},)}
|
'id': 'PUBLISH',
|
||||||
CALENDAR_ACTION_CACHE_L2: typing.Dict[str, typing.Any] = {'id': 'CACHEL2', 'description': _('Set L2 cache size'), 'params': ({'type': 'numeric', 'name': 'size', 'description': _('Cache L2 size'), 'default': '1'},)}
|
'description': _('Publish'),
|
||||||
CALENDAR_ACTION_INITIAL: typing.Dict[str, typing.Any] = {'id': 'INITIAL', 'description': _('Set initial services'), 'params': ({'type': 'numeric', 'name': 'size', 'description': _('Initial services'), 'default': '1'},)}
|
'params': (),
|
||||||
CALENDAR_ACTION_MAX: typing.Dict[str, typing.Any] = {'id': 'MAX', 'description': _('Set maximum number of services'), 'params': ({'type': 'numeric', 'name': 'size', 'description': _('Maximum services'), 'default': '10'},)}
|
}
|
||||||
CALENDAR_ACTION_ADD_TRANSPORT: typing.Dict[str, typing.Any] = {'id': 'ADD_TRANSPORT', 'description': _('Add a transport'), 'params': ({'type': 'transport', 'name': 'transport', 'description': _('Transport'), 'default': ''},)}
|
CALENDAR_ACTION_CACHE_L1: typing.Dict[str, typing.Any] = {
|
||||||
CALENDAR_ACTION_DEL_TRANSPORT: typing.Dict[str, typing.Any] = {'id': 'REMOVE_TRANSPORT', 'description': _('Remove a transport'), 'params': ({'type': 'transport', 'name': 'transport', 'description': _('Trasport'), 'default': ''},)}
|
'id': 'CACHEL1',
|
||||||
CALENDAR_ACTION_ADD_GROUP: typing.Dict[str, typing.Any] = {'id': 'ADD_GROUP', 'description': _('Add a group'), 'params': ({'type': 'group', 'name': 'group', 'description': _('Group'), 'default': ''},)}
|
'description': _('Set cache size'),
|
||||||
CALENDAR_ACTION_DEL_GROUP: typing.Dict[str, typing.Any] = {'id': 'REMOVE_GROUP', 'description': _('Remove a group'), 'params': ({'type': 'group', 'name': 'group', 'description': _('Group'), 'default': ''},)}
|
'params': (
|
||||||
CALENDAR_ACTION_IGNORE_UNUSED: typing.Dict[str, typing.Any] = {'id': 'IGNORE_UNUSED', 'description': _('Sets the ignore unused'), 'params': ({'type': 'bool', 'name': 'state', 'description': _('Ignore assigned and unused'), 'default': False},)}
|
{
|
||||||
CALENDAR_ACTION_REMOVE_USERSERVICES: typing.Dict[str, typing.Any] = {'id': 'REMOVE_USERSERVICES', 'description': _('Remove ALL assigned user service. USE WITH CAUTION!'), 'params': ()}
|
'type': 'numeric',
|
||||||
|
'name': 'size',
|
||||||
|
'description': _('Cache size'),
|
||||||
|
'default': '1',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
CALENDAR_ACTION_CACHE_L2: typing.Dict[str, typing.Any] = {
|
||||||
|
'id': 'CACHEL2',
|
||||||
|
'description': _('Set L2 cache size'),
|
||||||
|
'params': (
|
||||||
|
{
|
||||||
|
'type': 'numeric',
|
||||||
|
'name': 'size',
|
||||||
|
'description': _('Cache L2 size'),
|
||||||
|
'default': '1',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
CALENDAR_ACTION_INITIAL: typing.Dict[str, typing.Any] = {
|
||||||
|
'id': 'INITIAL',
|
||||||
|
'description': _('Set initial services'),
|
||||||
|
'params': (
|
||||||
|
{
|
||||||
|
'type': 'numeric',
|
||||||
|
'name': 'size',
|
||||||
|
'description': _('Initial services'),
|
||||||
|
'default': '1',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
CALENDAR_ACTION_MAX: typing.Dict[str, typing.Any] = {
|
||||||
|
'id': 'MAX',
|
||||||
|
'description': _('Set maximum number of services'),
|
||||||
|
'params': (
|
||||||
|
{
|
||||||
|
'type': 'numeric',
|
||||||
|
'name': 'size',
|
||||||
|
'description': _('Maximum services'),
|
||||||
|
'default': '10',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
CALENDAR_ACTION_ADD_TRANSPORT: typing.Dict[str, typing.Any] = {
|
||||||
|
'id': 'ADD_TRANSPORT',
|
||||||
|
'description': _('Add a transport'),
|
||||||
|
'params': (
|
||||||
|
{
|
||||||
|
'type': 'transport',
|
||||||
|
'name': 'transport',
|
||||||
|
'description': _('Transport'),
|
||||||
|
'default': '',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
CALENDAR_ACTION_DEL_TRANSPORT: typing.Dict[str, typing.Any] = {
|
||||||
|
'id': 'REMOVE_TRANSPORT',
|
||||||
|
'description': _('Remove a transport'),
|
||||||
|
'params': (
|
||||||
|
{
|
||||||
|
'type': 'transport',
|
||||||
|
'name': 'transport',
|
||||||
|
'description': _('Trasport'),
|
||||||
|
'default': '',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
CALENDAR_ACTION_ADD_GROUP: typing.Dict[str, typing.Any] = {
|
||||||
|
'id': 'ADD_GROUP',
|
||||||
|
'description': _('Add a group'),
|
||||||
|
'params': (
|
||||||
|
{'type': 'group', 'name': 'group', 'description': _('Group'), 'default': ''},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
CALENDAR_ACTION_DEL_GROUP: typing.Dict[str, typing.Any] = {
|
||||||
|
'id': 'REMOVE_GROUP',
|
||||||
|
'description': _('Remove a group'),
|
||||||
|
'params': (
|
||||||
|
{'type': 'group', 'name': 'group', 'description': _('Group'), 'default': ''},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
CALENDAR_ACTION_IGNORE_UNUSED: typing.Dict[str, typing.Any] = {
|
||||||
|
'id': 'IGNORE_UNUSED',
|
||||||
|
'description': _('Sets the ignore unused'),
|
||||||
|
'params': (
|
||||||
|
{
|
||||||
|
'type': 'bool',
|
||||||
|
'name': 'state',
|
||||||
|
'description': _('Ignore assigned and unused'),
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
CALENDAR_ACTION_REMOVE_USERSERVICES: typing.Dict[str, typing.Any] = {
|
||||||
|
'id': 'REMOVE_USERSERVICES',
|
||||||
|
'description': _('Remove ALL assigned user service. USE WITH CAUTION!'),
|
||||||
|
'params': (),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
CALENDAR_ACTION_DICT: typing.Dict[str, typing.Dict] = { c['id']: c for c in (
|
CALENDAR_ACTION_DICT: typing.Dict[str, typing.Dict] = {
|
||||||
CALENDAR_ACTION_PUBLISH, CALENDAR_ACTION_CACHE_L1,
|
c['id']: c
|
||||||
CALENDAR_ACTION_CACHE_L2, CALENDAR_ACTION_INITIAL,
|
for c in (
|
||||||
|
CALENDAR_ACTION_PUBLISH,
|
||||||
|
CALENDAR_ACTION_CACHE_L1,
|
||||||
|
CALENDAR_ACTION_CACHE_L2,
|
||||||
|
CALENDAR_ACTION_INITIAL,
|
||||||
CALENDAR_ACTION_MAX,
|
CALENDAR_ACTION_MAX,
|
||||||
CALENDAR_ACTION_ADD_TRANSPORT, CALENDAR_ACTION_DEL_TRANSPORT,
|
CALENDAR_ACTION_ADD_TRANSPORT,
|
||||||
CALENDAR_ACTION_ADD_GROUP, CALENDAR_ACTION_DEL_GROUP,
|
CALENDAR_ACTION_DEL_TRANSPORT,
|
||||||
|
CALENDAR_ACTION_ADD_GROUP,
|
||||||
|
CALENDAR_ACTION_DEL_GROUP,
|
||||||
CALENDAR_ACTION_IGNORE_UNUSED,
|
CALENDAR_ACTION_IGNORE_UNUSED,
|
||||||
CALENDAR_ACTION_REMOVE_USERSERVICES
|
CALENDAR_ACTION_REMOVE_USERSERVICES,
|
||||||
)}
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class CalendarAction(UUIDModel):
|
class CalendarAction(UUIDModel):
|
||||||
calendar: Calendar = models.ForeignKey(Calendar, on_delete=models.CASCADE)
|
calendar: 'models.ForeignKey[CalendarAction, Calendar]' = models.ForeignKey(
|
||||||
service_pool: ServicePool = models.ForeignKey(ServicePool, on_delete=models.CASCADE)
|
Calendar, on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
service_pool: 'models.ForeignKey[CalendarAction, ServicePool]' = models.ForeignKey(
|
||||||
|
ServicePool, on_delete=models.CASCADE
|
||||||
|
)
|
||||||
action = models.CharField(max_length=64, default='')
|
action = models.CharField(max_length=64, default='')
|
||||||
at_start = models.BooleanField(default=False) # If false, action is done at end of event
|
at_start = models.BooleanField(
|
||||||
|
default=False
|
||||||
|
) # If false, action is done at end of event
|
||||||
events_offset = models.IntegerField(default=0) # In minutes
|
events_offset = models.IntegerField(default=0) # In minutes
|
||||||
params = models.CharField(max_length=1024, default='')
|
params = models.CharField(max_length=1024, default='')
|
||||||
# Not to be edited, just to be used as indicators for executions
|
# Not to be edited, just to be used as indicators for executions
|
||||||
last_execution = models.DateTimeField(default=None, db_index=True, null=True, blank=True)
|
last_execution = models.DateTimeField(
|
||||||
next_execution = models.DateTimeField(default=None, db_index=True, null=True, blank=True)
|
default=None, db_index=True, null=True, blank=True
|
||||||
|
)
|
||||||
|
next_execution = models.DateTimeField(
|
||||||
|
default=None, db_index=True, null=True, blank=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[CalendarAction]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare db table
|
Meta class to declare db table
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_cal_action'
|
db_table = 'uds_cal_action'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
@ -140,7 +258,9 @@ class CalendarAction(UUIDModel):
|
|||||||
logger.exception('error')
|
logger.exception('error')
|
||||||
return '(invalid action)'
|
return '(invalid action)'
|
||||||
|
|
||||||
def execute(self, save: bool = True) -> None: # pylint: disable=too-many-branches, too-many-statements
|
def execute(
|
||||||
|
self, save: bool = True
|
||||||
|
) -> None: # pylint: disable=too-many-branches, too-many-statements
|
||||||
"""Executes the calendar action
|
"""Executes the calendar action
|
||||||
|
|
||||||
Keyword Arguments:
|
Keyword Arguments:
|
||||||
@ -149,7 +269,10 @@ class CalendarAction(UUIDModel):
|
|||||||
logger.debug('Executing action')
|
logger.debug('Executing action')
|
||||||
# If restrained pool, skip this execution (will rery later, not updated)
|
# If restrained pool, skip this execution (will rery later, not updated)
|
||||||
if not self.service_pool.isUsable():
|
if not self.service_pool.isUsable():
|
||||||
logger.info('Execution of task for %s due to contained state (restrained, in maintenance or removing)', self.service_pool.name)
|
logger.info(
|
||||||
|
'Execution of task for %s due to contained state (restrained, in maintenance or removing)',
|
||||||
|
self.service_pool.name,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.last_execution = getSqlDatetime()
|
self.last_execution = getSqlDatetime()
|
||||||
@ -182,11 +305,19 @@ class CalendarAction(UUIDModel):
|
|||||||
self.service_pool.ignores_unused = params['state'] in ('true', '1', True)
|
self.service_pool.ignores_unused = params['state'] in ('true', '1', True)
|
||||||
elif CALENDAR_ACTION_REMOVE_USERSERVICES['id'] == self.action:
|
elif CALENDAR_ACTION_REMOVE_USERSERVICES['id'] == self.action:
|
||||||
# 1.- Remove usable assigned services (Ignore "creating ones", just for created)
|
# 1.- Remove usable assigned services (Ignore "creating ones", just for created)
|
||||||
for userService in self.service_pool.assignedUserServices().filter(state=state.State.USABLE):
|
for userService in self.service_pool.assignedUserServices().filter(
|
||||||
|
state=state.State.USABLE
|
||||||
|
):
|
||||||
userService.remove()
|
userService.remove()
|
||||||
else:
|
else:
|
||||||
caTransports = (CALENDAR_ACTION_ADD_TRANSPORT['id'], CALENDAR_ACTION_DEL_TRANSPORT['id'])
|
caTransports = (
|
||||||
caGroups = (CALENDAR_ACTION_ADD_GROUP['id'], CALENDAR_ACTION_DEL_GROUP['id'])
|
CALENDAR_ACTION_ADD_TRANSPORT['id'],
|
||||||
|
CALENDAR_ACTION_DEL_TRANSPORT['id'],
|
||||||
|
)
|
||||||
|
caGroups = (
|
||||||
|
CALENDAR_ACTION_ADD_GROUP['id'],
|
||||||
|
CALENDAR_ACTION_DEL_GROUP['id'],
|
||||||
|
)
|
||||||
if self.action in caTransports:
|
if self.action in caTransports:
|
||||||
try:
|
try:
|
||||||
t = Transport.objects.get(uuid=params['transport'])
|
t = Transport.objects.get(uuid=params['transport'])
|
||||||
@ -196,7 +327,9 @@ class CalendarAction(UUIDModel):
|
|||||||
self.service_pool.transports.remove(t)
|
self.service_pool.transports.remove(t)
|
||||||
executed = True
|
executed = True
|
||||||
except Exception:
|
except Exception:
|
||||||
self.service_pool.log('Scheduled action not executed because transport is not available anymore')
|
self.service_pool.log(
|
||||||
|
'Scheduled action not executed because transport is not available anymore'
|
||||||
|
)
|
||||||
saveServicePool = False
|
saveServicePool = False
|
||||||
elif self.action in caGroups:
|
elif self.action in caGroups:
|
||||||
try:
|
try:
|
||||||
@ -208,20 +341,27 @@ class CalendarAction(UUIDModel):
|
|||||||
self.service_pool.assignedGroups.remove(grp)
|
self.service_pool.assignedGroups.remove(grp)
|
||||||
executed = True
|
executed = True
|
||||||
except Exception:
|
except Exception:
|
||||||
self.service_pool.log('Scheduled action not executed because group is not available anymore')
|
self.service_pool.log(
|
||||||
|
'Scheduled action not executed because group is not available anymore'
|
||||||
|
)
|
||||||
saveServicePool = False
|
saveServicePool = False
|
||||||
|
|
||||||
if executed:
|
if executed:
|
||||||
try:
|
try:
|
||||||
self.service_pool.log(
|
self.service_pool.log(
|
||||||
'Executed action {} [{}]'.format(
|
'Executed action {} [{}]'.format(
|
||||||
CALENDAR_ACTION_DICT.get(self.action, {})['description'], self.prettyParams
|
CALENDAR_ACTION_DICT.get(self.action, {})['description'],
|
||||||
|
self.prettyParams,
|
||||||
),
|
),
|
||||||
level=log.INFO
|
level=log.INFO,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
# Avoid invalid ACTIONS errors on log
|
# Avoid invalid ACTIONS errors on log
|
||||||
self.service_pool.log('Action {} is not a valid scheduled action! please, remove it from your list.'.format(self.action))
|
self.service_pool.log(
|
||||||
|
'Action {} is not a valid scheduled action! please, remove it from your list.'.format(
|
||||||
|
self.action
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# On save, will regenerate nextExecution
|
# On save, will regenerate nextExecution
|
||||||
if save:
|
if save:
|
||||||
@ -230,9 +370,13 @@ class CalendarAction(UUIDModel):
|
|||||||
if saveServicePool:
|
if saveServicePool:
|
||||||
self.service_pool.save()
|
self.service_pool.save()
|
||||||
|
|
||||||
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
|
def save(
|
||||||
|
self, force_insert=False, force_update=False, using=None, update_fields=None
|
||||||
|
):
|
||||||
lastExecution = self.last_execution or getSqlDatetime()
|
lastExecution = self.last_execution or getSqlDatetime()
|
||||||
possibleNext = calendar.CalendarChecker(self.calendar).nextEvent(checkFrom=lastExecution-self.offset, startEvent=self.at_start)
|
possibleNext = calendar.CalendarChecker(self.calendar).nextEvent(
|
||||||
|
checkFrom=lastExecution - self.offset, startEvent=self.at_start
|
||||||
|
)
|
||||||
if possibleNext:
|
if possibleNext:
|
||||||
self.next_execution = possibleNext + self.offset
|
self.next_execution = possibleNext + self.offset
|
||||||
else:
|
else:
|
||||||
@ -242,5 +386,9 @@ class CalendarAction(UUIDModel):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Calendar of {}, last_execution = {}, next execution = {}, action = {}, params = {}'.format(
|
return 'Calendar of {}, last_execution = {}, next execution = {}, action = {}, params = {}'.format(
|
||||||
self.service_pool.name, self.last_execution, self.next_execution, self.action, self.params
|
self.service_pool.name,
|
||||||
|
self.last_execution,
|
||||||
|
self.next_execution,
|
||||||
|
self.action,
|
||||||
|
self.params,
|
||||||
)
|
)
|
||||||
|
@ -54,7 +54,7 @@ freqs: typing.Tuple[typing.Tuple[str, str], ...] = (
|
|||||||
('MONTHLY', _('Monthly')),
|
('MONTHLY', _('Monthly')),
|
||||||
('WEEKLY', _('Weekly')),
|
('WEEKLY', _('Weekly')),
|
||||||
('DAILY', _('Daily')),
|
('DAILY', _('Daily')),
|
||||||
(WEEKDAYS, _('Weekdays'))
|
(WEEKDAYS, _('Weekdays')),
|
||||||
)
|
)
|
||||||
|
|
||||||
frq_to_rrl: typing.Dict[str, int] = {
|
frq_to_rrl: typing.Dict[str, int] = {
|
||||||
@ -82,10 +82,18 @@ dunit_to_mins: typing.Dict[str, int] = {
|
|||||||
'MINUTES': 1,
|
'MINUTES': 1,
|
||||||
'HOURS': 60,
|
'HOURS': 60,
|
||||||
'DAYS': 60 * 24,
|
'DAYS': 60 * 24,
|
||||||
'WEEKS': 60 * 24 * 7
|
'WEEKS': 60 * 24 * 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
weekdays: typing.Tuple[rules.weekday, ...] = (rules.SU, rules.MO, rules.TU, rules.WE, rules.TH, rules.FR, rules.SA)
|
weekdays: typing.Tuple[rules.weekday, ...] = (
|
||||||
|
rules.SU,
|
||||||
|
rules.MO,
|
||||||
|
rules.TU,
|
||||||
|
rules.WE,
|
||||||
|
rules.TH,
|
||||||
|
rules.FR,
|
||||||
|
rules.SA,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CalendarRule(UUIDModel):
|
class CalendarRule(UUIDModel):
|
||||||
@ -95,16 +103,24 @@ class CalendarRule(UUIDModel):
|
|||||||
start = models.DateTimeField()
|
start = models.DateTimeField()
|
||||||
end = models.DateField(null=True, blank=True)
|
end = models.DateField(null=True, blank=True)
|
||||||
frequency = models.CharField(choices=freqs, max_length=32)
|
frequency = models.CharField(choices=freqs, max_length=32)
|
||||||
interval = models.IntegerField(default=1) # If interval is for WEEKDAYS, every bit means a day of week (bit 0 = SUN, 1 = MON, ...)
|
interval = models.IntegerField(
|
||||||
|
default=1
|
||||||
|
) # If interval is for WEEKDAYS, every bit means a day of week (bit 0 = SUN, 1 = MON, ...)
|
||||||
duration = models.IntegerField(default=0) # Duration in minutes
|
duration = models.IntegerField(default=0) # Duration in minutes
|
||||||
duration_unit = models.CharField(choices=dunits, default='MINUTES', max_length=32)
|
duration_unit = models.CharField(choices=dunits, default='MINUTES', max_length=32)
|
||||||
|
|
||||||
calendar: 'models.ForeignKey[CalendarRule, Calendar]' = models.ForeignKey(Calendar, related_name='rules', on_delete=models.CASCADE)
|
calendar: 'models.ForeignKey[CalendarRule, Calendar]' = models.ForeignKey(
|
||||||
|
Calendar, related_name='rules', on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[CalendarRule]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare db table
|
Meta class to declare db table
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_calendar_rules'
|
db_table = 'uds_calendar_rules'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
@ -112,7 +128,10 @@ class CalendarRule(UUIDModel):
|
|||||||
if self.interval == 0: # Fix 0 intervals
|
if self.interval == 0: # Fix 0 intervals
|
||||||
self.interval = 1
|
self.interval = 1
|
||||||
|
|
||||||
end = datetime.datetime.combine(self.end if self.end is not None else datetime.datetime.max.date(), datetime.datetime.max.time())
|
end = datetime.datetime.combine(
|
||||||
|
self.end if self.end is not None else datetime.datetime.max.date(),
|
||||||
|
datetime.datetime.max.time(),
|
||||||
|
)
|
||||||
|
|
||||||
if self.frequency == WEEKDAYS:
|
if self.frequency == WEEKDAYS:
|
||||||
dw = []
|
dw = []
|
||||||
@ -122,13 +141,21 @@ class CalendarRule(UUIDModel):
|
|||||||
dw.append(weekdays[i])
|
dw.append(weekdays[i])
|
||||||
l >>= 1
|
l >>= 1
|
||||||
return rules.rrule(rules.DAILY, byweekday=dw, dtstart=self.start, until=end)
|
return rules.rrule(rules.DAILY, byweekday=dw, dtstart=self.start, until=end)
|
||||||
return rules.rrule(frq_to_rrl[self.frequency], interval=self.interval, dtstart=self.start, until=end)
|
return rules.rrule(
|
||||||
|
frq_to_rrl[self.frequency],
|
||||||
|
interval=self.interval,
|
||||||
|
dtstart=self.start,
|
||||||
|
until=end,
|
||||||
|
)
|
||||||
|
|
||||||
def as_rrule_end(self) -> rules.rrule:
|
def as_rrule_end(self) -> rules.rrule:
|
||||||
if self.interval == 0: # Fix 0 intervals
|
if self.interval == 0: # Fix 0 intervals
|
||||||
self.interval = 1
|
self.interval = 1
|
||||||
|
|
||||||
end = datetime.datetime.combine(self.end if self.end is not None else datetime.datetime.max.date(), datetime.datetime.max.time())
|
end = datetime.datetime.combine(
|
||||||
|
self.end if self.end is not None else datetime.datetime.max.date(),
|
||||||
|
datetime.datetime.max.time(),
|
||||||
|
)
|
||||||
|
|
||||||
if self.frequency == WEEKDAYS:
|
if self.frequency == WEEKDAYS:
|
||||||
dw = []
|
dw = []
|
||||||
@ -137,8 +164,19 @@ class CalendarRule(UUIDModel):
|
|||||||
if l & 1 == 1:
|
if l & 1 == 1:
|
||||||
dw.append(weekdays[i])
|
dw.append(weekdays[i])
|
||||||
l >>= 1
|
l >>= 1
|
||||||
return rules.rrule(rules.DAILY, byweekday=dw, dtstart=self.start + datetime.timedelta(minutes=self.duration_as_minutes), until=end)
|
return rules.rrule(
|
||||||
return rules.rrule(frq_to_rrl[self.frequency], interval=self.interval, dtstart=self.start + datetime.timedelta(minutes=self.duration_as_minutes), until=end)
|
rules.DAILY,
|
||||||
|
byweekday=dw,
|
||||||
|
dtstart=self.start
|
||||||
|
+ datetime.timedelta(minutes=self.duration_as_minutes),
|
||||||
|
until=end,
|
||||||
|
)
|
||||||
|
return rules.rrule(
|
||||||
|
frq_to_rrl[self.frequency],
|
||||||
|
interval=self.interval,
|
||||||
|
dtstart=self.start + datetime.timedelta(minutes=self.duration_as_minutes),
|
||||||
|
until=end,
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def frequency_as_minutes(self) -> int:
|
def frequency_as_minutes(self) -> int:
|
||||||
@ -150,7 +188,9 @@ class CalendarRule(UUIDModel):
|
|||||||
def duration_as_minutes(self) -> int:
|
def duration_as_minutes(self) -> int:
|
||||||
return dunit_to_mins.get(self.duration_unit, 1) * self.duration
|
return dunit_to_mins.get(self.duration_unit, 1) * self.duration
|
||||||
|
|
||||||
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
|
def save(
|
||||||
|
self, force_insert=False, force_update=False, using=None, update_fields=None
|
||||||
|
):
|
||||||
logger.debug('Saving...')
|
logger.debug('Saving...')
|
||||||
self.calendar.modified = getSqlDatetime()
|
self.calendar.modified = getSqlDatetime()
|
||||||
|
|
||||||
@ -160,4 +200,11 @@ class CalendarRule(UUIDModel):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Rule {0}: {1}-{2}, {3}, Interval: {4}, duration: {5}'.format(self.name, self.start, self.end, self.frequency, self.interval, self.duration)
|
return 'Rule {0}: {1}-{2}, {3}, Interval: {4}, duration: {5}'.format(
|
||||||
|
self.name,
|
||||||
|
self.start,
|
||||||
|
self.end,
|
||||||
|
self.frequency,
|
||||||
|
self.interval,
|
||||||
|
self.duration,
|
||||||
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -43,6 +43,7 @@ class Config(models.Model):
|
|||||||
General configuration values model. Used to store global and specific modules configuration values.
|
General configuration values model. Used to store global and specific modules configuration values.
|
||||||
This model is managed via uds.core.util.config.Config class
|
This model is managed via uds.core.util.config.Config class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
section = models.CharField(max_length=128, db_index=True)
|
section = models.CharField(max_length=128, db_index=True)
|
||||||
key = models.CharField(max_length=64, db_index=True)
|
key = models.CharField(max_length=64, db_index=True)
|
||||||
value = models.TextField(default='')
|
value = models.TextField(default='')
|
||||||
@ -50,10 +51,14 @@ class Config(models.Model):
|
|||||||
long = models.BooleanField(default=False)
|
long = models.BooleanField(default=False)
|
||||||
field_type = models.IntegerField(default=-1)
|
field_type = models.IntegerField(default=-1)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Config]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare default order and unique multiple field index
|
Meta class to declare default order and unique multiple field index
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_configuration'
|
db_table = 'uds_configuration'
|
||||||
unique_together = (('section', 'key'),)
|
unique_together = (('section', 'key'),)
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Model based on https://github.com/llazzaro/django-scheduler
|
# Model based on https://github.com/llazzaro/django-scheduler
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2019 Virtual Cable S.L.
|
# Copyright (c) 2016-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -31,28 +31,33 @@
|
|||||||
"""
|
"""
|
||||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
"""
|
"""
|
||||||
|
import codecs
|
||||||
import logging
|
import logging
|
||||||
import typing
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from uds.core.util import encoders
|
|
||||||
|
|
||||||
from .uuid_model import UUIDModel
|
from .uuid_model import UUIDModel
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DBFile(UUIDModel):
|
class DBFile(UUIDModel):
|
||||||
owner = models.CharField(max_length=32, default='') # Not indexed, used for cleanups only
|
|
||||||
|
# Not indexed, used for cleanups only
|
||||||
|
owner = models.CharField(max_length=32, default='')
|
||||||
name = models.CharField(max_length=255, primary_key=True)
|
name = models.CharField(max_length=255, primary_key=True)
|
||||||
content = models.TextField(blank=True)
|
content = models.TextField(blank=True)
|
||||||
size = models.IntegerField(default=0)
|
size = models.IntegerField(default=0)
|
||||||
created = models.DateTimeField()
|
created = models.DateTimeField()
|
||||||
modified = models.DateTimeField()
|
modified = models.DateTimeField()
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[DBFile]'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self) -> bytes:
|
def data(self) -> bytes:
|
||||||
try:
|
try:
|
||||||
return typing.cast(bytes, encoders.decode(encoders.decode(self.content, 'base64'), 'zip'))
|
return codecs.decode(codecs.decode(self.content.encode(), 'base64'), 'zip')
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error('DBFile %s has errors and cannot be used', self.name)
|
logger.error('DBFile %s has errors and cannot be used', self.name)
|
||||||
try:
|
try:
|
||||||
@ -65,7 +70,9 @@ class DBFile(UUIDModel):
|
|||||||
@data.setter
|
@data.setter
|
||||||
def data(self, value: bytes):
|
def data(self, value: bytes):
|
||||||
self.size = len(value)
|
self.size = len(value)
|
||||||
self.content = typing.cast(str, encoders.encode(encoders.encode(value, 'zip'), 'base64', asText=True))
|
self.content = codecs.encode(codecs.encode(value, 'zip'), 'base64').decode()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return 'File: {} {} {} {}'.format(self.name, self.size, self.created, self.modified)
|
return 'File: {} {} {} {}'.format(
|
||||||
|
self.name, self.size, self.created, self.modified
|
||||||
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2020 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -55,6 +55,9 @@ class DelayedTask(models.Model):
|
|||||||
execution_delay = models.PositiveIntegerField()
|
execution_delay = models.PositiveIntegerField()
|
||||||
execution_time = models.DateTimeField(db_index=True)
|
execution_time = models.DateTimeField(db_index=True)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[DelayedTask]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare default order and unique multiple field index
|
Meta class to declare default order and unique multiple field index
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -54,8 +54,10 @@ class Group(UUIDModel):
|
|||||||
"""
|
"""
|
||||||
This class represents a group, associated with one authenticator
|
This class represents a group, associated with one authenticator
|
||||||
"""
|
"""
|
||||||
# pylint: disable=model-missing-unicode
|
|
||||||
manager: 'models.ForeignKey[Group, Authenticator]' = UnsavedForeignKey(Authenticator, on_delete=models.CASCADE, related_name='groups')
|
manager: 'models.ForeignKey[Group, Authenticator]' = UnsavedForeignKey(
|
||||||
|
Authenticator, on_delete=models.CASCADE, related_name='groups'
|
||||||
|
)
|
||||||
name = models.CharField(max_length=128, db_index=True)
|
name = models.CharField(max_length=128, db_index=True)
|
||||||
state = models.CharField(max_length=1, default=State.ACTIVE, db_index=True)
|
state = models.CharField(max_length=1, default=State.ACTIVE, db_index=True)
|
||||||
comments = models.CharField(max_length=256, default='')
|
comments = models.CharField(max_length=256, default='')
|
||||||
@ -65,10 +67,14 @@ class Group(UUIDModel):
|
|||||||
groups = models.ManyToManyField('self', symmetrical=False)
|
groups = models.ManyToManyField('self', symmetrical=False)
|
||||||
created = models.DateTimeField(default=getSqlDatetime, blank=True)
|
created = models.DateTimeField(default=getSqlDatetime, blank=True)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Group]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare default order and unique multiple field index
|
Meta class to declare default order and unique multiple field index
|
||||||
"""
|
"""
|
||||||
|
|
||||||
unique_together = (("manager", "name"),)
|
unique_together = (("manager", "name"),)
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
@ -87,12 +93,16 @@ class Group(UUIDModel):
|
|||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
if self.is_meta:
|
if self.is_meta:
|
||||||
return "Meta group {}(id:{}) with groups {}".format(self.name, self.id, list(self.groups.all()))
|
return "Meta group {}(id:{}) with groups {}".format(
|
||||||
|
self.name, self.id, list(self.groups.all())
|
||||||
|
)
|
||||||
|
|
||||||
return "Group {}(id:{}) from auth {}".format(self.name, self.id, self.manager.name)
|
return "Group {}(id:{}) from auth {}".format(
|
||||||
|
self.name, self.id, self.manager.name
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def beforeDelete(sender, **kwargs):
|
def beforeDelete(sender, **kwargs) -> None:
|
||||||
"""
|
"""
|
||||||
Used to invoke the Service class "Destroy" before deleting it from database.
|
Used to invoke the Service class "Destroy" before deleting it from database.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -29,6 +29,7 @@
|
|||||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
"""
|
"""
|
||||||
import io
|
import io
|
||||||
|
import codecs
|
||||||
import logging
|
import logging
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
@ -38,8 +39,6 @@ import PIL.Image
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
from uds.core.util import encoders
|
|
||||||
|
|
||||||
|
|
||||||
from .uuid_model import UUIDModel
|
from .uuid_model import UUIDModel
|
||||||
from .util import getSqlDatetime
|
from .util import getSqlDatetime
|
||||||
@ -54,30 +53,37 @@ class Image(UUIDModel):
|
|||||||
This is intended for small images (i will limit them to 128x128), so storing at db is fine
|
This is intended for small images (i will limit them to 128x128), so storing at db is fine
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MAX_IMAGE_SIZE = (128, 128)
|
MAX_IMAGE_SIZE = (128, 128)
|
||||||
THUMBNAIL_SIZE = (48, 48)
|
THUMBNAIL_SIZE = (48, 48)
|
||||||
|
|
||||||
name = models.CharField(max_length=128, unique=True, db_index=True)
|
name = models.CharField(max_length=128, unique=True, db_index=True)
|
||||||
stamp = models.DateTimeField() # Date creation or validation of this entry. Set at write time
|
stamp = (
|
||||||
|
models.DateTimeField()
|
||||||
|
) # Date creation or validation of this entry. Set at write time
|
||||||
data = models.BinaryField() # Image storage
|
data = models.BinaryField() # Image storage
|
||||||
thumb = models.BinaryField() # Thumbnail, very small
|
thumb = models.BinaryField() # Thumbnail, very small
|
||||||
width = models.IntegerField(default=0)
|
width = models.IntegerField(default=0)
|
||||||
height = models.IntegerField(default=0)
|
height = models.IntegerField(default=0)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Image]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare the name of the table at database
|
Meta class to declare the name of the table at database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_images'
|
db_table = 'uds_images'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def encode64(data: bytes) -> str:
|
def encode64(data: bytes) -> str:
|
||||||
return typing.cast(str, encoders.encode(data, 'base64', asText=True)).replace('\n', '') # Removes \n
|
return codecs.encode(data, 'base64').decode().replace('\n', '')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def decode64(data64: str) -> bytes:
|
def decode64(data64: str) -> bytes:
|
||||||
return typing.cast(bytes, encoders.decode(data64, 'base64'))
|
return codecs.decode(data64.encode(), 'base64')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def prepareForDb(data: bytes) -> bytes:
|
def prepareForDb(data: bytes) -> bytes:
|
||||||
@ -168,12 +174,16 @@ class Image(UUIDModel):
|
|||||||
def thumbnailResponse(self) -> HttpResponse:
|
def thumbnailResponse(self) -> HttpResponse:
|
||||||
return HttpResponse(self.thumb, content_type='image/png')
|
return HttpResponse(self.thumb, content_type='image/png')
|
||||||
|
|
||||||
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
|
def save(
|
||||||
|
self, force_insert=False, force_update=False, using=None, update_fields=None
|
||||||
|
):
|
||||||
self.stamp = getSqlDatetime()
|
self.stamp = getSqlDatetime()
|
||||||
return super().save(force_insert, force_update, using, update_fields)
|
return super().save(force_insert, force_update, using, update_fields)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Image id {}, name {}, {} bytes, {} bytes thumb'.format(self.id, self.name, len(self.data), len(self.thumb))
|
return 'Image id {}, name {}, {} bytes, {} bytes thumb'.format(
|
||||||
|
self.id, self.name, len(self.data), len(self.thumb)
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def beforeDelete(sender, **kwargs):
|
def beforeDelete(sender, **kwargs):
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -54,12 +54,23 @@ class Log(models.Model):
|
|||||||
level = models.PositiveIntegerField(default=0, db_index=True)
|
level = models.PositiveIntegerField(default=0, db_index=True)
|
||||||
data = models.CharField(max_length=255, default='')
|
data = models.CharField(max_length=255, default='')
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Log]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare db table
|
Meta class to declare db table
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_log'
|
db_table = 'uds_log'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return "Log of {}({}): {} - {} - {} - {}".format(self.owner_type, self.owner_id, self.created, self.source, self.level, self.data)
|
return "Log of {}({}): {} - {} - {} - {}".format(
|
||||||
|
self.owner_type,
|
||||||
|
self.owner_id,
|
||||||
|
self.created,
|
||||||
|
self.source,
|
||||||
|
self.level,
|
||||||
|
self.data,
|
||||||
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -49,6 +49,7 @@ class ManagedObjectModel(UUIDModel):
|
|||||||
Base abstract model for models that are top level Managed Objects
|
Base abstract model for models that are top level Managed Objects
|
||||||
(such as Authenticator, Transport, OSManager, Provider, Service ...)
|
(such as Authenticator, Transport, OSManager, Provider, Service ...)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = models.CharField(max_length=128, unique=False, db_index=True)
|
name = models.CharField(max_length=128, unique=False, db_index=True)
|
||||||
data_type = models.CharField(max_length=128)
|
data_type = models.CharField(max_length=128)
|
||||||
data = models.TextField(default='')
|
data = models.TextField(default='')
|
||||||
@ -60,6 +61,7 @@ class ManagedObjectModel(UUIDModel):
|
|||||||
"""
|
"""
|
||||||
Defines this is an abstract class
|
Defines this is an abstract class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
def getEnvironment(self) -> Environment:
|
def getEnvironment(self) -> Environment:
|
||||||
@ -79,7 +81,9 @@ class ManagedObjectModel(UUIDModel):
|
|||||||
|
|
||||||
self._cachedInstance = None # Ensures returns correct value on getInstance
|
self._cachedInstance = None # Ensures returns correct value on getInstance
|
||||||
|
|
||||||
def getInstance(self, values: typing.Optional[typing.Dict[str, str]] = None) -> Module:
|
def getInstance(
|
||||||
|
self, values: typing.Optional[typing.Dict[str, str]] = None
|
||||||
|
) -> Module:
|
||||||
"""
|
"""
|
||||||
Instantiates the object this record contains.
|
Instantiates the object this record contains.
|
||||||
|
|
||||||
@ -95,7 +99,6 @@ class ManagedObjectModel(UUIDModel):
|
|||||||
Can be overriden
|
Can be overriden
|
||||||
"""
|
"""
|
||||||
if self._cachedInstance and values is None:
|
if self._cachedInstance and values is None:
|
||||||
# logger.debug('Got cached instance instead of deserializing a new one for {}'.format(self.name))
|
|
||||||
return self._cachedInstance
|
return self._cachedInstance
|
||||||
|
|
||||||
klass = self.getType()
|
klass = self.getType()
|
||||||
@ -112,7 +115,9 @@ class ManagedObjectModel(UUIDModel):
|
|||||||
Returns the type of self (as python type)
|
Returns the type of self (as python type)
|
||||||
Must be overriden!!!
|
Must be overriden!!!
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('getType has not been implemented for {}'.format(self.__class__))
|
raise NotImplementedError(
|
||||||
|
'getType has not been implemented for {}'.format(self.__class__)
|
||||||
|
)
|
||||||
|
|
||||||
def isOfType(self, type_: str) -> bool:
|
def isOfType(self, type_: str) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2019 Virtual Cable S.L.
|
# Copyright (c) 2018-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -55,7 +55,6 @@ if typing.TYPE_CHECKING:
|
|||||||
from uds.models import User, CalendarAccessMeta
|
from uds.models import User, CalendarAccessMeta
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -63,6 +62,7 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
"""
|
"""
|
||||||
A meta pool is a pool that has pool members
|
A meta pool is a pool that has pool members
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Type of pool selection for meta pool
|
# Type of pool selection for meta pool
|
||||||
ROUND_ROBIN_POOL = 0
|
ROUND_ROBIN_POOL = 0
|
||||||
PRIORITY_POOL = 1
|
PRIORITY_POOL = 1
|
||||||
@ -78,9 +78,23 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
short_name = models.CharField(max_length=32, default='')
|
short_name = models.CharField(max_length=32, default='')
|
||||||
comments = models.CharField(max_length=256, default='')
|
comments = models.CharField(max_length=256, default='')
|
||||||
visible = models.BooleanField(default=True)
|
visible = models.BooleanField(default=True)
|
||||||
image = models.ForeignKey(Image, null=True, blank=True, related_name='metaPools', on_delete=models.SET_NULL)
|
image = models.ForeignKey(
|
||||||
servicesPoolGroup = models.ForeignKey(ServicePoolGroup, null=True, blank=True, related_name='metaPools', on_delete=models.SET_NULL)
|
Image,
|
||||||
assignedGroups = models.ManyToManyField(Group, related_name='metaPools', db_table='uds__meta_grps')
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name='metaPools',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
)
|
||||||
|
servicesPoolGroup = models.ForeignKey(
|
||||||
|
ServicePoolGroup,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name='metaPools',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
)
|
||||||
|
assignedGroups = models.ManyToManyField(
|
||||||
|
Group, related_name='metaPools', db_table='uds__meta_grps'
|
||||||
|
)
|
||||||
|
|
||||||
# Message if access denied
|
# Message if access denied
|
||||||
calendar_message = models.CharField(default='', max_length=256)
|
calendar_message = models.CharField(default='', max_length=256)
|
||||||
@ -90,7 +104,8 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
# Pool selection policy
|
# Pool selection policy
|
||||||
policy = models.SmallIntegerField(default=0)
|
policy = models.SmallIntegerField(default=0)
|
||||||
|
|
||||||
# "fake" relations declarations for type checking
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[MetaPool]'
|
||||||
calendarAccess: 'models.QuerySet[CalendarAccessMeta]'
|
calendarAccess: 'models.QuerySet[CalendarAccessMeta]'
|
||||||
members: 'models.QuerySet[MetaPoolMember]'
|
members: 'models.QuerySet[MetaPoolMember]'
|
||||||
|
|
||||||
@ -98,6 +113,7 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
"""
|
"""
|
||||||
Meta class to declare the name of the table at database
|
Meta class to declare the name of the table at database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds__pool_meta'
|
db_table = 'uds__pool_meta'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
@ -115,7 +131,9 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
maintenance += 1
|
maintenance += 1
|
||||||
return total == maintenance
|
return total == maintenance
|
||||||
|
|
||||||
def isAccessAllowed(self, chkDateTime: typing.Optional['datetime.datetime'] = None) -> bool:
|
def isAccessAllowed(
|
||||||
|
self, chkDateTime: typing.Optional['datetime.datetime'] = None
|
||||||
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks if the access for a service pool is allowed or not (based esclusively on associated calendars)
|
Checks if the access for a service pool is allowed or not (based esclusively on associated calendars)
|
||||||
"""
|
"""
|
||||||
@ -133,13 +151,17 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def visual_name(self) -> str:
|
def visual_name(self) -> str:
|
||||||
logger.debug('SHORT: %s %s %s', self.short_name, self.short_name is not None, self.name)
|
logger.debug(
|
||||||
|
'SHORT: %s %s %s', self.short_name, self.short_name is not None, self.name
|
||||||
|
)
|
||||||
if self.short_name.strip():
|
if self.short_name.strip():
|
||||||
return self.short_name
|
return self.short_name
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getForGroups(groups: typing.Iterable['Group'], user: typing.Optional['User'] = None) -> 'QuerySet[MetaPool]':
|
def getForGroups(
|
||||||
|
groups: typing.Iterable['Group'], user: typing.Optional['User'] = None
|
||||||
|
) -> 'QuerySet[MetaPool]':
|
||||||
"""
|
"""
|
||||||
Return deployed services with publications for the groups requested.
|
Return deployed services with publications for the groups requested.
|
||||||
|
|
||||||
@ -153,7 +175,7 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
meta = MetaPool.objects.filter(
|
meta = MetaPool.objects.filter(
|
||||||
assignedGroups__in=groups,
|
assignedGroups__in=groups,
|
||||||
assignedGroups__state=states.group.ACTIVE,
|
assignedGroups__state=states.group.ACTIVE,
|
||||||
visible=True
|
visible=True,
|
||||||
).prefetch_related(
|
).prefetch_related(
|
||||||
'servicesPoolGroup',
|
'servicesPoolGroup',
|
||||||
'servicesPoolGroup__image',
|
'servicesPoolGroup__image',
|
||||||
@ -168,7 +190,7 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
'calendarAccess',
|
'calendarAccess',
|
||||||
'calendarAccess__calendar',
|
'calendarAccess__calendar',
|
||||||
'calendarAccess__calendar__rules',
|
'calendarAccess__calendar__rules',
|
||||||
'image'
|
'image',
|
||||||
)
|
)
|
||||||
if user:
|
if user:
|
||||||
meta = meta.annotate(
|
meta = meta.annotate(
|
||||||
@ -177,8 +199,8 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
filter=models.Q(
|
filter=models.Q(
|
||||||
members__pool__userServices__user=user,
|
members__pool__userServices__user=user,
|
||||||
members__pool__userServices__in_use=True,
|
members__pool__userServices__in_use=True,
|
||||||
members__pool__userServices__state__in=states.userService.USABLE
|
members__pool__userServices__state__in=states.userService.USABLE,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# TODO: Maybe we can exclude non "usable" metapools (all his pools are in maintenance mode?)
|
# TODO: Maybe we can exclude non "usable" metapools (all his pools are in maintenance mode?)
|
||||||
@ -196,6 +218,7 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
:note: If destroy raises an exception, the deletion is not taken.
|
:note: If destroy raises an exception, the deletion is not taken.
|
||||||
"""
|
"""
|
||||||
from uds.core.util.permissions import clean
|
from uds.core.util.permissions import clean
|
||||||
|
|
||||||
toDelete = kwargs['instance']
|
toDelete = kwargs['instance']
|
||||||
|
|
||||||
# Clears related logs
|
# Clears related logs
|
||||||
@ -215,8 +238,12 @@ signals.pre_delete.connect(MetaPool.beforeDelete, sender=MetaPool)
|
|||||||
|
|
||||||
|
|
||||||
class MetaPoolMember(UUIDModel):
|
class MetaPoolMember(UUIDModel):
|
||||||
pool: 'models.ForeignKey[MetaPoolMember, ServicePool]' = models.ForeignKey(ServicePool, related_name='memberOfMeta', on_delete=models.CASCADE)
|
pool: 'models.ForeignKey[MetaPoolMember, ServicePool]' = models.ForeignKey(
|
||||||
meta_pool: 'models.ForeignKey[MetaPoolMember, MetaPool]' = models.ForeignKey(MetaPool, related_name='members', on_delete=models.CASCADE)
|
ServicePool, related_name='memberOfMeta', on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
meta_pool: 'models.ForeignKey[MetaPoolMember, MetaPool]' = models.ForeignKey(
|
||||||
|
MetaPool, related_name='members', on_delete=models.CASCADE
|
||||||
|
)
|
||||||
priority = models.PositiveIntegerField(default=0)
|
priority = models.PositiveIntegerField(default=0)
|
||||||
enabled = models.BooleanField(default=True)
|
enabled = models.BooleanField(default=True)
|
||||||
|
|
||||||
@ -224,8 +251,11 @@ class MetaPoolMember(UUIDModel):
|
|||||||
"""
|
"""
|
||||||
Meta class to declare the name of the table at database
|
Meta class to declare the name of the table at database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds__meta_pool_member'
|
db_table = 'uds__meta_pool_member'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return '{}/{} {} {}'.format(self.pool.name, self.meta_pool.name, self.priority, self.enabled)
|
return '{}/{} {} {}'.format(
|
||||||
|
self.pool.name, self.meta_pool.name, self.priority, self.enabled
|
||||||
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -49,17 +49,23 @@ class Network(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
"""
|
"""
|
||||||
This model is used for keeping information of networks associated with transports (right now, just transports..)
|
This model is used for keeping information of networks associated with transports (right now, just transports..)
|
||||||
"""
|
"""
|
||||||
# pylint: disable=model-missing-unicode
|
|
||||||
name = models.CharField(max_length=64, unique=True)
|
name = models.CharField(max_length=64, unique=True)
|
||||||
net_start = models.BigIntegerField(db_index=True)
|
net_start = models.BigIntegerField(db_index=True)
|
||||||
net_end = models.BigIntegerField(db_index=True)
|
net_end = models.BigIntegerField(db_index=True)
|
||||||
net_string = models.CharField(max_length=128, default='')
|
net_string = models.CharField(max_length=128, default='')
|
||||||
transports = models.ManyToManyField(Transport, related_name='networks', db_table='uds_net_trans')
|
transports = models.ManyToManyField(
|
||||||
|
Transport, related_name='networks', db_table='uds_net_trans'
|
||||||
|
)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Network]'
|
||||||
|
|
||||||
class Meta(UUIDModel.Meta):
|
class Meta(UUIDModel.Meta):
|
||||||
"""
|
"""
|
||||||
Meta class to declare default order
|
Meta class to declare default order
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
@ -82,7 +88,9 @@ class Network(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
netEnd: Network end
|
netEnd: Network end
|
||||||
"""
|
"""
|
||||||
nr = net.networkFromString(netRange)
|
nr = net.networkFromString(netRange)
|
||||||
return Network.objects.create(name=name, net_start=nr[0], net_end=nr[1], net_string=netRange)
|
return Network.objects.create(
|
||||||
|
name=name, net_start=nr[0], net_end=nr[1], net_string=netRange
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def netStart(self) -> str:
|
def netStart(self) -> str:
|
||||||
@ -123,11 +131,17 @@ class Network(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return u'Network {} ({}) from {} to {}'.format(self.name, self.net_string, net.longToIp(self.net_start), net.longToIp(self.net_end))
|
return u'Network {} ({}) from {} to {}'.format(
|
||||||
|
self.name,
|
||||||
|
self.net_string,
|
||||||
|
net.longToIp(self.net_start),
|
||||||
|
net.longToIp(self.net_end),
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def beforeDelete(sender, **kwargs) -> None:
|
def beforeDelete(sender, **kwargs) -> None:
|
||||||
from uds.core.util.permissions import clean
|
from uds.core.util.permissions import clean
|
||||||
|
|
||||||
toDelete = kwargs['instance']
|
toDelete = kwargs['instance']
|
||||||
|
|
||||||
logger.debug('Before delete auth %s', toDelete)
|
logger.debug('Before delete auth %s', toDelete)
|
||||||
@ -135,5 +149,6 @@ class Network(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
# Clears related permissions
|
# Clears related permissions
|
||||||
clean(toDelete)
|
clean(toDelete)
|
||||||
|
|
||||||
|
|
||||||
# Connects a pre deletion signal to Authenticator
|
# Connects a pre deletion signal to Authenticator
|
||||||
models.signals.pre_delete.connect(Network.beforeDelete, sender=Network)
|
models.signals.pre_delete.connect(Network.beforeDelete, sender=Network)
|
||||||
|
@ -51,17 +51,21 @@ class OSManager(ManagedObjectModel, TaggingMixin): # type: ignore
|
|||||||
An OS Manager represents a manager for responding requests for agents inside services.
|
An OS Manager represents a manager for responding requests for agents inside services.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# "fake" relations declarations for type checking
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[OSManager]'
|
||||||
deployedServices: 'models.QuerySet[ServicePool]'
|
deployedServices: 'models.QuerySet[ServicePool]'
|
||||||
|
|
||||||
class Meta(ManagedObjectModel.Meta):
|
class Meta(ManagedObjectModel.Meta):
|
||||||
"""
|
"""
|
||||||
Meta class to declare default order
|
Meta class to declare default order
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
def getInstance(self, values: typing.Optional[typing.Dict[str, str]] = None) -> 'osmanagers.OSManager':
|
def getInstance(
|
||||||
|
self, values: typing.Optional[typing.Dict[str, str]] = None
|
||||||
|
) -> 'osmanagers.OSManager':
|
||||||
return typing.cast('osmanagers.OSManager', super().getInstance(values=values))
|
return typing.cast('osmanagers.OSManager', super().getInstance(values=values))
|
||||||
|
|
||||||
def getType(self) -> typing.Type['osmanagers.OSManager']:
|
def getType(self) -> typing.Type['osmanagers.OSManager']:
|
||||||
@ -76,7 +80,7 @@ class OSManager(ManagedObjectModel, TaggingMixin): # type: ignore
|
|||||||
:note: We only need to get info from this, not access specific data (class specific info)
|
:note: We only need to get info from this, not access specific data (class specific info)
|
||||||
"""
|
"""
|
||||||
# We only need to get info from this, not access specific data (class specific info)
|
# We only need to get info from this, not access specific data (class specific info)
|
||||||
from uds.core import osmanagers # pylint: disable=redefined-outer-name
|
from uds.core import osmanagers
|
||||||
|
|
||||||
type_ = osmanagers.factory().lookup(self.data_type)
|
type_ = osmanagers.factory().lookup(self.data_type)
|
||||||
if type_:
|
if type_:
|
||||||
@ -100,7 +104,7 @@ class OSManager(ManagedObjectModel, TaggingMixin): # type: ignore
|
|||||||
self.delete()
|
self.delete()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return "{0} of type {1} (id:{2})".format(self.name, self.data_type, self.id)
|
return "{0} of type {1} (id:{2})".format(self.name, self.data_type, self.id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -115,7 +119,9 @@ class OSManager(ManagedObjectModel, TaggingMixin): # type: ignore
|
|||||||
"""
|
"""
|
||||||
toDelete = kwargs['instance']
|
toDelete = kwargs['instance']
|
||||||
if toDelete.deployedServices.count() > 0:
|
if toDelete.deployedServices.count() > 0:
|
||||||
raise IntegrityError('Can\'t remove os managers with assigned deployed services')
|
raise IntegrityError(
|
||||||
|
'Can\'t remove os managers with assigned deployed services'
|
||||||
|
)
|
||||||
# Only tries to get instance if data is not empty
|
# Only tries to get instance if data is not empty
|
||||||
if toDelete.data != '':
|
if toDelete.data != '':
|
||||||
s = toDelete.getInstance()
|
s = toDelete.getInstance()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -49,6 +49,7 @@ class Permissions(UUIDModel):
|
|||||||
"""
|
"""
|
||||||
An OS Manager represents a manager for responding requests for agents inside services.
|
An OS Manager represents a manager for responding requests for agents inside services.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Allowed permissions
|
# Allowed permissions
|
||||||
PERMISSION_NONE = 0
|
PERMISSION_NONE = 0
|
||||||
PERMISSION_READ = 32
|
PERMISSION_READ = 32
|
||||||
@ -56,23 +57,42 @@ class Permissions(UUIDModel):
|
|||||||
PERMISSION_ALL = 96
|
PERMISSION_ALL = 96
|
||||||
|
|
||||||
created = models.DateTimeField(db_index=True)
|
created = models.DateTimeField(db_index=True)
|
||||||
ends = models.DateTimeField(db_index=True, null=True, blank=True, default=None) # Future "permisions ends at this moment", not assigned right now
|
ends = models.DateTimeField(
|
||||||
|
db_index=True, null=True, blank=True, default=None
|
||||||
|
) # Future "permisions ends at this moment", not assigned right now
|
||||||
|
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='permissions', null=True, blank=True, default=None)
|
user = models.ForeignKey(
|
||||||
group = models.ForeignKey(Group, on_delete=models.CASCADE, related_name='permissions', null=True, blank=True, default=None)
|
User,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='permissions',
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
group = models.ForeignKey(
|
||||||
|
Group,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='permissions',
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
|
||||||
object_type = models.SmallIntegerField(default=-1, db_index=True)
|
object_type = models.SmallIntegerField(default=-1, db_index=True)
|
||||||
object_id = models.IntegerField(default=None, db_index=True, null=True, blank=True)
|
object_id = models.IntegerField(default=None, db_index=True, null=True, blank=True)
|
||||||
|
|
||||||
permission = models.SmallIntegerField(default=PERMISSION_NONE, db_index=True)
|
permission = models.SmallIntegerField(default=PERMISSION_NONE, db_index=True)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Permissions]'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def permissionAsString(perm: int) -> str:
|
def permissionAsString(perm: int) -> str:
|
||||||
return {
|
return {
|
||||||
Permissions.PERMISSION_NONE: _('None'),
|
Permissions.PERMISSION_NONE: _('None'),
|
||||||
Permissions.PERMISSION_READ: _('Read'),
|
Permissions.PERMISSION_READ: _('Read'),
|
||||||
Permissions.PERMISSION_MANAGEMENT: _('Manage'),
|
Permissions.PERMISSION_MANAGEMENT: _('Manage'),
|
||||||
Permissions.PERMISSION_ALL: _('All')
|
Permissions.PERMISSION_ALL: _('All'),
|
||||||
}.get(perm, _('None'))
|
}.get(perm, _('None'))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -104,13 +124,22 @@ class Permissions(UUIDModel):
|
|||||||
q = Q(group=group)
|
q = Q(group=group)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
existing = Permissions.objects.filter(q, object_type=object_type, object_id=object_id)[0]
|
existing = Permissions.objects.filter(
|
||||||
|
q, object_type=object_type, object_id=object_id
|
||||||
|
)[0]
|
||||||
existing.permission = permission
|
existing.permission = permission
|
||||||
existing.save()
|
existing.save()
|
||||||
return existing
|
return existing
|
||||||
except Exception: # Does not exists
|
except Exception: # Does not exists
|
||||||
return Permissions.objects.create(created=getSqlDatetime(), ends=None, user=user, group=group,
|
return Permissions.objects.create(
|
||||||
object_type=object_type, object_id=object_id, permission=permission)
|
created=getSqlDatetime(),
|
||||||
|
ends=None,
|
||||||
|
user=user,
|
||||||
|
group=group,
|
||||||
|
object_type=object_type,
|
||||||
|
object_id=object_id,
|
||||||
|
permission=permission,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getPermissions(**kwargs) -> int:
|
def getPermissions(**kwargs) -> int:
|
||||||
@ -141,7 +170,7 @@ class Permissions(UUIDModel):
|
|||||||
perm = Permissions.objects.filter(
|
perm = Permissions.objects.filter(
|
||||||
Q(object_type=object_type),
|
Q(object_type=object_type),
|
||||||
Q(object_id=None) | Q(object_id=object_id),
|
Q(object_id=None) | Q(object_id=object_id),
|
||||||
q
|
q,
|
||||||
).order_by('-permission')[0]
|
).order_by('-permission')[0]
|
||||||
logger.debug('Got permission %s', perm)
|
logger.debug('Got permission %s', perm)
|
||||||
return perm.permission
|
return perm.permission
|
||||||
@ -157,7 +186,9 @@ class Permissions(UUIDModel):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def cleanPermissions(object_type, object_id) -> None:
|
def cleanPermissions(object_type, object_id) -> None:
|
||||||
Permissions.objects.filter(object_type=object_type, object_id=object_id).delete()
|
Permissions.objects.filter(
|
||||||
|
object_type=object_type, object_id=object_id
|
||||||
|
).delete()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def cleanUserPermissions(user) -> None:
|
def cleanUserPermissions(user) -> None:
|
||||||
@ -173,5 +204,10 @@ class Permissions(UUIDModel):
|
|||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return 'Permission {}, user {} group {} object_type {} object_id {} permission {}'.format(
|
return 'Permission {}, user {} group {} object_type {} object_id {} permission {}'.format(
|
||||||
self.uuid, self.user, self.group, self.object_type, self.object_id, Permissions.permissionAsString(self.permission)
|
self.uuid,
|
||||||
|
self.user,
|
||||||
|
self.group,
|
||||||
|
self.object_type,
|
||||||
|
self.object_id,
|
||||||
|
Permissions.permissionAsString(self.permission),
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -52,33 +52,43 @@ class Provider(ManagedObjectModel, TaggingMixin): # type: ignore
|
|||||||
A Provider represents the Service provider itself, (i.e. a KVM Server or a Terminal Server)
|
A Provider represents the Service provider itself, (i.e. a KVM Server or a Terminal Server)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# pylint: disable=model-missing-unicode
|
|
||||||
maintenance_mode = models.BooleanField(default=False, db_index=True)
|
maintenance_mode = models.BooleanField(default=False, db_index=True)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Provider]'
|
||||||
|
|
||||||
class Meta(ManagedObjectModel.Meta):
|
class Meta(ManagedObjectModel.Meta):
|
||||||
"""
|
"""
|
||||||
Meta class to declare default order
|
Meta class to declare default order
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
def getType(self) -> typing.Type['services.ServiceProvider']:
|
def getType(self) -> typing.Type['services.ServiceProvider']:
|
||||||
'''
|
"""
|
||||||
Get the type of the object this record represents.
|
Get the type of the object this record represents.
|
||||||
|
|
||||||
The type is Python type, it obtains this type from ServiceProviderFactory and associated record field.
|
The type is Python type, it obtains this type from ServiceProviderFactory and associated record field.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The python type for this record object
|
The python type for this record object
|
||||||
'''
|
"""
|
||||||
from uds.core import services # pylint: disable=redefined-outer-name
|
from uds.core import services # pylint: disable=redefined-outer-name
|
||||||
|
|
||||||
type_ = services.factory().lookup(self.data_type)
|
type_ = services.factory().lookup(self.data_type)
|
||||||
if type_:
|
if type_:
|
||||||
return type_
|
return type_
|
||||||
return services.ServiceProvider # Basic Service implementation. Will fail if we try to use it, but will be ok to reference it
|
return (
|
||||||
|
services.ServiceProvider
|
||||||
|
) # Basic Service implementation. Will fail if we try to use it, but will be ok to reference it
|
||||||
|
|
||||||
def getInstance(self, values: typing.Optional[typing.Dict[str, str]] = None) -> 'services.ServiceProvider':
|
def getInstance(
|
||||||
prov: services.ServiceProvider = typing.cast('services.ServiceProvider', super().getInstance(values=values))
|
self, values: typing.Optional[typing.Dict[str, str]] = None
|
||||||
|
) -> 'services.ServiceProvider':
|
||||||
|
prov: services.ServiceProvider = typing.cast(
|
||||||
|
'services.ServiceProvider', super().getInstance(values=values)
|
||||||
|
)
|
||||||
# Set uuid
|
# Set uuid
|
||||||
prov.setUuid(self.uuid)
|
prov.setUuid(self.uuid)
|
||||||
return prov
|
return prov
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -47,6 +47,7 @@ class Proxy(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
"""
|
"""
|
||||||
Proxy DB model
|
Proxy DB model
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = models.CharField(max_length=128, unique=False, db_index=True)
|
name = models.CharField(max_length=128, unique=False, db_index=True)
|
||||||
comments = models.CharField(max_length=256)
|
comments = models.CharField(max_length=256)
|
||||||
|
|
||||||
@ -55,16 +56,22 @@ class Proxy(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
ssl = models.BooleanField(default=True)
|
ssl = models.BooleanField(default=True)
|
||||||
check_cert = models.BooleanField(default=False)
|
check_cert = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Proxy]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare the name of the table at database
|
Meta class to declare the name of the table at database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_proxies'
|
db_table = 'uds_proxies'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self) -> str:
|
def url(self) -> str:
|
||||||
return 'http{}://{}:{}'.format('s' if self.ssl is True else '', self.host, self.port)
|
return 'http{}://{}:{}'.format(
|
||||||
|
's' if self.ssl is True else '', self.host, self.port
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def proxyRequestUrl(self) -> str:
|
def proxyRequestUrl(self) -> str:
|
||||||
@ -74,10 +81,10 @@ class Proxy(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
def testServerUrl(self) -> str:
|
def testServerUrl(self) -> str:
|
||||||
return self.url + "/testServer"
|
return self.url + "/testServer"
|
||||||
|
|
||||||
def doProxyRequest(self, url, data: typing.Optional[typing.Any] = None, timeout: int = 5) -> requests.Response:
|
def doProxyRequest(
|
||||||
d = {
|
self, url, data: typing.Optional[typing.Any] = None, timeout: int = 5
|
||||||
'url': url
|
) -> requests.Response:
|
||||||
}
|
d = {'url': url}
|
||||||
if data is not None:
|
if data is not None:
|
||||||
d['data'] = data
|
d['data'] = data
|
||||||
|
|
||||||
@ -86,17 +93,15 @@ class Proxy(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
data=json.dumps(d),
|
data=json.dumps(d),
|
||||||
headers={'content-type': 'application/json'},
|
headers={'content-type': 'application/json'},
|
||||||
verify=self.check_cert,
|
verify=self.check_cert,
|
||||||
timeout=timeout
|
timeout=timeout,
|
||||||
)
|
)
|
||||||
|
|
||||||
def doTestServer(self, ip: str, port: typing.Union[str, int], timeout=5) -> bool:
|
def doTestServer(self, ip: str, port: typing.Union[str, int], timeout=5) -> bool:
|
||||||
try:
|
try:
|
||||||
url = self.testServerUrl + '?host={}&port={}&timeout={}'.format(ip, port, timeout)
|
url = self.testServerUrl + '?host={}&port={}&timeout={}'.format(
|
||||||
r = requests.get(
|
ip, port, timeout
|
||||||
url,
|
|
||||||
verify=self.check_cert,
|
|
||||||
timeout=timeout
|
|
||||||
)
|
)
|
||||||
|
r = requests.get(url, verify=self.check_cert, timeout=timeout)
|
||||||
if r.status_code == 302: # Proxy returns "Found" for a success test
|
if r.status_code == 302: # Proxy returns "Found" for a success test
|
||||||
return True
|
return True
|
||||||
# Else returns 404
|
# Else returns 404
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -34,7 +34,6 @@ import logging
|
|||||||
import typing
|
import typing
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import signals
|
|
||||||
|
|
||||||
from uds.core.util.state import State
|
from uds.core.util.state import State
|
||||||
from uds.core.environment import Environment
|
from uds.core.environment import Environment
|
||||||
@ -72,12 +71,16 @@ class Scheduler(models.Model):
|
|||||||
state = models.CharField(max_length=1, default=State.FOR_EXECUTE, db_index=True)
|
state = models.CharField(max_length=1, default=State.FOR_EXECUTE, db_index=True)
|
||||||
|
|
||||||
# primary key id declaration (for type checking)
|
# primary key id declaration (for type checking)
|
||||||
id: int
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Scheduler]'
|
||||||
|
id: int # Primary key (Autogenerated by model)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare default order and unique multiple field index
|
Meta class to declare default order and unique multiple field index
|
||||||
"""
|
"""
|
||||||
|
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
def getEnvironment(self) -> Environment:
|
def getEnvironment(self) -> Environment:
|
||||||
@ -99,7 +102,7 @@ class Scheduler(models.Model):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def beforeDelete(sender, **kwargs):
|
def beforeDelete(sender, **kwargs) -> None:
|
||||||
"""
|
"""
|
||||||
Used to remove environment for sheduled task
|
Used to remove environment for sheduled task
|
||||||
"""
|
"""
|
||||||
@ -107,9 +110,11 @@ class Scheduler(models.Model):
|
|||||||
logger.debug('Deleting sheduled task %s', toDelete)
|
logger.debug('Deleting sheduled task %s', toDelete)
|
||||||
toDelete.getEnvironment().clearRelatedData()
|
toDelete.getEnvironment().clearRelatedData()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return 'Scheduled task {}, every {}, last execution at {}, state = {}'.format(self.name, self.frecuency, self.last_execution, self.state)
|
return 'Scheduled task {}, every {}, last execution at {}, state = {}'.format(
|
||||||
|
self.name, self.frecuency, self.last_execution, self.state
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Connects a pre deletion signal to Scheduler
|
# Connects a pre deletion signal to Scheduler
|
||||||
signals.pre_delete.connect(Scheduler.beforeDelete, sender=Scheduler)
|
models.signals.pre_delete.connect(Scheduler.beforeDelete, sender=Scheduler)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -58,30 +58,38 @@ class Service(ManagedObjectModel, TaggingMixin): # type: ignore
|
|||||||
A Service represents an specidied type of service offered to final users, with it configuration (i.e. a KVM Base Machine for cloning
|
A Service represents an specidied type of service offered to final users, with it configuration (i.e. a KVM Base Machine for cloning
|
||||||
or a Terminal Server configuration).
|
or a Terminal Server configuration).
|
||||||
"""
|
"""
|
||||||
provider: 'models.ForeignKey[Service, Provider]' = models.ForeignKey(Provider, related_name='services', on_delete=models.CASCADE)
|
|
||||||
|
provider: 'models.ForeignKey[Service, Provider]' = models.ForeignKey(
|
||||||
|
Provider, related_name='services', on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
# Proxy for this service
|
# Proxy for this service
|
||||||
proxy: 'models.ForeignKey[Service, Proxy]' = models.ForeignKey(Proxy, null=True, blank=True, related_name='services', on_delete=models.CASCADE)
|
proxy: 'models.ForeignKey[Service, Proxy]' = models.ForeignKey(
|
||||||
|
Proxy, null=True, blank=True, related_name='services', on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
token = models.CharField(max_length=32, default=None, null=True, blank=True, unique=True)
|
token = models.CharField(
|
||||||
|
max_length=32, default=None, null=True, blank=True, unique=True
|
||||||
|
)
|
||||||
|
|
||||||
_cachedInstance: typing.Optional['services.Service'] = None
|
_cachedInstance: typing.Optional['services.Service'] = None
|
||||||
|
|
||||||
class Meta(ManagedObjectModel.Meta): # pylint: disable=too-few-public-methods
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Service]'
|
||||||
|
|
||||||
|
class Meta(ManagedObjectModel.Meta):
|
||||||
"""
|
"""
|
||||||
Meta class to declare default order and unique multiple field index
|
Meta class to declare default order and unique multiple field index
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
unique_together = (("provider", "name"),)
|
unique_together = (("provider", "name"),)
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
def getEnvironment(self):
|
def getEnvironment(self) -> Environment:
|
||||||
"""
|
"""
|
||||||
Returns an environment valid for the record this object represents
|
Returns an environment valid for the record this object represents
|
||||||
"""
|
"""
|
||||||
# from uds.core.util.unique_mac_generator import UniqueMacGenerator
|
|
||||||
# from uds.core.util.unique_name_generator import UniqueNameGenerator
|
|
||||||
|
|
||||||
return Environment.getEnvForTableElement(
|
return Environment.getEnvForTableElement(
|
||||||
self._meta.verbose_name,
|
self._meta.verbose_name,
|
||||||
self.id,
|
self.id,
|
||||||
@ -89,10 +97,10 @@ class Service(ManagedObjectModel, TaggingMixin): # type: ignore
|
|||||||
'mac': unique.UniqueMacGenerator,
|
'mac': unique.UniqueMacGenerator,
|
||||||
'name': unique.UniqueNameGenerator,
|
'name': unique.UniqueNameGenerator,
|
||||||
'id': unique.UniqueGIDGenerator,
|
'id': unique.UniqueGIDGenerator,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def getInstance(self, values=None) -> 'services.Service':
|
def getInstance(self, values: typing.Optional[typing.Dict[str, str]] = None) -> 'services.Service':
|
||||||
"""
|
"""
|
||||||
Instantiates the object this record contains.
|
Instantiates the object this record contains.
|
||||||
|
|
||||||
@ -118,7 +126,11 @@ class Service(ManagedObjectModel, TaggingMixin): # type: ignore
|
|||||||
obj = sType(self.getEnvironment(), prov, values, uuid=self.uuid)
|
obj = sType(self.getEnvironment(), prov, values, uuid=self.uuid)
|
||||||
self.deserialize(obj, values)
|
self.deserialize(obj, values)
|
||||||
else:
|
else:
|
||||||
raise Exception('Service type of {} is not recogniced by provider {}'.format(self.data_type, prov))
|
raise Exception(
|
||||||
|
'Service type of {} is not recogniced by provider {}'.format(
|
||||||
|
self.data_type, prov
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
self._cachedInstance = obj
|
self._cachedInstance = obj
|
||||||
|
|
||||||
@ -140,22 +152,28 @@ class Service(ManagedObjectModel, TaggingMixin): # type: ignore
|
|||||||
if type_:
|
if type_:
|
||||||
return type_
|
return type_
|
||||||
|
|
||||||
raise Exception('Service type of {} is not recogniced by provider {}'.format(self.data_type, prov))
|
raise Exception(
|
||||||
|
'Service type of {} is not recogniced by provider {}'.format(
|
||||||
|
self.data_type, prov
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def isInMaintenance(self) -> bool:
|
def isInMaintenance(self) -> bool:
|
||||||
# orphaned services?
|
# orphaned services?
|
||||||
return self.provider.isInMaintenance() if self.provider else True
|
return self.provider.isInMaintenance() if self.provider else True
|
||||||
|
|
||||||
def testServer(self, host: str, port: typing.Union[str, int], timeout: int = 4) -> bool:
|
def testServer(
|
||||||
if self.proxy is not None:
|
self, host: str, port: typing.Union[str, int], timeout: int = 4
|
||||||
|
) -> bool:
|
||||||
|
if self.proxy:
|
||||||
return self.proxy.doTestServer(host, port, timeout)
|
return self.proxy.doTestServer(host, port, timeout)
|
||||||
return connection.testServer(host, port, timeout)
|
return connection.testServer(host, port, timeout)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return '{} of type {} (id:{})'.format(self.name, self.data_type, self.id)
|
return '{} of type {} (id:{})'.format(self.name, self.data_type, self.id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def beforeDelete(sender, **kwargs):
|
def beforeDelete(sender, **kwargs) -> None:
|
||||||
"""
|
"""
|
||||||
Used to invoke the Service class "Destroy" before deleting it from database.
|
Used to invoke the Service class "Destroy" before deleting it from database.
|
||||||
|
|
||||||
@ -165,6 +183,7 @@ class Service(ManagedObjectModel, TaggingMixin): # type: ignore
|
|||||||
:note: If destroy raises an exception, the deletion is not taken.
|
:note: If destroy raises an exception, the deletion is not taken.
|
||||||
"""
|
"""
|
||||||
from uds.core.util.permissions import clean
|
from uds.core.util.permissions import clean
|
||||||
|
|
||||||
toDelete = kwargs['instance']
|
toDelete = kwargs['instance']
|
||||||
|
|
||||||
logger.debug('Before delete service %s', toDelete)
|
logger.debug('Before delete service %s', toDelete)
|
||||||
@ -180,5 +199,6 @@ class Service(ManagedObjectModel, TaggingMixin): # type: ignore
|
|||||||
# Clears related permissions
|
# Clears related permissions
|
||||||
clean(toDelete)
|
clean(toDelete)
|
||||||
|
|
||||||
|
|
||||||
# : Connects a pre deletion signal to Service
|
# : Connects a pre deletion signal to Service
|
||||||
models.signals.pre_delete.connect(Service.beforeDelete, sender=Service)
|
models.signals.pre_delete.connect(Service.beforeDelete, sender=Service)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -61,7 +61,15 @@ from .util import getSqlDatetime
|
|||||||
|
|
||||||
# Not imported at runtime, just for type checking
|
# Not imported at runtime, just for type checking
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from uds.models import UserService, ServicePoolPublication, User, Group, Proxy, MetaPoolMember, CalendarAccess
|
from uds.models import (
|
||||||
|
UserService,
|
||||||
|
ServicePoolPublication,
|
||||||
|
User,
|
||||||
|
Group,
|
||||||
|
Proxy,
|
||||||
|
MetaPoolMember,
|
||||||
|
CalendarAccess,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -70,14 +78,33 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
"""
|
"""
|
||||||
A deployed service is the Service produced element that is assigned finally to an user (i.e. a Virtual Machine, etc..)
|
A deployed service is the Service produced element that is assigned finally to an user (i.e. a Virtual Machine, etc..)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = models.CharField(max_length=128, default='')
|
name = models.CharField(max_length=128, default='')
|
||||||
short_name = models.CharField(max_length=32, default='')
|
short_name = models.CharField(max_length=32, default='')
|
||||||
comments = models.CharField(max_length=256, default='')
|
comments = models.CharField(max_length=256, default='')
|
||||||
service: 'models.ForeignKey[ServicePool, Service]' = models.ForeignKey(Service, null=True, blank=True, related_name='deployedServices', on_delete=models.CASCADE)
|
service: 'models.ForeignKey[ServicePool, Service]' = models.ForeignKey(
|
||||||
osmanager: 'models.ForeignKey[ServicePool, OSManager]' = models.ForeignKey(OSManager, null=True, blank=True, related_name='deployedServices', on_delete=models.CASCADE)
|
Service,
|
||||||
transports = models.ManyToManyField(Transport, related_name='deployedServices', db_table='uds__ds_trans')
|
null=True,
|
||||||
assignedGroups = models.ManyToManyField(Group, related_name='deployedServices', db_table='uds__ds_grps')
|
blank=True,
|
||||||
state = models.CharField(max_length=1, default=states.servicePool.ACTIVE, db_index=True)
|
related_name='deployedServices',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
)
|
||||||
|
osmanager: 'models.ForeignKey[ServicePool, OSManager]' = models.ForeignKey(
|
||||||
|
OSManager,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name='deployedServices',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
)
|
||||||
|
transports = models.ManyToManyField(
|
||||||
|
Transport, related_name='deployedServices', db_table='uds__ds_trans'
|
||||||
|
)
|
||||||
|
assignedGroups = models.ManyToManyField(
|
||||||
|
Group, related_name='deployedServices', db_table='uds__ds_grps'
|
||||||
|
)
|
||||||
|
state = models.CharField(
|
||||||
|
max_length=1, default=states.servicePool.ACTIVE, db_index=True
|
||||||
|
)
|
||||||
state_date = models.DateTimeField(default=NEVER)
|
state_date = models.DateTimeField(default=NEVER)
|
||||||
show_transports = models.BooleanField(default=True)
|
show_transports = models.BooleanField(default=True)
|
||||||
visible = models.BooleanField(default=True)
|
visible = models.BooleanField(default=True)
|
||||||
@ -86,18 +113,37 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
|
|
||||||
ignores_unused = models.BooleanField(default=False)
|
ignores_unused = models.BooleanField(default=False)
|
||||||
|
|
||||||
image: 'models.ForeignKey[ServicePool, Image]' = models.ForeignKey(Image, null=True, blank=True, related_name='deployedServices', on_delete=models.SET_NULL)
|
image: 'models.ForeignKey[ServicePool, Image]' = models.ForeignKey(
|
||||||
|
Image,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name='deployedServices',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
)
|
||||||
|
|
||||||
servicesPoolGroup: 'models.ForeignKey[ServicePool, ServicePoolGroup]' = models.ForeignKey(ServicePoolGroup, null=True, blank=True, related_name='servicesPools', on_delete=models.SET_NULL)
|
servicesPoolGroup: 'models.ForeignKey[ServicePool, ServicePoolGroup]' = (
|
||||||
|
models.ForeignKey(
|
||||||
|
ServicePoolGroup,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name='servicesPools',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Message if access denied
|
# Message if access denied
|
||||||
calendar_message = models.CharField(default='', max_length=256)
|
calendar_message = models.CharField(default='', max_length=256)
|
||||||
# Default fallback action for access
|
# Default fallback action for access
|
||||||
fallbackAccess = models.CharField(default=states.action.ALLOW, max_length=8)
|
fallbackAccess = models.CharField(default=states.action.ALLOW, max_length=8)
|
||||||
# actionsCalendars = models.ManyToManyField(Calendar, related_name='actionsSP', through='CalendarAction')
|
|
||||||
|
|
||||||
# Usage accounting
|
# Usage accounting
|
||||||
account: 'models.ForeignKey[ServicePool, Account]' = models.ForeignKey(Account, null=True, blank=True, related_name='servicesPools', on_delete=models.CASCADE)
|
account: 'models.ForeignKey[ServicePool, Account]' = models.ForeignKey(
|
||||||
|
Account,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name='servicesPools',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
)
|
||||||
|
|
||||||
initial_srvs = models.PositiveIntegerField(default=0)
|
initial_srvs = models.PositiveIntegerField(default=0)
|
||||||
cache_l1_srvs = models.PositiveIntegerField(default=0)
|
cache_l1_srvs = models.PositiveIntegerField(default=0)
|
||||||
@ -105,19 +151,18 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
max_srvs = models.PositiveIntegerField(default=0)
|
max_srvs = models.PositiveIntegerField(default=0)
|
||||||
current_pub_revision = models.PositiveIntegerField(default=1)
|
current_pub_revision = models.PositiveIntegerField(default=1)
|
||||||
|
|
||||||
# "fake" relations declarations for type checking
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[ServicePool]'
|
||||||
publications: 'models.QuerySet[ServicePoolPublication]'
|
publications: 'models.QuerySet[ServicePoolPublication]'
|
||||||
memberOfMeta: 'models.QuerySet[MetaPoolMember]'
|
memberOfMeta: 'models.QuerySet[MetaPoolMember]'
|
||||||
userServices: 'models.QuerySet[UserService]'
|
userServices: 'models.QuerySet[UserService]'
|
||||||
calendarAccess: 'models.QuerySet[CalendarAccess]'
|
calendarAccess: 'models.QuerySet[CalendarAccess]'
|
||||||
|
|
||||||
# Meta service related
|
|
||||||
# meta_pools = models.ManyToManyField('self', symmetrical=False)
|
|
||||||
|
|
||||||
class Meta(UUIDModel.Meta):
|
class Meta(UUIDModel.Meta):
|
||||||
"""
|
"""
|
||||||
Meta class to declare the name of the table at database
|
Meta class to declare the name of the table at database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds__deployed_service'
|
db_table = 'uds__deployed_service'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
@ -125,9 +170,6 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
"""
|
"""
|
||||||
Returns an environment valid for the record this object represents
|
Returns an environment valid for the record this object represents
|
||||||
"""
|
"""
|
||||||
a = self.image
|
|
||||||
|
|
||||||
|
|
||||||
return Environment.getEnvForTableElement(self._meta.verbose_name, self.id)
|
return Environment.getEnvForTableElement(self._meta.verbose_name, self.id)
|
||||||
|
|
||||||
def activePublication(self) -> typing.Optional['ServicePoolPublication']:
|
def activePublication(self) -> typing.Optional['ServicePoolPublication']:
|
||||||
@ -140,7 +182,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
None if there is no valid publication for this deployed service.
|
None if there is no valid publication for this deployed service.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return typing.cast(ServicePoolPublication, self.publications.filter(state=states.publication.USABLE)[0])
|
return self.publications.filter(state=states.publication.USABLE)[0]
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -161,36 +203,49 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
return username, password
|
return username, password
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getRestrainedsQuerySet() -> models.QuerySet:
|
def getRestrainedsQuerySet() -> 'models.QuerySet[ServicePool]':
|
||||||
from uds.models.user_service import UserService # pylint: disable=redefined-outer-name
|
from uds.models.user_service import (
|
||||||
|
UserService,
|
||||||
|
) # pylint: disable=redefined-outer-name
|
||||||
from uds.core.util.config import GlobalConfig
|
from uds.core.util.config import GlobalConfig
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
|
|
||||||
if GlobalConfig.RESTRAINT_TIME.getInt() <= 0:
|
if GlobalConfig.RESTRAINT_TIME.getInt() <= 0:
|
||||||
return ServicePool.objects.none() # Do not perform any restraint check if we set the globalconfig to 0 (or less)
|
return (
|
||||||
|
ServicePool.objects.none()
|
||||||
|
) # Do not perform any restraint check if we set the globalconfig to 0 (or less)
|
||||||
|
|
||||||
date = typing.cast(datetime, getSqlDatetime()) - timedelta(seconds=GlobalConfig.RESTRAINT_TIME.getInt())
|
date = getSqlDatetime() - timedelta(
|
||||||
|
seconds=GlobalConfig.RESTRAINT_TIME.getInt()
|
||||||
|
)
|
||||||
min_ = GlobalConfig.RESTRAINT_COUNT.getInt()
|
min_ = GlobalConfig.RESTRAINT_COUNT.getInt()
|
||||||
|
|
||||||
res = []
|
res = []
|
||||||
for v in UserService.objects.filter(
|
for v in (
|
||||||
|
UserService.objects.filter(
|
||||||
state=states.userService.ERROR, state_date__gt=date
|
state=states.userService.ERROR, state_date__gt=date
|
||||||
).values('deployed_service').annotate(how_many=Count('deployed_service')).order_by('deployed_service'):
|
)
|
||||||
|
.values('deployed_service')
|
||||||
|
.annotate(how_many=Count('deployed_service'))
|
||||||
|
.order_by('deployed_service')
|
||||||
|
):
|
||||||
if v['how_many'] >= min_:
|
if v['how_many'] >= min_:
|
||||||
res.append(v['deployed_service'])
|
res.append(v['deployed_service'])
|
||||||
return ServicePool.objects.filter(pk__in=res)
|
return ServicePool.objects.filter(pk__in=res)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getRestraineds() -> typing.Iterator['ServicePool']:
|
def getRestraineds() -> typing.Iterator['ServicePool']:
|
||||||
return typing.cast(typing.Iterator['ServicePool'], ServicePool.getRestrainedsQuerySet().iterator())
|
return ServicePool.getRestrainedsQuerySet().iterator()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_meta(self) -> bool:
|
def owned_by_meta(self) -> bool:
|
||||||
return self.memberOfMeta.count() > 0
|
return self.memberOfMeta.count() > 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def visual_name(self) -> str:
|
def visual_name(self) -> str:
|
||||||
logger.debug("SHORT: %s %s %s", self.short_name, self.short_name is not None, self.name)
|
logger.debug(
|
||||||
|
"SHORT: %s %s %s", self.short_name, self.short_name is not None, self.name
|
||||||
|
)
|
||||||
if self.short_name is not None and str(self.short_name).strip() != '':
|
if self.short_name is not None and str(self.short_name).strip() != '':
|
||||||
return str(self.short_name)
|
return str(self.short_name)
|
||||||
return str(self.name)
|
return str(self.name)
|
||||||
@ -214,8 +269,15 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
if GlobalConfig.RESTRAINT_TIME.getInt() <= 0:
|
if GlobalConfig.RESTRAINT_TIME.getInt() <= 0:
|
||||||
return False # Do not perform any restraint check if we set the globalconfig to 0 (or less)
|
return False # Do not perform any restraint check if we set the globalconfig to 0 (or less)
|
||||||
|
|
||||||
date = typing.cast(datetime, getSqlDatetime()) - timedelta(seconds=GlobalConfig.RESTRAINT_TIME.getInt())
|
date = typing.cast(datetime, getSqlDatetime()) - timedelta(
|
||||||
if self.userServices.filter(state=states.userService.ERROR, state_date__gt=date).count() >= GlobalConfig.RESTRAINT_COUNT.getInt():
|
seconds=GlobalConfig.RESTRAINT_TIME.getInt()
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
self.userServices.filter(
|
||||||
|
state=states.userService.ERROR, state_date__gt=date
|
||||||
|
).count()
|
||||||
|
>= GlobalConfig.RESTRAINT_COUNT.getInt()
|
||||||
|
):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -227,7 +289,11 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
return self.visible
|
return self.visible
|
||||||
|
|
||||||
def isUsable(self) -> bool:
|
def isUsable(self) -> bool:
|
||||||
return self.state == states.servicePool.ACTIVE and not self.isInMaintenance() and not self.isRestrained()
|
return (
|
||||||
|
self.state == states.servicePool.ACTIVE
|
||||||
|
and not self.isInMaintenance()
|
||||||
|
and not self.isRestrained()
|
||||||
|
)
|
||||||
|
|
||||||
def toBeReplaced(self, forUser: 'User') -> typing.Optional[datetime]:
|
def toBeReplaced(self, forUser: 'User') -> typing.Optional[datetime]:
|
||||||
activePub: typing.Optional['ServicePoolPublication'] = self.activePublication()
|
activePub: typing.Optional['ServicePoolPublication'] = self.activePublication()
|
||||||
@ -241,7 +307,12 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
|
|
||||||
# Return the date
|
# Return the date
|
||||||
try:
|
try:
|
||||||
found = typing.cast('UserService', self.assignedUserServices().filter(user=forUser, state__in=states.userService.VALID_STATES)[0])
|
found = typing.cast(
|
||||||
|
'UserService',
|
||||||
|
self.assignedUserServices().filter(
|
||||||
|
user=forUser, state__in=states.userService.VALID_STATES
|
||||||
|
)[0],
|
||||||
|
)
|
||||||
if activePub and found.publication and activePub.id != found.publication.id:
|
if activePub and found.publication and activePub.id != found.publication.id:
|
||||||
ret = self.recoverValue('toBeReplacedIn')
|
ret = self.recoverValue('toBeReplacedIn')
|
||||||
if ret:
|
if ret:
|
||||||
@ -268,7 +339,9 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
|
|
||||||
return access == states.action.ALLOW
|
return access == states.action.ALLOW
|
||||||
|
|
||||||
def getDeadline(self, chkDateTime: typing.Optional[datetime] = None) -> typing.Optional[int]:
|
def getDeadline(
|
||||||
|
self, chkDateTime: typing.Optional[datetime] = None
|
||||||
|
) -> typing.Optional[int]:
|
||||||
"""Gets the deadline for an access on chkDateTime in seconds
|
"""Gets the deadline for an access on chkDateTime in seconds
|
||||||
|
|
||||||
Keyword Arguments:
|
Keyword Arguments:
|
||||||
@ -286,7 +359,10 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
deadLine = None
|
deadLine = None
|
||||||
|
|
||||||
for ac in self.calendarAccess.all():
|
for ac in self.calendarAccess.all():
|
||||||
if ac.access == states.action.ALLOW and self.fallbackAccess == states.action.DENY:
|
if (
|
||||||
|
ac.access == states.action.ALLOW
|
||||||
|
and self.fallbackAccess == states.action.DENY
|
||||||
|
):
|
||||||
nextE = CalendarChecker(ac.calendar).nextEvent(chkDateTime, False)
|
nextE = CalendarChecker(ac.calendar).nextEvent(chkDateTime, False)
|
||||||
if deadLine is None or deadLine > nextE:
|
if deadLine is None or deadLine > nextE:
|
||||||
deadLine = nextE
|
deadLine = nextE
|
||||||
@ -350,8 +426,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
def removed(self) -> None:
|
def removed(self) -> None:
|
||||||
"""
|
"""
|
||||||
Mark the deployed service as removed.
|
Mark the deployed service as removed.
|
||||||
|
Basically, deletes the user service
|
||||||
A background worker will check for removed deloyed services and clean database of them.
|
|
||||||
"""
|
"""
|
||||||
# self.transports.clear()
|
# self.transports.clear()
|
||||||
# self.assignedGroups.clear()
|
# self.assignedGroups.clear()
|
||||||
@ -360,7 +435,11 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
# self.setState(State.REMOVED)
|
# self.setState(State.REMOVED)
|
||||||
self.delete()
|
self.delete()
|
||||||
|
|
||||||
def markOldUserServicesAsRemovables(self, activePub: typing.Optional['ServicePoolPublication'], skipAssigned: bool = False):
|
def markOldUserServicesAsRemovables(
|
||||||
|
self,
|
||||||
|
activePub: typing.Optional['ServicePoolPublication'],
|
||||||
|
skipAssigned: bool = False,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Used when a new publication is finished.
|
Used when a new publication is finished.
|
||||||
|
|
||||||
@ -379,15 +458,23 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
userService: 'UserService'
|
userService: 'UserService'
|
||||||
|
|
||||||
if activePub is None:
|
if activePub is None:
|
||||||
logger.error('No active publication, don\'t know what to erase!!! (ds = %s)', self)
|
logger.error(
|
||||||
|
'No active publication, don\'t know what to erase!!! (ds = %s)', self
|
||||||
|
)
|
||||||
return
|
return
|
||||||
for nonActivePub in self.publications.exclude(id=activePub.id):
|
for nonActivePub in self.publications.exclude(id=activePub.id):
|
||||||
for userService in nonActivePub.userServices.filter(state=states.userService.PREPARING):
|
for userService in nonActivePub.userServices.filter(
|
||||||
|
state=states.userService.PREPARING
|
||||||
|
):
|
||||||
userService.cancel()
|
userService.cancel()
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
nonActivePub.userServices.exclude(cache_level=0).filter(state=states.userService.USABLE).update(state=states.userService.REMOVABLE, state_date=now)
|
nonActivePub.userServices.exclude(cache_level=0).filter(
|
||||||
|
state=states.userService.USABLE
|
||||||
|
).update(state=states.userService.REMOVABLE, state_date=now)
|
||||||
if not skipAssigned:
|
if not skipAssigned:
|
||||||
nonActivePub.userServices.filter(cache_level=0, state=states.userService.USABLE, in_use=False).update(state=states.userService.REMOVABLE, state_date=now)
|
nonActivePub.userServices.filter(
|
||||||
|
cache_level=0, state=states.userService.USABLE, in_use=False
|
||||||
|
).update(state=states.userService.REMOVABLE, state_date=now)
|
||||||
|
|
||||||
def validateGroups(self, groups: typing.Iterable['Group']) -> None:
|
def validateGroups(self, groups: typing.Iterable['Group']) -> None:
|
||||||
"""
|
"""
|
||||||
@ -395,6 +482,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
raise an InvalidUserException if fails check
|
raise an InvalidUserException if fails check
|
||||||
"""
|
"""
|
||||||
from uds.core import auths
|
from uds.core import auths
|
||||||
|
|
||||||
if not set(groups) & set(self.assignedGroups.all()):
|
if not set(groups) & set(self.assignedGroups.all()):
|
||||||
raise auths.exceptions.InvalidUserException()
|
raise auths.exceptions.InvalidUserException()
|
||||||
|
|
||||||
@ -403,7 +491,10 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
Ensures that, if this service has publications, that a publication is active
|
Ensures that, if this service has publications, that a publication is active
|
||||||
raises an IvalidServiceException if check fails
|
raises an IvalidServiceException if check fails
|
||||||
"""
|
"""
|
||||||
if self.activePublication() is None and self.service.getType().publicationType is not None:
|
if (
|
||||||
|
self.activePublication() is None
|
||||||
|
and self.service.getType().publicationType is not None
|
||||||
|
):
|
||||||
raise InvalidServiceException()
|
raise InvalidServiceException()
|
||||||
|
|
||||||
def validateTransport(self, transport) -> None:
|
def validateTransport(self, transport) -> None:
|
||||||
@ -419,9 +510,6 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
Args:
|
Args:
|
||||||
user: User (db record) to check if has access to this deployed service
|
user: User (db record) to check if has access to this deployed service
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if has access
|
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
InvalidUserException() if user do not has access to this deployed service
|
InvalidUserException() if user do not has access to this deployed service
|
||||||
|
|
||||||
@ -436,7 +524,9 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
self.validatePublication()
|
self.validatePublication()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getDeployedServicesForGroups(groups: typing.Iterable['Group'], user: typing.Optional['User'] = None) -> typing.List['ServicePool']:
|
def getDeployedServicesForGroups(
|
||||||
|
groups: typing.Iterable['Group'], user: typing.Optional['User'] = None
|
||||||
|
) -> typing.Iterable['ServicePool']:
|
||||||
"""
|
"""
|
||||||
Return deployed services with publications for the groups requested.
|
Return deployed services with publications for the groups requested.
|
||||||
|
|
||||||
@ -447,17 +537,33 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
List of accesible deployed services
|
List of accesible deployed services
|
||||||
"""
|
"""
|
||||||
from uds.core import services
|
from uds.core import services
|
||||||
servicesNotNeedingPub = [t.type() for t in services.factory().servicesThatDoNotNeedPublication()]
|
|
||||||
|
servicesNotNeedingPub = [
|
||||||
|
t.type() for t in services.factory().servicesThatDoNotNeedPublication()
|
||||||
|
]
|
||||||
# Get services that HAS publications
|
# Get services that HAS publications
|
||||||
query = (
|
query = (
|
||||||
ServicePool.objects.filter(
|
ServicePool.objects.filter(
|
||||||
assignedGroups__in=groups,
|
assignedGroups__in=groups,
|
||||||
assignedGroups__state=states.group.ACTIVE,
|
assignedGroups__state=states.group.ACTIVE,
|
||||||
state=states.servicePool.ACTIVE,
|
state=states.servicePool.ACTIVE,
|
||||||
visible=True
|
visible=True,
|
||||||
|
)
|
||||||
|
.annotate(
|
||||||
|
pubs_active=models.Count(
|
||||||
|
'publications',
|
||||||
|
filter=models.Q(publications__state=states.publication.USABLE),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.annotate(
|
||||||
|
usage_count=models.Count(
|
||||||
|
'userServices',
|
||||||
|
filter=models.Q(
|
||||||
|
userServices__state__in=states.userService.VALID_STATES,
|
||||||
|
userServices__cache_level=0,
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.annotate(pubs_active=models.Count('publications', filter=models.Q(publications__state=states.publication.USABLE)))
|
|
||||||
.annotate(usage_count=models.Count('userServices', filter=models.Q(userServices__state__in=states.userService.VALID_STATES, userServices__cache_level=0)))
|
|
||||||
.prefetch_related(
|
.prefetch_related(
|
||||||
'transports',
|
'transports',
|
||||||
'transports__networks',
|
'transports__networks',
|
||||||
@ -471,22 +577,28 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
'calendarAccess',
|
'calendarAccess',
|
||||||
'calendarAccess__calendar',
|
'calendarAccess__calendar',
|
||||||
'calendarAccess__calendar__rules',
|
'calendarAccess__calendar__rules',
|
||||||
'image'
|
'image',
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if user: # Optimize loading if there is some assgned service..
|
if user: # Optimize loading if there is some assgned service..
|
||||||
query = query.annotate(
|
query = query.annotate(
|
||||||
number_in_use=models.Count(
|
number_in_use=models.Count(
|
||||||
'userServices', filter=models.Q(
|
'userServices',
|
||||||
|
filter=models.Q(
|
||||||
userServices__user=user,
|
userServices__user=user,
|
||||||
userServices__in_use=True,
|
userServices__in_use=True,
|
||||||
userServices__state__in=states.userService.USABLE
|
userServices__state__in=states.userService.USABLE,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
servicePool: 'ServicePool'
|
servicePool: 'ServicePool'
|
||||||
return [servicePool for servicePool in query if servicePool.pubs_active or servicePool.service.data_type in servicesNotNeedingPub]
|
for servicePool in query:
|
||||||
|
if (
|
||||||
|
typing.cast(typing.Any, servicePool).pubs_active
|
||||||
|
or servicePool.service.data_type in servicesNotNeedingPub
|
||||||
|
):
|
||||||
|
yield servicePool
|
||||||
|
|
||||||
def publish(self, changeLog: typing.Optional[str] = None) -> None:
|
def publish(self, changeLog: typing.Optional[str] = None) -> None:
|
||||||
"""
|
"""
|
||||||
@ -495,6 +607,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
No check is done, it simply redirects the request to PublicationManager, where checks are done.
|
No check is done, it simply redirects the request to PublicationManager, where checks are done.
|
||||||
"""
|
"""
|
||||||
from uds.core.managers import publicationManager
|
from uds.core.managers import publicationManager
|
||||||
|
|
||||||
publicationManager().publish(self, changeLog)
|
publicationManager().publish(self, changeLog)
|
||||||
|
|
||||||
def unpublish(self) -> None:
|
def unpublish(self) -> None:
|
||||||
@ -507,7 +620,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
if pub:
|
if pub:
|
||||||
pub.unpublish()
|
pub.unpublish()
|
||||||
|
|
||||||
def cachedUserServices(self) -> 'models.QuerySet':
|
def cachedUserServices(self) -> 'models.QuerySet[UserService]':
|
||||||
"""
|
"""
|
||||||
':rtype uds.models.user_service.UserService'
|
':rtype uds.models.user_service.UserService'
|
||||||
Utility method to access the cached user services (level 1 and 2)
|
Utility method to access the cached user services (level 1 and 2)
|
||||||
@ -517,7 +630,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
"""
|
"""
|
||||||
return self.userServices.exclude(cache_level=0)
|
return self.userServices.exclude(cache_level=0)
|
||||||
|
|
||||||
def assignedUserServices(self) -> 'models.QuerySet':
|
def assignedUserServices(self) -> 'models.QuerySet[UserService]':
|
||||||
"""
|
"""
|
||||||
Utility method to access the assigned user services
|
Utility method to access the assigned user services
|
||||||
|
|
||||||
@ -526,7 +639,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
"""
|
"""
|
||||||
return self.userServices.filter(cache_level=0)
|
return self.userServices.filter(cache_level=0)
|
||||||
|
|
||||||
def erroneousUserServices(self) -> 'models.QuerySet':
|
def erroneousUserServices(self) -> 'models.QuerySet[UserService]':
|
||||||
"""
|
"""
|
||||||
Utility method to locate invalid assigned user services.
|
Utility method to locate invalid assigned user services.
|
||||||
|
|
||||||
@ -549,7 +662,11 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
if cachedValue == -1:
|
if cachedValue == -1:
|
||||||
cachedValue = self.assignedUserServices().filter(state__in=states.userService.VALID_STATES).count()
|
cachedValue = (
|
||||||
|
self.assignedUserServices()
|
||||||
|
.filter(state__in=states.userService.VALID_STATES)
|
||||||
|
.count()
|
||||||
|
)
|
||||||
|
|
||||||
return 100 * cachedValue // maxs
|
return 100 * cachedValue // maxs
|
||||||
|
|
||||||
@ -576,6 +693,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
:note: If destroy raises an exception, the deletion is not taken.
|
:note: If destroy raises an exception, the deletion is not taken.
|
||||||
"""
|
"""
|
||||||
from uds.core.util.permissions import clean
|
from uds.core.util.permissions import clean
|
||||||
|
|
||||||
toDelete: 'ServicePool' = kwargs['instance']
|
toDelete: 'ServicePool' = kwargs['instance']
|
||||||
|
|
||||||
logger.debug('Deleting Service Pool %s', toDelete)
|
logger.debug('Deleting Service Pool %s', toDelete)
|
||||||
@ -589,7 +707,13 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Deployed service {}({}) with {} as initial, {} as L1 cache, {} as L2 cache, {} as max'.format(
|
return 'Deployed service {}({}) with {} as initial, {} as L1 cache, {} as L2 cache, {} as max'.format(
|
||||||
self.name, self.id, self.initial_srvs, self.cache_l1_srvs, self.cache_l2_srvs, self.max_srvs)
|
self.name,
|
||||||
|
self.id,
|
||||||
|
self.initial_srvs,
|
||||||
|
self.cache_l1_srvs,
|
||||||
|
self.cache_l2_srvs,
|
||||||
|
self.max_srvs,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Connects a pre deletion signal to Authenticator
|
# Connects a pre deletion signal to Authenticator
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -55,6 +55,9 @@ class ServicePoolGroup(UUIDModel):
|
|||||||
priority = models.IntegerField(default=0, db_index=True)
|
priority = models.IntegerField(default=0, db_index=True)
|
||||||
image: 'models.ForeignKey[ServicePoolGroup, Image]' = models.ForeignKey(Image, null=True, blank=True, related_name='servicesPoolsGroup', on_delete=models.SET_NULL)
|
image: 'models.ForeignKey[ServicePoolGroup, Image]' = models.ForeignKey(Image, null=True, blank=True, related_name='servicesPoolsGroup', on_delete=models.SET_NULL)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[ServicePoolGroup]'
|
||||||
|
|
||||||
class Meta(UUIDModel.Meta):
|
class Meta(UUIDModel.Meta):
|
||||||
"""
|
"""
|
||||||
Meta class to declare the name of the table at database
|
Meta class to declare the name of the table at database
|
||||||
@ -62,11 +65,11 @@ class ServicePoolGroup(UUIDModel):
|
|||||||
db_table = 'uds__pools_groups'
|
db_table = 'uds__pools_groups'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return 'Service Pool group {}({}): {}'.format(self.name, self.comments, self.image.name)
|
return 'Service Pool group {}({}): {}'.format(self.name, self.comments, self.image.name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def as_dict(self) -> typing.Dict[str, typing.Any]:
|
def as_dict(self) -> typing.MutableMapping[str, typing.Any]:
|
||||||
return {
|
return {
|
||||||
'id': self.uuid,
|
'id': self.uuid,
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
@ -81,4 +84,9 @@ class ServicePoolGroup(UUIDModel):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def default() -> 'ServicePoolGroup':
|
def default() -> 'ServicePoolGroup':
|
||||||
|
"""Returns an "default" service pool group. Used on services agroupation on visualization
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
[ServicePoolGroup]: Default ServicePoolGroup
|
||||||
|
"""
|
||||||
return ServicePoolGroup(name=_('General'), comments='', priority=-10000)
|
return ServicePoolGroup(name=_('General'), comments='', priority=-10000)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -36,6 +36,7 @@ import typing
|
|||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
from uds.core.managers import publicationManager
|
||||||
from uds.core.util.state import State
|
from uds.core.util.state import State
|
||||||
from uds.core.environment import Environment
|
from uds.core.environment import Environment
|
||||||
from uds.core.util import log
|
from uds.core.util import log
|
||||||
@ -53,26 +54,43 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class ServicePoolPublicationChangelog(models.Model):
|
class ServicePoolPublicationChangelog(models.Model):
|
||||||
publication: 'models.ForeignKey[ServicePoolPublication, ServicePool]' = models.ForeignKey(ServicePool, on_delete=models.CASCADE, related_name='changelog')
|
# This should be "servicePool"
|
||||||
|
publication: 'models.ForeignKey[ServicePoolPublicationChangelog, ServicePool]' = (
|
||||||
|
models.ForeignKey(
|
||||||
|
ServicePool, on_delete=models.CASCADE, related_name='changelog'
|
||||||
|
)
|
||||||
|
)
|
||||||
stamp = models.DateTimeField()
|
stamp = models.DateTimeField()
|
||||||
revision = models.PositiveIntegerField(default=1)
|
revision = models.PositiveIntegerField(default=1)
|
||||||
log = models.TextField(default='')
|
log = models.TextField(default='')
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[ServicePoolPublicationChangelog]'
|
||||||
|
|
||||||
class Meta(UUIDModel.Meta):
|
class Meta(UUIDModel.Meta):
|
||||||
"""
|
"""
|
||||||
Meta class to declare default order and unique multiple field index
|
Meta class to declare default order and unique multiple field index
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds__deployed_service_pub_cl'
|
db_table = 'uds__deployed_service_pub_cl'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return 'Revision log for publication {}, rev {}: {}'.format(self.publication.name, self.revision, self.log)
|
return 'Revision log for publication {}, rev {}: {}'.format(
|
||||||
|
self.publication.name, self.revision, self.log
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ServicePoolPublication(UUIDModel):
|
class ServicePoolPublication(UUIDModel):
|
||||||
"""
|
"""
|
||||||
A deployed service publication keep track of data needed by services that needs "preparation". (i.e. Virtual machine --> base machine --> children of base machines)
|
A deployed service publication keep track of data needed by services that needs "preparation". (i.e. Virtual machine --> base machine --> children of base machines)
|
||||||
"""
|
"""
|
||||||
deployed_service: 'models.ForeignKey[ServicePoolPublication, ServicePool]' = models.ForeignKey(ServicePool, on_delete=models.CASCADE, related_name='publications')
|
|
||||||
|
deployed_service: 'models.ForeignKey[ServicePoolPublication, ServicePool]' = (
|
||||||
|
models.ForeignKey(
|
||||||
|
ServicePool, on_delete=models.CASCADE, related_name='publications'
|
||||||
|
)
|
||||||
|
)
|
||||||
publish_date = models.DateTimeField(db_index=True)
|
publish_date = models.DateTimeField(db_index=True)
|
||||||
# data_type = models.CharField(max_length=128) # The data type is specified by the service itself
|
# data_type = models.CharField(max_length=128) # The data type is specified by the service itself
|
||||||
data = models.TextField(default='')
|
data = models.TextField(default='')
|
||||||
@ -87,7 +105,8 @@ class ServicePoolPublication(UUIDModel):
|
|||||||
state_date = models.DateTimeField()
|
state_date = models.DateTimeField()
|
||||||
revision = models.PositiveIntegerField(default=1)
|
revision = models.PositiveIntegerField(default=1)
|
||||||
|
|
||||||
# "fake" relations declarations for type checking
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[ServicePoolPublication]'
|
||||||
userServices: 'models.QuerySet[UserService]'
|
userServices: 'models.QuerySet[UserService]'
|
||||||
|
|
||||||
|
|
||||||
@ -95,6 +114,7 @@ class ServicePoolPublication(UUIDModel):
|
|||||||
"""
|
"""
|
||||||
Meta class to declare default order and unique multiple field index
|
Meta class to declare default order and unique multiple field index
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds__deployed_service_pub'
|
db_table = 'uds__deployed_service_pub'
|
||||||
ordering = ('publish_date',)
|
ordering = ('publish_date',)
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
@ -128,7 +148,11 @@ class ServicePoolPublication(UUIDModel):
|
|||||||
# a service that needs publication but do not have
|
# a service that needs publication but do not have
|
||||||
|
|
||||||
if serviceInstance.publicationType is None:
|
if serviceInstance.publicationType is None:
|
||||||
raise Exception('Class {} do not have defined publicationType but needs to be published!!!'.format(serviceInstance.__class__))
|
raise Exception(
|
||||||
|
'Class {} do not have defined publicationType but needs to be published!!!'.format(
|
||||||
|
serviceInstance.__class__
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
publication = serviceInstance.publicationType(
|
publication = serviceInstance.publicationType(
|
||||||
self.getEnvironment(),
|
self.getEnvironment(),
|
||||||
@ -136,7 +160,8 @@ class ServicePoolPublication(UUIDModel):
|
|||||||
osManager=osManagerInstance,
|
osManager=osManagerInstance,
|
||||||
revision=self.revision,
|
revision=self.revision,
|
||||||
dsName=self.deployed_service.name,
|
dsName=self.deployed_service.name,
|
||||||
uuid=self.uuid, dbPublication=self
|
uuid=self.uuid,
|
||||||
|
dbPublication=self,
|
||||||
)
|
)
|
||||||
# Only invokes deserialization if data has something. '' is nothing
|
# Only invokes deserialization if data has something. '' is nothing
|
||||||
if self.data:
|
if self.data:
|
||||||
@ -175,14 +200,14 @@ class ServicePoolPublication(UUIDModel):
|
|||||||
|
|
||||||
No check is done, it simply redirects the request to PublicationManager, where checks are done.
|
No check is done, it simply redirects the request to PublicationManager, where checks are done.
|
||||||
"""
|
"""
|
||||||
from uds.core.managers import publicationManager
|
|
||||||
publicationManager().unpublish(self)
|
publicationManager().unpublish(self)
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
"""
|
"""
|
||||||
Invoques the cancelation of this publication
|
Invoques the cancelation of this publication
|
||||||
"""
|
"""
|
||||||
from uds.core.managers import publicationManager
|
|
||||||
publicationManager().cancel(self)
|
publicationManager().cancel(self)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -207,8 +232,13 @@ class ServicePoolPublication(UUIDModel):
|
|||||||
|
|
||||||
logger.debug('Deleted publication %s', toDelete)
|
logger.debug('Deleted publication %s', toDelete)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return 'Publication {}, rev {}, state {}'.format(self.deployed_service.name, self.revision, State.toString(self.state))
|
return 'Publication {}, rev {}, state {}'.format(
|
||||||
|
self.deployed_service.name, self.revision, State.toString(self.state)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Connects a pre deletion signal to Authenticator
|
# Connects a pre deletion signal to Authenticator
|
||||||
models.signals.pre_delete.connect(ServicePoolPublication.beforeDelete, sender=ServicePoolPublication)
|
models.signals.pre_delete.connect(
|
||||||
|
ServicePoolPublication.beforeDelete, sender=ServicePoolPublication
|
||||||
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -46,7 +46,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class StatsCounters(models.Model):
|
class StatsCounters(models.Model):
|
||||||
"""
|
"""
|
||||||
Counter statistocs mpdes the counter statistics
|
Statistics about counters (number of users at a given time, number of services at a time, whatever...)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
owner_id = models.IntegerField(db_index=True, default=0)
|
owner_id = models.IntegerField(db_index=True, default=0)
|
||||||
@ -55,15 +55,21 @@ class StatsCounters(models.Model):
|
|||||||
stamp = models.IntegerField(db_index=True, default=0)
|
stamp = models.IntegerField(db_index=True, default=0)
|
||||||
value = models.IntegerField(db_index=True, default=0)
|
value = models.IntegerField(db_index=True, default=0)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[StatsCounters]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare db table
|
Meta class to declare db table
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_stats_c'
|
db_table = 'uds_stats_c'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_grouped(owner_type: typing.Union[int, typing.Iterable[int]], counter_type: int, **kwargs) -> 'models.QuerySet[StatsCounters]': # pylint: disable=too-many-locals
|
def get_grouped(
|
||||||
|
owner_type: typing.Union[int, typing.Iterable[int]], counter_type: int, **kwargs
|
||||||
|
) -> 'models.QuerySet[StatsCounters]': # pylint: disable=too-many-locals
|
||||||
"""
|
"""
|
||||||
Returns the average stats grouped by interval for owner_type and owner_id (optional)
|
Returns the average stats grouped by interval for owner_type and owner_id (optional)
|
||||||
|
|
||||||
@ -92,7 +98,9 @@ class StatsCounters(models.Model):
|
|||||||
since = int(since) if since else NEVER_UNIX
|
since = int(since) if since else NEVER_UNIX
|
||||||
to = int(to) if to else getSqlDatetimeAsUnix()
|
to = int(to) if to else getSqlDatetimeAsUnix()
|
||||||
|
|
||||||
interval = int(kwargs.get('interval') or '600') # By default, group items in ten minutes interval (600 seconds)
|
interval = int(
|
||||||
|
kwargs.get('interval') or '600'
|
||||||
|
) # By default, group items in ten minutes interval (600 seconds)
|
||||||
|
|
||||||
max_intervals = kwargs.get('max_intervals')
|
max_intervals = kwargs.get('max_intervals')
|
||||||
|
|
||||||
@ -106,9 +114,13 @@ class StatsCounters(models.Model):
|
|||||||
q = StatsCounters.objects.filter(stamp__gte=since, stamp__lte=to)
|
q = StatsCounters.objects.filter(stamp__gte=since, stamp__lte=to)
|
||||||
else:
|
else:
|
||||||
if isinstance(owner_id, (list, tuple, types.GeneratorType)):
|
if isinstance(owner_id, (list, tuple, types.GeneratorType)):
|
||||||
q = StatsCounters.objects.filter(owner_id__in=owner_id, stamp__gte=since, stamp__lte=to)
|
q = StatsCounters.objects.filter(
|
||||||
|
owner_id__in=owner_id, stamp__gte=since, stamp__lte=to
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
q = StatsCounters.objects.filter(owner_id=owner_id, stamp__gte=since, stamp__lte=to)
|
q = StatsCounters.objects.filter(
|
||||||
|
owner_id=owner_id, stamp__gte=since, stamp__lte=to
|
||||||
|
)
|
||||||
|
|
||||||
if isinstance(owner_type, (list, tuple, types.GeneratorType)):
|
if isinstance(owner_type, (list, tuple, types.GeneratorType)):
|
||||||
q = q.filter(owner_type__in=owner_type)
|
q = q.filter(owner_type__in=owner_type)
|
||||||
@ -120,11 +132,11 @@ class StatsCounters(models.Model):
|
|||||||
last = q.order_by('stamp').reverse()[0].stamp
|
last = q.order_by('stamp').reverse()[0].stamp
|
||||||
interval = int((last - first) / (max_intervals - 1))
|
interval = int((last - first) / (max_intervals - 1))
|
||||||
|
|
||||||
stampValue = '{ceil}(stamp/{interval})'.format(ceil=getSqlFnc('CEIL'), interval=interval)
|
stampValue = '{ceil}(stamp/{interval})'.format(
|
||||||
|
ceil=getSqlFnc('CEIL'), interval=interval
|
||||||
|
)
|
||||||
filt += ' AND stamp>={since} AND stamp<={to} GROUP BY {stampValue} ORDER BY stamp'.format(
|
filt += ' AND stamp>={since} AND stamp<={to} GROUP BY {stampValue} ORDER BY stamp'.format(
|
||||||
since=since,
|
since=since, to=to, stampValue=stampValue
|
||||||
to=to,
|
|
||||||
stampValue=stampValue
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if limit:
|
if limit:
|
||||||
@ -138,15 +150,22 @@ class StatsCounters(models.Model):
|
|||||||
# fnc = getSqlFnc('MAX' if kwargs.get('use_max', False) else 'AVG')
|
# fnc = getSqlFnc('MAX' if kwargs.get('use_max', False) else 'AVG')
|
||||||
|
|
||||||
query = (
|
query = (
|
||||||
'SELECT -1 as id,-1 as owner_id,-1 as owner_type,-1 as counter_type, ' + stampValue + '*{}'.format(interval) + ' AS stamp, ' +
|
'SELECT -1 as id,-1 as owner_id,-1 as owner_type,-1 as counter_type, '
|
||||||
'{} AS value '
|
+ stampValue
|
||||||
|
+ '*{}'.format(interval)
|
||||||
|
+ ' AS stamp, '
|
||||||
|
+ '{} AS value '
|
||||||
'FROM {} WHERE {}'
|
'FROM {} WHERE {}'
|
||||||
).format(fnc, StatsCounters._meta.db_table, filt)
|
).format(fnc, StatsCounters._meta.db_table, filt)
|
||||||
|
|
||||||
logger.debug('Stats query: %s', query)
|
logger.debug('Stats query: %s', query)
|
||||||
|
|
||||||
# We use result as an iterator
|
# We use result as an iterator
|
||||||
return typing.cast('models.QuerySet[StatsCounters]', StatsCounters.objects.raw(query))
|
return typing.cast(
|
||||||
|
'models.QuerySet[StatsCounters]', StatsCounters.objects.raw(query)
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return u"Counter of {}({}): {} - {} - {}".format(self.owner_type, self.owner_id, self.stamp, self.counter_type, self.value)
|
return u"Counter of {}({}): {} - {} - {}".format(
|
||||||
|
self.owner_type, self.owner_id, self.stamp, self.counter_type, self.value
|
||||||
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -45,7 +45,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class StatsEvents(models.Model):
|
class StatsEvents(models.Model):
|
||||||
"""
|
"""
|
||||||
Counter statistocs mpdes the counter statistics
|
Statistics about events (login, logout, whatever...)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
owner_id = models.IntegerField(db_index=True, default=0)
|
owner_id = models.IntegerField(db_index=True, default=0)
|
||||||
@ -59,15 +59,23 @@ class StatsEvents(models.Model):
|
|||||||
fld3 = models.CharField(max_length=128, default='')
|
fld3 = models.CharField(max_length=128, default='')
|
||||||
fld4 = models.CharField(max_length=128, default='')
|
fld4 = models.CharField(max_length=128, default='')
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[StatsEvents]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare db table
|
Meta class to declare db table
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_stats_e'
|
db_table = 'uds_stats_e'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_stats(owner_type: typing.Union[int, typing.Iterable[int]], event_type: typing.Union[int, typing.Iterable[int]], **kwargs) -> 'models.QuerySet[StatsEvents]':
|
def get_stats(
|
||||||
|
owner_type: typing.Union[int, typing.Iterable[int]],
|
||||||
|
event_type: typing.Union[int, typing.Iterable[int]],
|
||||||
|
**kwargs
|
||||||
|
) -> 'models.QuerySet[StatsEvents]':
|
||||||
"""
|
"""
|
||||||
Returns a queryset with the average stats grouped by interval for owner_type and owner_id (optional)
|
Returns a queryset with the average stats grouped by interval for owner_type and owner_id (optional)
|
||||||
|
|
||||||
@ -119,4 +127,12 @@ class StatsEvents(models.Model):
|
|||||||
return self.fld4
|
return self.fld4
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Log of {}({}): {} - {} - {}, {}, {}'.format(self.owner_type, self.owner_id, self.event_type, self.stamp, self.fld1, self.fld2, self.fld3)
|
return 'Log of {}({}): {} - {} - {}, {}, {}'.format(
|
||||||
|
self.owner_type,
|
||||||
|
self.owner_id,
|
||||||
|
self.event_type,
|
||||||
|
self.stamp,
|
||||||
|
self.fld1,
|
||||||
|
self.fld2,
|
||||||
|
self.fld3,
|
||||||
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -47,7 +47,8 @@ class Storage(models.Model):
|
|||||||
data = models.TextField(default='')
|
data = models.TextField(default='')
|
||||||
attr1 = models.CharField(max_length=64, db_index=True, null=True, blank=True, default=None)
|
attr1 = models.CharField(max_length=64, db_index=True, null=True, blank=True, default=None)
|
||||||
|
|
||||||
# Removed old locking manager, that blocked tables
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Storage]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
@ -55,5 +56,5 @@ class Storage(models.Model):
|
|||||||
"""
|
"""
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return '{} {} > str= {}, {}'.format(self.owner, self.key, self.data, '/'.join([self.attr1]))
|
return '{} {} > str= {}, {}'.format(self.owner, self.key, self.data, '/'.join([self.attr1]))
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -49,10 +49,14 @@ class Tag(UUIDModel):
|
|||||||
|
|
||||||
tag = models.CharField(max_length=32, db_index=True, unique=True)
|
tag = models.CharField(max_length=32, db_index=True, unique=True)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Tag]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare db table
|
Meta class to declare db table
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_tag'
|
db_table = 'uds_tag'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -50,16 +50,24 @@ class TicketStore(UUIDModel):
|
|||||||
"""
|
"""
|
||||||
Tickets storing on DB
|
Tickets storing on DB
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DEFAULT_VALIDITY = 60
|
DEFAULT_VALIDITY = 60
|
||||||
MAX_VALIDITY = 60 * 60 * 12
|
MAX_VALIDITY = 60 * 60 * 12
|
||||||
# Cleanup will purge all elements that have been created MAX_VALIDITY ago
|
# Cleanup will purge all elements that have been created MAX_VALIDITY ago
|
||||||
|
|
||||||
owner = models.CharField(null=True, blank=True, default=None, max_length=8)
|
owner = models.CharField(null=True, blank=True, default=None, max_length=8)
|
||||||
stamp = models.DateTimeField() # Date creation or validation of this entry
|
stamp = models.DateTimeField() # Date creation or validation of this entry
|
||||||
validity = models.IntegerField(default=60) # Duration allowed for this ticket to be valid, in seconds
|
validity = models.IntegerField(
|
||||||
|
default=60
|
||||||
|
) # Duration allowed for this ticket to be valid, in seconds
|
||||||
|
|
||||||
data = models.BinaryField() # Associated ticket data
|
data = models.BinaryField() # Associated ticket data
|
||||||
validator = models.BinaryField(null=True, blank=True, default=None) # Associated validator for this ticket
|
validator = models.BinaryField(
|
||||||
|
null=True, blank=True, default=None
|
||||||
|
) # Associated validator for this ticket
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[TicketStore]'
|
||||||
|
|
||||||
class InvalidTicket(Exception):
|
class InvalidTicket(Exception):
|
||||||
pass
|
pass
|
||||||
@ -68,6 +76,7 @@ class TicketStore(UUIDModel):
|
|||||||
"""
|
"""
|
||||||
Meta class to declare the name of the table at database
|
Meta class to declare the name of the table at database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds_tickets'
|
db_table = 'uds_tickets'
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
@ -84,7 +93,7 @@ class TicketStore(UUIDModel):
|
|||||||
validatorFnc: typing.Optional[ValidatorType] = None,
|
validatorFnc: typing.Optional[ValidatorType] = None,
|
||||||
validity: int = DEFAULT_VALIDITY,
|
validity: int = DEFAULT_VALIDITY,
|
||||||
owner: typing.Optional[str] = None,
|
owner: typing.Optional[str] = None,
|
||||||
secure: bool = False
|
secure: bool = False,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
validity is in seconds
|
validity is in seconds
|
||||||
@ -94,7 +103,13 @@ class TicketStore(UUIDModel):
|
|||||||
if secure:
|
if secure:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return TicketStore.objects.create(stamp=getSqlDatetime(), data=data, validator=validator, validity=validity, owner=owner).uuid
|
return TicketStore.objects.create(
|
||||||
|
stamp=getSqlDatetime(),
|
||||||
|
data=data,
|
||||||
|
validator=validator,
|
||||||
|
validity=validity,
|
||||||
|
owner=owner,
|
||||||
|
).uuid
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def store(
|
def store(
|
||||||
@ -103,7 +118,7 @@ class TicketStore(UUIDModel):
|
|||||||
validatorFnc: typing.Optional[ValidatorType] = None,
|
validatorFnc: typing.Optional[ValidatorType] = None,
|
||||||
validity: int = DEFAULT_VALIDITY,
|
validity: int = DEFAULT_VALIDITY,
|
||||||
owner: typing.Optional[str] = None,
|
owner: typing.Optional[str] = None,
|
||||||
secure: bool = False
|
secure: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Stores an ticketstore. If one with this uuid already exists, replaces it. Else, creates a new one
|
Stores an ticketstore. If one with this uuid already exists, replaces it. Else, creates a new one
|
||||||
@ -122,14 +137,20 @@ class TicketStore(UUIDModel):
|
|||||||
t.owner = owner
|
t.owner = owner
|
||||||
t.save()
|
t.save()
|
||||||
except TicketStore.DoesNotExist:
|
except TicketStore.DoesNotExist:
|
||||||
TicketStore.objects.create(uuid=uuid, stamp=getSqlDatetime(), data=pickle.dumps(data), validator=validator, validity=validity)
|
TicketStore.objects.create(
|
||||||
|
uuid=uuid,
|
||||||
|
stamp=getSqlDatetime(),
|
||||||
|
data=pickle.dumps(data),
|
||||||
|
validator=validator,
|
||||||
|
validity=validity,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(
|
def get(
|
||||||
uuid: str,
|
uuid: str,
|
||||||
invalidate: bool = True,
|
invalidate: bool = True,
|
||||||
owner: typing.Optional[str] = None,
|
owner: typing.Optional[str] = None,
|
||||||
secure: bool = False
|
secure: bool = False,
|
||||||
) -> typing.Any:
|
) -> typing.Any:
|
||||||
try:
|
try:
|
||||||
t = TicketStore.objects.get(uuid=uuid, owner=owner)
|
t = TicketStore.objects.get(uuid=uuid, owner=owner)
|
||||||
@ -159,30 +180,43 @@ class TicketStore(UUIDModel):
|
|||||||
raise TicketStore.InvalidTicket('Does not exists')
|
raise TicketStore.InvalidTicket('Does not exists')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def revalidate(uuid, validity=None, owner=None):
|
def revalidate(
|
||||||
|
uuid: str,
|
||||||
|
validity: typing.Optional[int] = None,
|
||||||
|
owner: typing.Optional[str] = None,
|
||||||
|
):
|
||||||
try:
|
try:
|
||||||
t = TicketStore.objects.get(uuid=uuid, owner=owner)
|
t = TicketStore.objects.get(uuid=uuid, owner=owner)
|
||||||
t.stamp = getSqlDatetime()
|
t.stamp = getSqlDatetime()
|
||||||
if validity is not None:
|
if validity:
|
||||||
t.validity = validity
|
t.validity = validity
|
||||||
t.save()
|
t.save(update_fields=['validity', 'stamp'])
|
||||||
except TicketStore.DoesNotExist:
|
except TicketStore.DoesNotExist:
|
||||||
raise Exception('Does not exists')
|
raise Exception('Does not exists')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def cleanup():
|
def cleanup() -> None:
|
||||||
from datetime import timedelta
|
|
||||||
now = getSqlDatetime()
|
now = getSqlDatetime()
|
||||||
for v in TicketStore.objects.all():
|
for v in TicketStore.objects.all():
|
||||||
if now > v.stamp + timedelta(seconds=v.validity+600): # Delete only really old tickets. Avoid "revalidate" issues
|
if now > v.stamp + datetime.timedelta(
|
||||||
|
seconds=v.validity + 600
|
||||||
|
): # Delete only really old tickets. Avoid "revalidate" issues
|
||||||
v.delete()
|
v.delete()
|
||||||
cleanSince = now - datetime.timedelta(seconds=TicketStore.MAX_VALIDITY)
|
cleanSince = now - datetime.timedelta(seconds=TicketStore.MAX_VALIDITY)
|
||||||
|
# Also remove too long tickets (12 hours is the default)
|
||||||
TicketStore.objects.filter(stamp__lt=cleanSince).delete()
|
TicketStore.objects.filter(stamp__lt=cleanSince).delete()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
if self.validator is not None:
|
if self.validator:
|
||||||
validator = pickle.loads(self.validator)
|
validator = pickle.loads(self.validator)
|
||||||
else:
|
else:
|
||||||
validator = None
|
validator = None
|
||||||
|
|
||||||
return 'Ticket id: {}, Secure: {}, Stamp: {}, Validity: {}, Validator: {}, Data: {}'.format(self.uuid, self.owner, self.stamp, self.validity, validator, pickle.loads(self.data))
|
return 'Ticket id: {}, Secure: {}, Stamp: {}, Validity: {}, Validator: {}, Data: {}'.format(
|
||||||
|
self.uuid,
|
||||||
|
self.owner,
|
||||||
|
self.stamp,
|
||||||
|
self.validity,
|
||||||
|
validator,
|
||||||
|
pickle.loads(self.data),
|
||||||
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -65,6 +65,8 @@ class Transport(ManagedObjectModel, TaggingMixin):
|
|||||||
# "fake" relations declarations for type checking
|
# "fake" relations declarations for type checking
|
||||||
networks: 'models.QuerySet[Network]'
|
networks: 'models.QuerySet[Network]'
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[Transport]'
|
||||||
|
|
||||||
class Meta(ManagedObjectModel.Meta):
|
class Meta(ManagedObjectModel.Meta):
|
||||||
"""
|
"""
|
||||||
@ -132,7 +134,7 @@ class Transport(ManagedObjectModel, TaggingMixin):
|
|||||||
return '{} of type {} (id:{})'.format(self.name, self.data_type, self.id)
|
return '{} of type {} (id:{})'.format(self.name, self.data_type, self.id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def beforeDelete(sender, **kwargs):
|
def beforeDelete(sender, **kwargs) -> None:
|
||||||
"""
|
"""
|
||||||
Used to invoke the Service class "Destroy" before deleting it from database.
|
Used to invoke the Service class "Destroy" before deleting it from database.
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -42,19 +42,26 @@ class UniqueId(models.Model):
|
|||||||
Unique ID Database. Used to store unique names, unique macs, etc...
|
Unique ID Database. Used to store unique names, unique macs, etc...
|
||||||
Managed via uds.core.util.unique_id_generator.unique_id_generator
|
Managed via uds.core.util.unique_id_generator.unique_id_generator
|
||||||
"""
|
"""
|
||||||
|
|
||||||
owner = models.CharField(max_length=128, db_index=True, default='')
|
owner = models.CharField(max_length=128, db_index=True, default='')
|
||||||
basename = models.CharField(max_length=32, db_index=True)
|
basename = models.CharField(max_length=32, db_index=True)
|
||||||
seq = models.BigIntegerField(db_index=True)
|
seq = models.BigIntegerField(db_index=True)
|
||||||
assigned = models.BooleanField(db_index=True, default=True)
|
assigned = models.BooleanField(db_index=True, default=True)
|
||||||
stamp = models.IntegerField(db_index=True, default=0)
|
stamp = models.IntegerField(db_index=True, default=0)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[UniqueId]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare default order and unique multiple field index
|
Meta class to declare default order and unique multiple field index
|
||||||
"""
|
"""
|
||||||
|
|
||||||
unique_together = (('basename', 'seq'),)
|
unique_together = (('basename', 'seq'),)
|
||||||
ordering = ('-seq',)
|
ordering = ('-seq',)
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return u"{0} {1}.{2}, assigned is {3}".format(self.owner, self.basename, self.seq, self.assigned)
|
return u"{0} {1}.{2}, assigned is {3}".format(
|
||||||
|
self.owner, self.basename, self.seq, self.assigned
|
||||||
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -70,7 +70,8 @@ class User(UUIDModel):
|
|||||||
parent = models.CharField(max_length=50, default=None, null=True)
|
parent = models.CharField(max_length=50, default=None, null=True)
|
||||||
created = models.DateTimeField(default=getSqlDatetime, blank=True)
|
created = models.DateTimeField(default=getSqlDatetime, blank=True)
|
||||||
|
|
||||||
# "fake" relations declarations for type checking
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[User]'
|
||||||
groups: 'models.QuerySet[Group]'
|
groups: 'models.QuerySet[Group]'
|
||||||
|
|
||||||
class Meta(UUIDModel.Meta):
|
class Meta(UUIDModel.Meta):
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -49,6 +49,9 @@ class UserPreference(models.Model):
|
|||||||
value = models.CharField(max_length=128, db_index=True)
|
value = models.CharField(max_length=128, db_index=True)
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='preferences')
|
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='preferences')
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[UserPreference]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -52,7 +52,13 @@ from .util import getSqlDatetime
|
|||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from uds.core import osmanagers
|
from uds.core import osmanagers
|
||||||
from uds.core import services
|
from uds.core import services
|
||||||
from uds.models import OSManager, ServicePool, ServicePoolPublication, UserServiceProperty
|
from uds.models import (
|
||||||
|
OSManager,
|
||||||
|
ServicePool,
|
||||||
|
ServicePoolPublication,
|
||||||
|
UserServiceProperty,
|
||||||
|
AccountUsage,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -65,44 +71,68 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
|||||||
|
|
||||||
# The reference to deployed service is used to accelerate the queries for different methods, in fact its redundant cause we can access to the deployed service
|
# The reference to deployed service is used to accelerate the queries for different methods, in fact its redundant cause we can access to the deployed service
|
||||||
# through publication, but queries are much more simple
|
# through publication, but queries are much more simple
|
||||||
deployed_service: 'models.ForeignKey[UserService, ServicePool]' = models.ForeignKey(ServicePool, on_delete=models.CASCADE, related_name='userServices')
|
deployed_service: 'models.ForeignKey[UserService, ServicePool]' = models.ForeignKey(
|
||||||
publication: 'models.ForeignKey[UserService, ServicePoolPublication]' = models.ForeignKey(
|
ServicePool, on_delete=models.CASCADE, related_name='userServices'
|
||||||
ServicePoolPublication, on_delete=models.CASCADE, null=True, blank=True, related_name='userServices')
|
)
|
||||||
|
publication: 'models.ForeignKey[UserService, ServicePoolPublication]' = (
|
||||||
|
models.ForeignKey(
|
||||||
|
ServicePoolPublication,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name='userServices',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
unique_id = models.CharField(max_length=128, default='', db_index=True) # User by agents to locate machine
|
unique_id = models.CharField(
|
||||||
|
max_length=128, default='', db_index=True
|
||||||
|
) # User by agents to locate machine
|
||||||
friendly_name = models.CharField(max_length=128, default='')
|
friendly_name = models.CharField(max_length=128, default='')
|
||||||
# We need to keep separated two differents os states so service operations (move beween caches, recover service) do not affects os manager state
|
# We need to keep separated two differents os states so service operations (move beween caches, recover service) do not affects os manager state
|
||||||
state = models.CharField(max_length=1, default=State.PREPARING, db_index=True) # We set index so filters at cache level executes faster
|
state = models.CharField(
|
||||||
os_state = models.CharField(max_length=1, default=State.PREPARING) # The valid values for this field are PREPARE and USABLE
|
max_length=1, default=State.PREPARING, db_index=True
|
||||||
|
) # We set index so filters at cache level executes faster
|
||||||
|
os_state = models.CharField(
|
||||||
|
max_length=1, default=State.PREPARING
|
||||||
|
) # The valid values for this field are PREPARE and USABLE
|
||||||
state_date = models.DateTimeField(db_index=True)
|
state_date = models.DateTimeField(db_index=True)
|
||||||
creation_date = models.DateTimeField(db_index=True)
|
creation_date = models.DateTimeField(db_index=True)
|
||||||
data = models.TextField(default='')
|
data = models.TextField(default='')
|
||||||
user: 'models.ForeignKey[UserService, User]' = models.ForeignKey(
|
user: 'models.ForeignKey[UserService, User]' = models.ForeignKey(
|
||||||
User, on_delete=models.CASCADE, related_name='userServices', null=True, blank=True, default=None)
|
User,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='userServices',
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
in_use = models.BooleanField(default=False)
|
in_use = models.BooleanField(default=False)
|
||||||
in_use_date = models.DateTimeField(default=NEVER)
|
in_use_date = models.DateTimeField(default=NEVER)
|
||||||
cache_level = models.PositiveSmallIntegerField(db_index=True, default=0) # Cache level must be 1 for L1 or 2 for L2, 0 if it is not cached service
|
cache_level = models.PositiveSmallIntegerField(
|
||||||
|
db_index=True, default=0
|
||||||
|
) # Cache level must be 1 for L1 or 2 for L2, 0 if it is not cached service
|
||||||
|
|
||||||
src_hostname = models.CharField(max_length=64, default='')
|
src_hostname = models.CharField(max_length=64, default='')
|
||||||
src_ip = models.CharField(max_length=15, default='')
|
src_ip = models.CharField(max_length=15, default='')
|
||||||
|
|
||||||
cluster_node = models.CharField(max_length=128, default=None, blank=True, null=True, db_index=True)
|
cluster_node = models.CharField(
|
||||||
|
max_length=128, default=None, blank=True, null=True, db_index=True
|
||||||
|
)
|
||||||
|
|
||||||
# "fake" relations declarations for type checking
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[UserService]'
|
||||||
properties: 'models.QuerySet[UserServiceProperty]'
|
properties: 'models.QuerySet[UserServiceProperty]'
|
||||||
|
accounting: 'AccountUsage'
|
||||||
|
|
||||||
class Meta(UUIDModel.Meta):
|
class Meta(UUIDModel.Meta):
|
||||||
"""
|
"""
|
||||||
Meta class to declare default order and unique multiple field index
|
Meta class to declare default order and unique multiple field index
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds__user_service'
|
db_table = 'uds__user_service'
|
||||||
ordering = ('creation_date',)
|
ordering = ('creation_date',)
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
index_together = (
|
index_together = ('deployed_service', 'cache_level', 'state')
|
||||||
'deployed_service',
|
|
||||||
'cache_level',
|
|
||||||
'state'
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
@ -130,7 +160,7 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
|||||||
'mac': unique.UniqueMacGenerator,
|
'mac': unique.UniqueMacGenerator,
|
||||||
'name': unique.UniqueNameGenerator,
|
'name': unique.UniqueNameGenerator,
|
||||||
'id': unique.UniqueGIDGenerator,
|
'id': unique.UniqueGIDGenerator,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def getInstance(self) -> 'services.UserDeployment':
|
def getInstance(self) -> 'services.UserDeployment':
|
||||||
@ -164,16 +194,33 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
|||||||
except Exception:
|
except Exception:
|
||||||
# The publication to witch this item points to, does not exists
|
# The publication to witch this item points to, does not exists
|
||||||
self.publication = None
|
self.publication = None
|
||||||
logger.exception('Got exception at getInstance of an userService %s (seems that publication does not exists!)', self)
|
logger.exception(
|
||||||
|
'Got exception at getInstance of an userService %s (seems that publication does not exists!)',
|
||||||
|
self,
|
||||||
|
)
|
||||||
if serviceInstance.deployedType is None:
|
if serviceInstance.deployedType is None:
|
||||||
raise Exception('Class {0} needs deployedType but it is not defined!!!'.format(serviceInstance.__class__.__name__))
|
raise Exception(
|
||||||
us = serviceInstance.deployedType(self.getEnvironment(), service=serviceInstance,
|
'Class {0} needs deployedType but it is not defined!!!'.format(
|
||||||
publication=publicationInstance, osmanager=osmanagerInstance, dbservice=self)
|
serviceInstance.__class__.__name__
|
||||||
|
)
|
||||||
|
)
|
||||||
|
us = serviceInstance.deployedType(
|
||||||
|
self.getEnvironment(),
|
||||||
|
service=serviceInstance,
|
||||||
|
publication=publicationInstance,
|
||||||
|
osmanager=osmanagerInstance,
|
||||||
|
dbservice=self,
|
||||||
|
)
|
||||||
if self.data != '' and self.data is not None:
|
if self.data != '' and self.data is not None:
|
||||||
try:
|
try:
|
||||||
us.unserialize(self.data)
|
us.unserialize(self.data)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception('Error unserializing %s//%s : %s', self.deployed_service.name, self.uuid, self.data)
|
logger.exception(
|
||||||
|
'Error unserializing %s//%s : %s',
|
||||||
|
self.deployed_service.name,
|
||||||
|
self.uuid,
|
||||||
|
self.data,
|
||||||
|
)
|
||||||
return us
|
return us
|
||||||
|
|
||||||
def updateData(self, userServiceInstance: 'services.UserDeployment'):
|
def updateData(self, userServiceInstance: 'services.UserDeployment'):
|
||||||
@ -285,7 +332,9 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
|||||||
"""
|
"""
|
||||||
return self.deployed_service.transformsUserOrPasswordForService()
|
return self.deployed_service.transformsUserOrPasswordForService()
|
||||||
|
|
||||||
def processUserPassword(self, username: str, password: str) -> typing.Tuple[str, str]:
|
def processUserPassword(
|
||||||
|
self, username: str, password: str
|
||||||
|
) -> typing.Tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
Before accessing a service by a transport, we can request
|
Before accessing a service by a transport, we can request
|
||||||
the service to "transform" the username & password that the transport
|
the service to "transform" the username & password that the transport
|
||||||
@ -309,7 +358,9 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
|||||||
if serviceInstance.needsManager is False or not servicePool.osmanager:
|
if serviceInstance.needsManager is False or not servicePool.osmanager:
|
||||||
return (username, password)
|
return (username, password)
|
||||||
|
|
||||||
return servicePool.osmanager.getInstance().processUserPassword(self, username, password)
|
return servicePool.osmanager.getInstance().processUserPassword(
|
||||||
|
self, username, password
|
||||||
|
)
|
||||||
|
|
||||||
def setState(self, state: str) -> None:
|
def setState(self, state: str) -> None:
|
||||||
"""
|
"""
|
||||||
@ -363,6 +414,7 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
|||||||
:note: If the state is Fase (set to not in use), a check for removal of this deployed service is launched.
|
:note: If the state is Fase (set to not in use), a check for removal of this deployed service is launched.
|
||||||
"""
|
"""
|
||||||
from uds.core.managers import userServiceManager
|
from uds.core.managers import userServiceManager
|
||||||
|
|
||||||
self.in_use = inUse
|
self.in_use = inUse
|
||||||
self.in_use_date = getSqlDatetime()
|
self.in_use_date = getSqlDatetime()
|
||||||
self.save(update_fields=['in_use', 'in_use_date'])
|
self.save(update_fields=['in_use', 'in_use_date'])
|
||||||
@ -391,7 +443,10 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
|||||||
# 1.- If do not have any accounter associated, do nothing
|
# 1.- If do not have any accounter associated, do nothing
|
||||||
# 2.- If called but not accounting, do nothing
|
# 2.- If called but not accounting, do nothing
|
||||||
# 3.- If called and accounting, stop accounting
|
# 3.- If called and accounting, stop accounting
|
||||||
if self.deployed_service.account is None or hasattr(self, 'accounting') is False:
|
if (
|
||||||
|
self.deployed_service.account is None
|
||||||
|
or hasattr(self, 'accounting') is False
|
||||||
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.deployed_service.account.stopUsageAccounting(self)
|
self.deployed_service.account.stopUsageAccounting(self)
|
||||||
@ -414,6 +469,7 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
|||||||
"""
|
"""
|
||||||
# Call to isReady of the instance
|
# Call to isReady of the instance
|
||||||
from uds.core.managers import userServiceManager
|
from uds.core.managers import userServiceManager
|
||||||
|
|
||||||
return userServiceManager().isReady(self)
|
return userServiceManager().isReady(self)
|
||||||
|
|
||||||
def isInMaintenance(self) -> bool:
|
def isInMaintenance(self) -> bool:
|
||||||
@ -436,6 +492,7 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
|||||||
Asks the UserServiceManager to cancel the current operation of this user deployed service.
|
Asks the UserServiceManager to cancel the current operation of this user deployed service.
|
||||||
"""
|
"""
|
||||||
from uds.core.managers import userServiceManager
|
from uds.core.managers import userServiceManager
|
||||||
|
|
||||||
userServiceManager().cancel(self)
|
userServiceManager().cancel(self)
|
||||||
|
|
||||||
def removeOrCancel(self) -> None:
|
def removeOrCancel(self) -> None:
|
||||||
@ -455,9 +512,12 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
|||||||
cacheLevel: New cache level to put object in
|
cacheLevel: New cache level to put object in
|
||||||
"""
|
"""
|
||||||
from uds.core.managers import userServiceManager
|
from uds.core.managers import userServiceManager
|
||||||
|
|
||||||
userServiceManager().moveToLevel(self, cacheLevel)
|
userServiceManager().moveToLevel(self, cacheLevel)
|
||||||
|
|
||||||
def getProperty(self, propName: str, default: typing.Optional[str] = None) -> typing.Optional[str]:
|
def getProperty(
|
||||||
|
self, propName: str, default: typing.Optional[str] = None
|
||||||
|
) -> typing.Optional[str]:
|
||||||
try:
|
try:
|
||||||
val = self.properties.get(name=propName).value
|
val = self.properties.get(name=propName).value
|
||||||
return val or default # Empty string is null
|
return val or default # Empty string is null
|
||||||
@ -496,7 +556,10 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
|||||||
"""
|
"""
|
||||||
Returns True if this user service does not needs an publication, or if this deployed service publication is the current one
|
Returns True if this user service does not needs an publication, or if this deployed service publication is the current one
|
||||||
"""
|
"""
|
||||||
return self.deployed_service.service.getType().publicationType is None or self.publication == self.deployed_service.activePublication()
|
return (
|
||||||
|
self.deployed_service.service.getType().publicationType is None
|
||||||
|
or self.publication == self.deployed_service.activePublication()
|
||||||
|
)
|
||||||
|
|
||||||
# Utility for logging
|
# Utility for logging
|
||||||
def log(self, message: str, level: int = log.INFO) -> None:
|
def log(self, message: str, level: int = log.INFO) -> None:
|
||||||
@ -507,8 +570,13 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "User service {}, unique_id {}, cache_level {}, user {}, name {}, state {}:{}".format(
|
return "User service {}, unique_id {}, cache_level {}, user {}, name {}, state {}:{}".format(
|
||||||
self.name, self.unique_id, self.cache_level, self.user, self.friendly_name,
|
self.name,
|
||||||
State.toString(self.state), State.toString(self.os_state)
|
self.unique_id,
|
||||||
|
self.cache_level,
|
||||||
|
self.user,
|
||||||
|
self.friendly_name,
|
||||||
|
State.toString(self.state),
|
||||||
|
State.toString(self.os_state),
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -45,17 +45,26 @@ class UserServiceProperty(models.Model): # pylint: disable=too-many-public-meth
|
|||||||
Properties for User Service.
|
Properties for User Service.
|
||||||
The value field is a Text field, so we can put whatever we want in it
|
The value field is a Text field, so we can put whatever we want in it
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = models.CharField(max_length=128, db_index=True)
|
name = models.CharField(max_length=128, db_index=True)
|
||||||
value = models.TextField(default='')
|
value = models.TextField(default='')
|
||||||
user_service = models.ForeignKey(UserService, on_delete=models.CASCADE, related_name='properties')
|
user_service = models.ForeignKey(
|
||||||
|
UserService, on_delete=models.CASCADE, related_name='properties'
|
||||||
|
)
|
||||||
|
|
||||||
|
# "fake" declarations for type checking
|
||||||
|
objects: 'models.BaseManager[UserServiceProperty]'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Meta class to declare default order and unique multiple field index
|
Meta class to declare default order and unique multiple field index
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_table = 'uds__user_service_property'
|
db_table = 'uds__user_service_property'
|
||||||
unique_together = (('name', 'user_service'),)
|
unique_together = (('name', 'user_service'),)
|
||||||
app_label = 'uds'
|
app_label = 'uds'
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return "Property of {}. {}={}".format(self.user_service.pk, self.name, self.value)
|
return "Property of {}. {}={}".format(
|
||||||
|
self.user_service.pk, self.name, self.value
|
||||||
|
)
|
||||||
|
@ -75,7 +75,7 @@ def getSqlDatetime() -> datetime:
|
|||||||
def getSqlDatetimeAsUnix() -> int:
|
def getSqlDatetimeAsUnix() -> int:
|
||||||
return int(mktime(getSqlDatetime().timetuple()))
|
return int(mktime(getSqlDatetime().timetuple()))
|
||||||
|
|
||||||
def getSqlFnc(fncName):
|
def getSqlFnc(fncName: str) -> str:
|
||||||
"""
|
"""
|
||||||
Convert different sql functions for different platforms
|
Convert different sql functions for different platforms
|
||||||
"""
|
"""
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -39,10 +39,12 @@ from uds.core.util.model import generateUuid
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class UUIDModel(models.Model):
|
class UUIDModel(models.Model):
|
||||||
"""
|
"""
|
||||||
Base abstract model for models that require an uuid
|
Base abstract model for models that require an uuid
|
||||||
"""
|
"""
|
||||||
|
|
||||||
uuid = models.CharField(max_length=50, default=None, null=True, unique=True)
|
uuid = models.CharField(max_length=50, default=None, null=True, unique=True)
|
||||||
|
|
||||||
# Automatic field from Model without a defined specific primary_key
|
# Automatic field from Model without a defined specific primary_key
|
||||||
@ -55,13 +57,17 @@ class UUIDModel(models.Model):
|
|||||||
return generateUuid()
|
return generateUuid()
|
||||||
|
|
||||||
# Override default save to add uuid
|
# Override default save to add uuid
|
||||||
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
|
def save(
|
||||||
|
self, force_insert=False, force_update=False, using=None, update_fields=None
|
||||||
|
):
|
||||||
if not self.uuid:
|
if not self.uuid:
|
||||||
self.uuid = self.genUuid()
|
self.uuid = self.genUuid()
|
||||||
elif self.uuid != self.uuid.lower():
|
elif self.uuid != self.uuid.lower():
|
||||||
self.uuid = self.uuid.lower() # If we modify uuid elsewhere, ensure that it's stored in lower case
|
self.uuid = (
|
||||||
|
self.uuid.lower()
|
||||||
|
) # If we modify uuid elsewhere, ensure that it's stored in lower case
|
||||||
|
|
||||||
return models.Model.save(self, force_insert, force_update, using, update_fields)
|
return models.Model.save(self, force_insert, force_update, using, update_fields)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return 'Object of class {} with uuid {}'.format(self.__class__, self.uuid)
|
return 'Object of class {} with uuid {}'.format(self.__class__, self.uuid)
|
||||||
|
Loading…
Reference in New Issue
Block a user