forked from shaba/openuds
Fixed cache bug intruduced on encoders module removal
Improbed type checking for REST (ongoing) Optimized queries (using prefetch) for service pools listing
This commit is contained in:
parent
45ca92b77e
commit
6c54f8e75a
@ -56,6 +56,10 @@ from .handlers import (
|
|||||||
|
|
||||||
from . import processors
|
from . import processors
|
||||||
|
|
||||||
|
# Not imported at runtime, just for type checking
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
from uds.core.util.request import ExtendedHttpRequest
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
__all__ = ['Handler', 'Dispatcher']
|
__all__ = ['Handler', 'Dispatcher']
|
||||||
@ -72,7 +76,7 @@ class Dispatcher(View):
|
|||||||
|
|
||||||
# pylint: disable=too-many-locals, too-many-return-statements, too-many-branches, too-many-statements
|
# pylint: disable=too-many-locals, too-many-return-statements, too-many-branches, too-many-statements
|
||||||
@method_decorator(csrf_exempt)
|
@method_decorator(csrf_exempt)
|
||||||
def dispatch(self, request: http.HttpRequest, *args, **kwargs):
|
def dispatch(self, request: 'ExtendedHttpRequest', *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Processes the REST request and routes it wherever it needs to be routed
|
Processes the REST request and routes it wherever it needs to be routed
|
||||||
"""
|
"""
|
||||||
|
@ -36,7 +36,16 @@ import typing
|
|||||||
|
|
||||||
from django.db.models import Q, Count
|
from django.db.models import Q, Count
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
from uds.models import ServicePool, OSManager, Service, Image, ServicePoolGroup, Account, User, getSqlDatetime
|
from uds.models import (
|
||||||
|
ServicePool,
|
||||||
|
OSManager,
|
||||||
|
Service,
|
||||||
|
Image,
|
||||||
|
ServicePoolGroup,
|
||||||
|
Account,
|
||||||
|
User,
|
||||||
|
getSqlDatetime,
|
||||||
|
)
|
||||||
from uds.models.calendar_action import (
|
from uds.models.calendar_action import (
|
||||||
CALENDAR_ACTION_INITIAL,
|
CALENDAR_ACTION_INITIAL,
|
||||||
CALENDAR_ACTION_MAX,
|
CALENDAR_ACTION_MAX,
|
||||||
@ -63,7 +72,14 @@ from uds.core.util import permissions
|
|||||||
from uds.REST.model import ModelHandler
|
from uds.REST.model import ModelHandler
|
||||||
from uds.REST import RequestError, ResponseError
|
from uds.REST import RequestError, ResponseError
|
||||||
|
|
||||||
from .user_services import AssignedService, CachedService, Groups, Transports, Publications, Changelog
|
from .user_services import (
|
||||||
|
AssignedService,
|
||||||
|
CachedService,
|
||||||
|
Groups,
|
||||||
|
Transports,
|
||||||
|
Publications,
|
||||||
|
Changelog,
|
||||||
|
)
|
||||||
from .op_calendars import AccessCalendars, ActionsCalendars
|
from .op_calendars import AccessCalendars, ActionsCalendars
|
||||||
from .services import Services
|
from .services import Services
|
||||||
|
|
||||||
@ -75,6 +91,7 @@ class ServicesPools(ModelHandler):
|
|||||||
"""
|
"""
|
||||||
Handles Services Pools REST requests
|
Handles Services Pools REST requests
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model = ServicePool
|
model = ServicePool
|
||||||
detail = {
|
detail = {
|
||||||
'services': AssignedService,
|
'services': AssignedService,
|
||||||
@ -84,13 +101,30 @@ class ServicesPools(ModelHandler):
|
|||||||
'publications': Publications,
|
'publications': Publications,
|
||||||
'changelog': Changelog,
|
'changelog': Changelog,
|
||||||
'access': AccessCalendars,
|
'access': AccessCalendars,
|
||||||
'actions': ActionsCalendars
|
'actions': ActionsCalendars,
|
||||||
}
|
}
|
||||||
|
|
||||||
save_fields = ['name', 'short_name', 'comments', 'tags', 'service_id',
|
save_fields = [
|
||||||
'osmanager_id', 'image_id', 'pool_group_id', 'initial_srvs',
|
'name',
|
||||||
'cache_l1_srvs', 'cache_l2_srvs', 'max_srvs', 'show_transports', 'visible',
|
'short_name',
|
||||||
'allow_users_remove', 'allow_users_reset', 'ignores_unused', 'account_id', 'calendar_message']
|
'comments',
|
||||||
|
'tags',
|
||||||
|
'service_id',
|
||||||
|
'osmanager_id',
|
||||||
|
'image_id',
|
||||||
|
'pool_group_id',
|
||||||
|
'initial_srvs',
|
||||||
|
'cache_l1_srvs',
|
||||||
|
'cache_l2_srvs',
|
||||||
|
'max_srvs',
|
||||||
|
'show_transports',
|
||||||
|
'visible',
|
||||||
|
'allow_users_remove',
|
||||||
|
'allow_users_reset',
|
||||||
|
'ignores_unused',
|
||||||
|
'account_id',
|
||||||
|
'calendar_message',
|
||||||
|
]
|
||||||
|
|
||||||
remove_fields = ['osmanager_id', 'service_id']
|
remove_fields = ['osmanager_id', 'service_id']
|
||||||
|
|
||||||
@ -120,14 +154,53 @@ class ServicesPools(ModelHandler):
|
|||||||
|
|
||||||
def getItems(self, *args, **kwargs):
|
def getItems(self, *args, **kwargs):
|
||||||
# Optimized query, due that there is a lot of info needed for theee
|
# Optimized query, due that there is a lot of info needed for theee
|
||||||
d = getSqlDatetime() - datetime.timedelta(seconds=GlobalConfig.RESTRAINT_TIME.getInt())
|
d = getSqlDatetime() - datetime.timedelta(
|
||||||
return super().getItems(overview=kwargs.get('overview', True),
|
seconds=GlobalConfig.RESTRAINT_TIME.getInt()
|
||||||
query=(ServicePool.objects.prefetch_related('service', 'service__provider', 'servicesPoolGroup', 'image', 'tags', 'memberOfMeta__meta_pool', 'account')
|
)
|
||||||
.annotate(valid_count=Count('userServices', filter=~Q(userServices__state__in=State.INFO_STATES)))
|
return super().getItems(
|
||||||
.annotate(preparing_count=Count('userServices', filter=Q(userServices__state=State.PREPARING)))
|
overview=kwargs.get('overview', True),
|
||||||
.annotate(error_count=Count('userServices', filter=Q(userServices__state=State.ERROR, userServices__state_date__gt=d)))
|
query=(
|
||||||
.annotate(usage_count=Count('userServices', filter=Q(userServices__state__in=State.VALID_STATES, userServices__cache_level=0)))
|
ServicePool.objects.prefetch_related(
|
||||||
)
|
'service',
|
||||||
|
'service__provider',
|
||||||
|
'servicesPoolGroup',
|
||||||
|
'servicesPoolGroup__image',
|
||||||
|
'osmanager',
|
||||||
|
'image',
|
||||||
|
'tags',
|
||||||
|
'memberOfMeta__meta_pool',
|
||||||
|
'account',
|
||||||
|
)
|
||||||
|
.annotate(
|
||||||
|
valid_count=Count(
|
||||||
|
'userServices',
|
||||||
|
filter=~Q(userServices__state__in=State.INFO_STATES),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.annotate(
|
||||||
|
preparing_count=Count(
|
||||||
|
'userServices', filter=Q(userServices__state=State.PREPARING)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.annotate(
|
||||||
|
error_count=Count(
|
||||||
|
'userServices',
|
||||||
|
filter=Q(
|
||||||
|
userServices__state=State.ERROR,
|
||||||
|
userServices__state_date__gt=d,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.annotate(
|
||||||
|
usage_count=Count(
|
||||||
|
'userServices',
|
||||||
|
filter=Q(
|
||||||
|
userServices__state__in=State.VALID_STATES,
|
||||||
|
userServices__cache_level=0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
# return super().getItems(overview=kwargs.get('overview', True), prefetch=['service', 'service__provider', 'servicesPoolGroup', 'image', 'tags'])
|
# return super().getItems(overview=kwargs.get('overview', True), prefetch=['service', 'service__provider', 'servicesPoolGroup', 'image', 'tags'])
|
||||||
# return super(ServicesPools, self).getItems(*args, **kwargs)
|
# return super(ServicesPools, self).getItems(*args, **kwargs)
|
||||||
@ -161,7 +234,9 @@ class ServicesPools(ModelHandler):
|
|||||||
'parent_type': item.service.data_type,
|
'parent_type': item.service.data_type,
|
||||||
'comments': item.comments,
|
'comments': item.comments,
|
||||||
'state': state,
|
'state': state,
|
||||||
'thumb': item.image.thumb64 if item.image is not None else DEFAULT_THUMB_BASE64,
|
'thumb': item.image.thumb64
|
||||||
|
if item.image is not None
|
||||||
|
else DEFAULT_THUMB_BASE64,
|
||||||
'account': item.account.name if item.account is not None else '',
|
'account': item.account.name if item.account is not None else '',
|
||||||
'account_id': item.account.uuid if item.account is not None else None,
|
'account_id': item.account.uuid if item.account is not None else None,
|
||||||
'service_id': item.service.uuid,
|
'service_id': item.service.uuid,
|
||||||
@ -177,7 +252,10 @@ class ServicesPools(ModelHandler):
|
|||||||
'allow_users_reset': item.allow_users_reset,
|
'allow_users_reset': item.allow_users_reset,
|
||||||
'ignores_unused': item.ignores_unused,
|
'ignores_unused': item.ignores_unused,
|
||||||
'fallbackAccess': item.fallbackAccess,
|
'fallbackAccess': item.fallbackAccess,
|
||||||
'meta_member': [{'id': i.meta_pool.uuid, 'name': i.meta_pool.name} for i in item.memberOfMeta.all()],
|
'meta_member': [
|
||||||
|
{'id': i.meta_pool.uuid, 'name': i.meta_pool.name}
|
||||||
|
for i in item.memberOfMeta.all()
|
||||||
|
],
|
||||||
'calendar_message': item.calendar_message,
|
'calendar_message': item.calendar_message,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,8 +267,12 @@ class ServicesPools(ModelHandler):
|
|||||||
restrained = item.error_count >= GlobalConfig.RESTRAINT_COUNT.getInt() # type: ignore
|
restrained = item.error_count >= GlobalConfig.RESTRAINT_COUNT.getInt() # type: ignore
|
||||||
usage_count = item.usage_count # type: ignore
|
usage_count = item.usage_count # type: ignore
|
||||||
else:
|
else:
|
||||||
valid_count = item.userServices.exclude(state__in=State.INFO_STATES).count()
|
valid_count = item.userServices.exclude(
|
||||||
preparing_count = item.userServices.filter(state=State.PREPARING).count()
|
state__in=State.INFO_STATES
|
||||||
|
).count()
|
||||||
|
preparing_count = item.userServices.filter(
|
||||||
|
state=State.PREPARING
|
||||||
|
).count()
|
||||||
restrained = item.isRestrained()
|
restrained = item.isRestrained()
|
||||||
usage_count = -1
|
usage_count = -1
|
||||||
|
|
||||||
@ -203,9 +285,10 @@ class ServicesPools(ModelHandler):
|
|||||||
if item.servicesPoolGroup.image is not None:
|
if item.servicesPoolGroup.image is not None:
|
||||||
poolGroupThumb = item.servicesPoolGroup.image.thumb64
|
poolGroupThumb = item.servicesPoolGroup.image.thumb64
|
||||||
|
|
||||||
|
|
||||||
val['state'] = state
|
val['state'] = state
|
||||||
val['thumb'] = item.image.thumb64 if item.image is not None else DEFAULT_THUMB_BASE64
|
val['thumb'] = (
|
||||||
|
item.image.thumb64 if item.image is not None else DEFAULT_THUMB_BASE64
|
||||||
|
)
|
||||||
val['user_services_count'] = valid_count
|
val['user_services_count'] = valid_count
|
||||||
val['user_services_in_preparation'] = preparing_count
|
val['user_services_in_preparation'] = preparing_count
|
||||||
val['tags'] = [tag.tag for tag in item.tags.all()]
|
val['tags'] = [tag.tag for tag in item.tags.all()]
|
||||||
@ -227,51 +310,74 @@ class ServicesPools(ModelHandler):
|
|||||||
# if OSManager.objects.count() < 1: # No os managers, can't create db
|
# if OSManager.objects.count() < 1: # No os managers, can't create db
|
||||||
# raise ResponseError(ugettext('Create at least one OS Manager before creating a new service pool'))
|
# raise ResponseError(ugettext('Create at least one OS Manager before creating a new service pool'))
|
||||||
if Service.objects.count() < 1:
|
if Service.objects.count() < 1:
|
||||||
raise ResponseError(ugettext('Create at least a service before creating a new service pool'))
|
raise ResponseError(
|
||||||
|
ugettext('Create at least a service before creating a new service pool')
|
||||||
|
)
|
||||||
|
|
||||||
g = self.addDefaultFields([], ['name', 'short_name', 'comments', 'tags'])
|
g = self.addDefaultFields([], ['name', 'short_name', 'comments', 'tags'])
|
||||||
|
|
||||||
for f in [{
|
for f in [
|
||||||
|
{
|
||||||
'name': 'service_id',
|
'name': 'service_id',
|
||||||
'values': [gui.choiceItem('', '')] + gui.sortedChoices([gui.choiceItem(v.uuid, v.provider.name + '\\' + v.name) for v in Service.objects.all()]),
|
'values': [gui.choiceItem('', '')]
|
||||||
|
+ gui.sortedChoices(
|
||||||
|
[
|
||||||
|
gui.choiceItem(v.uuid, v.provider.name + '\\' + v.name)
|
||||||
|
for v in Service.objects.all()
|
||||||
|
]
|
||||||
|
),
|
||||||
'label': ugettext('Base service'),
|
'label': ugettext('Base service'),
|
||||||
'tooltip': ugettext('Service used as base of this service pool'),
|
'tooltip': ugettext('Service used as base of this service pool'),
|
||||||
'type': gui.InputField.CHOICE_TYPE,
|
'type': gui.InputField.CHOICE_TYPE,
|
||||||
'rdonly': True,
|
'rdonly': True,
|
||||||
'order': 100, # Ensures is At end
|
'order': 100, # Ensures is At end
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'osmanager_id',
|
'name': 'osmanager_id',
|
||||||
'values': [gui.choiceItem(-1, '')] + gui.sortedChoices([gui.choiceItem(v.uuid, v.name) for v in OSManager.objects.all()]),
|
'values': [gui.choiceItem(-1, '')]
|
||||||
|
+ gui.sortedChoices(
|
||||||
|
[gui.choiceItem(v.uuid, v.name) for v in OSManager.objects.all()]
|
||||||
|
),
|
||||||
'label': ugettext('OS Manager'),
|
'label': ugettext('OS Manager'),
|
||||||
'tooltip': ugettext('OS Manager used as base of this service pool'),
|
'tooltip': ugettext('OS Manager used as base of this service pool'),
|
||||||
'type': gui.InputField.CHOICE_TYPE,
|
'type': gui.InputField.CHOICE_TYPE,
|
||||||
'rdonly': True,
|
'rdonly': True,
|
||||||
'order': 101,
|
'order': 101,
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'allow_users_remove',
|
'name': 'allow_users_remove',
|
||||||
'value': False,
|
'value': False,
|
||||||
'label': ugettext('Allow removal by users'),
|
'label': ugettext('Allow removal by users'),
|
||||||
'tooltip': ugettext('If active, the user will be allowed to remove the service "manually". Be careful with this, because the user will have the "power" to delete it\'s own service'),
|
'tooltip': ugettext(
|
||||||
|
'If active, the user will be allowed to remove the service "manually". Be careful with this, because the user will have the "power" to delete it\'s own service'
|
||||||
|
),
|
||||||
'type': gui.InputField.CHECKBOX_TYPE,
|
'type': gui.InputField.CHECKBOX_TYPE,
|
||||||
'order': 111,
|
'order': 111,
|
||||||
'tab': ugettext('Advanced'),
|
'tab': ugettext('Advanced'),
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'allow_users_reset',
|
'name': 'allow_users_reset',
|
||||||
'value': False,
|
'value': False,
|
||||||
'label': ugettext('Allow reset by users'),
|
'label': ugettext('Allow reset by users'),
|
||||||
'tooltip': ugettext('If active, the user will be allowed to reset the service'),
|
'tooltip': ugettext(
|
||||||
|
'If active, the user will be allowed to reset the service'
|
||||||
|
),
|
||||||
'type': gui.InputField.CHECKBOX_TYPE,
|
'type': gui.InputField.CHECKBOX_TYPE,
|
||||||
'order': 112,
|
'order': 112,
|
||||||
'tab': ugettext('Advanced'),
|
'tab': ugettext('Advanced'),
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'ignores_unused',
|
'name': 'ignores_unused',
|
||||||
'value': False,
|
'value': False,
|
||||||
'label': ugettext('Ignores unused'),
|
'label': ugettext('Ignores unused'),
|
||||||
'tooltip': ugettext('If the option is enabled, UDS will not attempt to detect and remove the user services assigned but not in use.'),
|
'tooltip': ugettext(
|
||||||
|
'If the option is enabled, UDS will not attempt to detect and remove the user services assigned but not in use.'
|
||||||
|
),
|
||||||
'type': gui.InputField.CHECKBOX_TYPE,
|
'type': gui.InputField.CHECKBOX_TYPE,
|
||||||
'order': 113,
|
'order': 113,
|
||||||
'tab': ugettext('Advanced'),
|
'tab': ugettext('Advanced'),
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'visible',
|
'name': 'visible',
|
||||||
'value': True,
|
'value': True,
|
||||||
'label': ugettext('Visible'),
|
'label': ugettext('Visible'),
|
||||||
@ -279,31 +385,51 @@ class ServicesPools(ModelHandler):
|
|||||||
'type': gui.InputField.CHECKBOX_TYPE,
|
'type': gui.InputField.CHECKBOX_TYPE,
|
||||||
'order': 107,
|
'order': 107,
|
||||||
'tab': ugettext('Display'),
|
'tab': ugettext('Display'),
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'image_id',
|
'name': 'image_id',
|
||||||
'values': [gui.choiceImage(-1, '--------', DEFAULT_THUMB_BASE64)] + gui.sortedChoices([gui.choiceImage(v.uuid, v.name, v.thumb64) for v in Image.objects.all()]),
|
'values': [gui.choiceImage(-1, '--------', DEFAULT_THUMB_BASE64)]
|
||||||
|
+ gui.sortedChoices(
|
||||||
|
[
|
||||||
|
gui.choiceImage(v.uuid, v.name, v.thumb64)
|
||||||
|
for v in Image.objects.all()
|
||||||
|
]
|
||||||
|
),
|
||||||
'label': ugettext('Associated Image'),
|
'label': ugettext('Associated Image'),
|
||||||
'tooltip': ugettext('Image assocciated with this service'),
|
'tooltip': ugettext('Image assocciated with this service'),
|
||||||
'type': gui.InputField.IMAGECHOICE_TYPE,
|
'type': gui.InputField.IMAGECHOICE_TYPE,
|
||||||
'order': 120,
|
'order': 120,
|
||||||
'tab': ugettext('Display'),
|
'tab': ugettext('Display'),
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'pool_group_id',
|
'name': 'pool_group_id',
|
||||||
'values': [gui.choiceImage(-1, _('Default'), DEFAULT_THUMB_BASE64)] + gui.sortedChoices([gui.choiceImage(v.uuid, v.name, v.thumb64) for v in ServicePoolGroup.objects.all()]),
|
'values': [gui.choiceImage(-1, _('Default'), DEFAULT_THUMB_BASE64)]
|
||||||
|
+ gui.sortedChoices(
|
||||||
|
[
|
||||||
|
gui.choiceImage(v.uuid, v.name, v.thumb64)
|
||||||
|
for v in ServicePoolGroup.objects.all()
|
||||||
|
]
|
||||||
|
),
|
||||||
'label': ugettext('Pool group'),
|
'label': ugettext('Pool group'),
|
||||||
'tooltip': ugettext('Pool group for this pool (for pool classify on display)'),
|
'tooltip': ugettext(
|
||||||
|
'Pool group for this pool (for pool classify on display)'
|
||||||
|
),
|
||||||
'type': gui.InputField.IMAGECHOICE_TYPE,
|
'type': gui.InputField.IMAGECHOICE_TYPE,
|
||||||
'order': 121,
|
'order': 121,
|
||||||
'tab': ugettext('Display'),
|
'tab': ugettext('Display'),
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'calendar_message',
|
'name': 'calendar_message',
|
||||||
'value': '',
|
'value': '',
|
||||||
'label': ugettext('Calendar access denied text'),
|
'label': ugettext('Calendar access denied text'),
|
||||||
'tooltip': ugettext('Custom message to be shown to users if access is limited by calendar rules.'),
|
'tooltip': ugettext(
|
||||||
|
'Custom message to be shown to users if access is limited by calendar rules.'
|
||||||
|
),
|
||||||
'type': gui.InputField.TEXT_TYPE,
|
'type': gui.InputField.TEXT_TYPE,
|
||||||
'order': 122,
|
'order': 122,
|
||||||
'tab': ugettext('Display'),
|
'tab': ugettext('Display'),
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'initial_srvs',
|
'name': 'initial_srvs',
|
||||||
'value': '0',
|
'value': '0',
|
||||||
'minValue': '0',
|
'minValue': '0',
|
||||||
@ -312,55 +438,74 @@ class ServicesPools(ModelHandler):
|
|||||||
'type': gui.InputField.NUMERIC_TYPE,
|
'type': gui.InputField.NUMERIC_TYPE,
|
||||||
'order': 130,
|
'order': 130,
|
||||||
'tab': ugettext('Availability'),
|
'tab': ugettext('Availability'),
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'cache_l1_srvs',
|
'name': 'cache_l1_srvs',
|
||||||
'value': '0',
|
'value': '0',
|
||||||
'minValue': '0',
|
'minValue': '0',
|
||||||
'label': ugettext('Services to keep in cache'),
|
'label': ugettext('Services to keep in cache'),
|
||||||
'tooltip': ugettext('Services kept in cache for improved user service assignation'),
|
'tooltip': ugettext(
|
||||||
|
'Services kept in cache for improved user service assignation'
|
||||||
|
),
|
||||||
'type': gui.InputField.NUMERIC_TYPE,
|
'type': gui.InputField.NUMERIC_TYPE,
|
||||||
'order': 131,
|
'order': 131,
|
||||||
'tab': ugettext('Availability'),
|
'tab': ugettext('Availability'),
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'cache_l2_srvs',
|
'name': 'cache_l2_srvs',
|
||||||
'value': '0',
|
'value': '0',
|
||||||
'minValue': '0',
|
'minValue': '0',
|
||||||
'label': ugettext('Services to keep in L2 cache'),
|
'label': ugettext('Services to keep in L2 cache'),
|
||||||
'tooltip': ugettext('Services kept in cache of level2 for improved service generation'),
|
'tooltip': ugettext(
|
||||||
|
'Services kept in cache of level2 for improved service generation'
|
||||||
|
),
|
||||||
'type': gui.InputField.NUMERIC_TYPE,
|
'type': gui.InputField.NUMERIC_TYPE,
|
||||||
'order': 132,
|
'order': 132,
|
||||||
'tab': ugettext('Availability'),
|
'tab': ugettext('Availability'),
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'max_srvs',
|
'name': 'max_srvs',
|
||||||
'value': '0',
|
'value': '0',
|
||||||
'minValue': '1',
|
'minValue': '1',
|
||||||
'label': ugettext('Maximum number of services to provide'),
|
'label': ugettext('Maximum number of services to provide'),
|
||||||
'tooltip': ugettext('Maximum number of service (assigned and L1 cache) that can be created for this service'),
|
'tooltip': ugettext(
|
||||||
|
'Maximum number of service (assigned and L1 cache) that can be created for this service'
|
||||||
|
),
|
||||||
'type': gui.InputField.NUMERIC_TYPE,
|
'type': gui.InputField.NUMERIC_TYPE,
|
||||||
'order': 133,
|
'order': 133,
|
||||||
'tab': ugettext('Availability'),
|
'tab': ugettext('Availability'),
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'show_transports',
|
'name': 'show_transports',
|
||||||
'value': True,
|
'value': True,
|
||||||
'label': ugettext('Show transports'),
|
'label': ugettext('Show transports'),
|
||||||
'tooltip': ugettext('If active, alternative transports for user will be shown'),
|
'tooltip': ugettext(
|
||||||
|
'If active, alternative transports for user will be shown'
|
||||||
|
),
|
||||||
'type': gui.InputField.CHECKBOX_TYPE,
|
'type': gui.InputField.CHECKBOX_TYPE,
|
||||||
'tab': ugettext('Advanced'),
|
'tab': ugettext('Advanced'),
|
||||||
'order': 130,
|
'order': 130,
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
'name': 'account_id',
|
'name': 'account_id',
|
||||||
'values': [gui.choiceItem(-1, '')] + gui.sortedChoices([gui.choiceItem(v.uuid, v.name) for v in Account.objects.all()]),
|
'values': [gui.choiceItem(-1, '')]
|
||||||
|
+ gui.sortedChoices(
|
||||||
|
[gui.choiceItem(v.uuid, v.name) for v in Account.objects.all()]
|
||||||
|
),
|
||||||
'label': ugettext('Accounting'),
|
'label': ugettext('Accounting'),
|
||||||
'tooltip': ugettext('Account associated to this service pool'),
|
'tooltip': ugettext('Account associated to this service pool'),
|
||||||
'type': gui.InputField.CHOICE_TYPE,
|
'type': gui.InputField.CHOICE_TYPE,
|
||||||
'tab': ugettext('Advanced'),
|
'tab': ugettext('Advanced'),
|
||||||
'order': 131,
|
'order': 131,
|
||||||
}]:
|
},
|
||||||
|
]:
|
||||||
self.addField(g, f)
|
self.addField(g, f)
|
||||||
|
|
||||||
return g
|
return g
|
||||||
|
|
||||||
def beforeSave(self, fields: typing.Dict[str, typing.Any]) -> None: # pylint: disable=too-many-branches,too-many-statements
|
def beforeSave(
|
||||||
|
self, fields: typing.Dict[str, typing.Any]
|
||||||
|
) -> None: # pylint: disable=too-many-branches,too-many-statements
|
||||||
# logger.debug(self._params)
|
# logger.debug(self._params)
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
@ -379,7 +524,9 @@ class ServicesPools(ModelHandler):
|
|||||||
self._params['allow_users_reset'] = False
|
self._params['allow_users_reset'] = False
|
||||||
|
|
||||||
if serviceType.needsManager is True:
|
if serviceType.needsManager is True:
|
||||||
osmanager = OSManager.objects.get(uuid=processUuid(fields['osmanager_id']))
|
osmanager = OSManager.objects.get(
|
||||||
|
uuid=processUuid(fields['osmanager_id'])
|
||||||
|
)
|
||||||
fields['osmanager_id'] = osmanager.id
|
fields['osmanager_id'] = osmanager.id
|
||||||
else:
|
else:
|
||||||
del fields['osmanager_id']
|
del fields['osmanager_id']
|
||||||
@ -390,12 +537,23 @@ class ServicesPools(ModelHandler):
|
|||||||
fields[k] = v
|
fields[k] = v
|
||||||
|
|
||||||
if serviceType.maxDeployed != -1:
|
if serviceType.maxDeployed != -1:
|
||||||
fields['max_srvs'] = min((int(fields['max_srvs']), serviceType.maxDeployed))
|
fields['max_srvs'] = min(
|
||||||
fields['initial_srvs'] = min(int(fields['initial_srvs']), serviceType.maxDeployed)
|
(int(fields['max_srvs']), serviceType.maxDeployed)
|
||||||
fields['cache_l1_srvs'] = min(int(fields['cache_l1_srvs']), serviceType.maxDeployed)
|
)
|
||||||
|
fields['initial_srvs'] = min(
|
||||||
|
int(fields['initial_srvs']), serviceType.maxDeployed
|
||||||
|
)
|
||||||
|
fields['cache_l1_srvs'] = min(
|
||||||
|
int(fields['cache_l1_srvs']), serviceType.maxDeployed
|
||||||
|
)
|
||||||
|
|
||||||
if serviceType.usesCache is False:
|
if serviceType.usesCache is False:
|
||||||
for k in ('initial_srvs', 'cache_l1_srvs', 'cache_l2_srvs', 'max_srvs'):
|
for k in (
|
||||||
|
'initial_srvs',
|
||||||
|
'cache_l1_srvs',
|
||||||
|
'cache_l2_srvs',
|
||||||
|
'max_srvs',
|
||||||
|
):
|
||||||
fields[k] = 0
|
fields[k] = 0
|
||||||
|
|
||||||
if serviceType.usesCache_L2 is False:
|
if serviceType.usesCache_L2 is False:
|
||||||
@ -405,7 +563,13 @@ class ServicesPools(ModelHandler):
|
|||||||
raise RequestError(ugettext('This service requires an OS Manager'))
|
raise RequestError(ugettext('This service requires an OS Manager'))
|
||||||
|
|
||||||
# If max < initial or cache_1 or cache_l2
|
# If max < initial or cache_1 or cache_l2
|
||||||
fields['max_srvs'] = max((int(fields['initial_srvs']), int(fields['cache_l1_srvs']), int(fields['max_srvs'])))
|
fields['max_srvs'] = max(
|
||||||
|
(
|
||||||
|
int(fields['initial_srvs']),
|
||||||
|
int(fields['cache_l1_srvs']),
|
||||||
|
int(fields['max_srvs']),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# *** ACCOUNT ***
|
# *** ACCOUNT ***
|
||||||
accountId = fields['account_id']
|
accountId = fields['account_id']
|
||||||
@ -414,7 +578,9 @@ class ServicesPools(ModelHandler):
|
|||||||
|
|
||||||
if accountId != '-1':
|
if accountId != '-1':
|
||||||
try:
|
try:
|
||||||
fields['account_id'] = Account.objects.get(uuid=processUuid(accountId)).id
|
fields['account_id'] = Account.objects.get(
|
||||||
|
uuid=processUuid(accountId)
|
||||||
|
).id
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception('Getting account ID')
|
logger.exception('Getting account ID')
|
||||||
|
|
||||||
@ -487,7 +653,11 @@ class ServicesPools(ModelHandler):
|
|||||||
validActions: typing.Tuple[typing.Dict, ...] = ()
|
validActions: typing.Tuple[typing.Dict, ...] = ()
|
||||||
itemInfo = item.service.getType()
|
itemInfo = item.service.getType()
|
||||||
if itemInfo.usesCache is True:
|
if itemInfo.usesCache is True:
|
||||||
validActions += (CALENDAR_ACTION_INITIAL, CALENDAR_ACTION_CACHE_L1, CALENDAR_ACTION_MAX)
|
validActions += (
|
||||||
|
CALENDAR_ACTION_INITIAL,
|
||||||
|
CALENDAR_ACTION_CACHE_L1,
|
||||||
|
CALENDAR_ACTION_MAX,
|
||||||
|
)
|
||||||
if itemInfo.usesCache_L2 is True:
|
if itemInfo.usesCache_L2 is True:
|
||||||
validActions += (CALENDAR_ACTION_CACHE_L2,)
|
validActions += (CALENDAR_ACTION_CACHE_L2,)
|
||||||
|
|
||||||
@ -495,21 +665,33 @@ class ServicesPools(ModelHandler):
|
|||||||
validActions += (CALENDAR_ACTION_PUBLISH,)
|
validActions += (CALENDAR_ACTION_PUBLISH,)
|
||||||
|
|
||||||
# Transport & groups actions
|
# Transport & groups actions
|
||||||
validActions += (CALENDAR_ACTION_ADD_TRANSPORT, CALENDAR_ACTION_DEL_TRANSPORT, CALENDAR_ACTION_ADD_GROUP, CALENDAR_ACTION_DEL_GROUP)
|
validActions += (
|
||||||
|
CALENDAR_ACTION_ADD_TRANSPORT,
|
||||||
|
CALENDAR_ACTION_DEL_TRANSPORT,
|
||||||
|
CALENDAR_ACTION_ADD_GROUP,
|
||||||
|
CALENDAR_ACTION_DEL_GROUP,
|
||||||
|
)
|
||||||
|
|
||||||
# Advanced actions
|
# Advanced actions
|
||||||
validActions += (CALENDAR_ACTION_IGNORE_UNUSED, CALENDAR_ACTION_REMOVE_USERSERVICES)
|
validActions += (
|
||||||
|
CALENDAR_ACTION_IGNORE_UNUSED,
|
||||||
|
CALENDAR_ACTION_REMOVE_USERSERVICES,
|
||||||
|
)
|
||||||
return validActions
|
return validActions
|
||||||
|
|
||||||
def listAssignables(self, item: ServicePool) -> typing.Any:
|
def listAssignables(self, item: ServicePool) -> typing.Any:
|
||||||
service = item.service.getInstance()
|
service = item.service.getInstance()
|
||||||
return [gui.choiceItem(i[0], i[1]) for i in service.listAssignables()]
|
return [gui.choiceItem(i[0], i[1]) for i in service.listAssignables()]
|
||||||
|
|
||||||
def createFromAssignable(self, item: ServicePool) ->typing.Any:
|
def createFromAssignable(self, item: ServicePool) -> typing.Any:
|
||||||
if 'user_id' not in self._params or 'assignable_id' not in self._params:
|
if 'user_id' not in self._params or 'assignable_id' not in self._params:
|
||||||
return self.invalidRequestException('Invalid parameters')
|
return self.invalidRequestException('Invalid parameters')
|
||||||
|
|
||||||
logger.debug('Creating from assignable: %s', self._params)
|
logger.debug('Creating from assignable: %s', self._params)
|
||||||
userServiceManager().createFromAssignable(item, User.objects.get(uuid=processUuid(self._params['user_id'])), self._params['assignable_id'])
|
userServiceManager().createFromAssignable(
|
||||||
|
item,
|
||||||
|
User.objects.get(uuid=processUuid(self._params['user_id'])),
|
||||||
|
self._params['assignable_id'],
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -45,7 +45,8 @@ from uds.core.ui import gui as uiGui
|
|||||||
from uds.core.util import log
|
from uds.core.util import log
|
||||||
from uds.core.util import permissions
|
from uds.core.util import permissions
|
||||||
from uds.core.util.model import processUuid
|
from uds.core.util.model import processUuid
|
||||||
from uds.models import Tag
|
|
||||||
|
from uds.models import Tag, TaggingMixin, ManagedObjectModel
|
||||||
|
|
||||||
from .handlers import (
|
from .handlers import (
|
||||||
Handler,
|
Handler,
|
||||||
@ -54,12 +55,11 @@ from .handlers import (
|
|||||||
RequestError,
|
RequestError,
|
||||||
ResponseError,
|
ResponseError,
|
||||||
AccessDenied,
|
AccessDenied,
|
||||||
NotSupportedError
|
NotSupportedError,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 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.managed_object_model import ManagedObjectModel
|
|
||||||
from uds.models import User
|
from uds.models import User
|
||||||
from uds.core import Module
|
from uds.core import Module
|
||||||
|
|
||||||
@ -80,12 +80,15 @@ class SaveException(HandlerError):
|
|||||||
Exception thrown if couldn't save
|
Exception thrown if couldn't save
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class BaseModelHandler(Handler):
|
class BaseModelHandler(Handler):
|
||||||
"""
|
"""
|
||||||
Base Handler for Master & Detail Handlers
|
Base Handler for Master & Detail Handlers
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def addField(self, gui: typing.List[typing.Any], field: typing.Dict[str, typing.Any]) -> typing.List[typing.Any]:
|
def addField(
|
||||||
|
self, gui: typing.List[typing.Any], field: typing.Dict[str, typing.Any]
|
||||||
|
) -> typing.List[typing.Any]:
|
||||||
"""
|
"""
|
||||||
Add a field to a "gui" description.
|
Add a field to a "gui" description.
|
||||||
This method checks that every required field element is in there.
|
This method checks that every required field element is in there.
|
||||||
@ -108,15 +111,17 @@ class BaseModelHandler(Handler):
|
|||||||
'rdonly': field.get('rdonly', False),
|
'rdonly': field.get('rdonly', False),
|
||||||
'type': field.get('type', uiGui.InputField.TEXT_TYPE),
|
'type': field.get('type', uiGui.InputField.TEXT_TYPE),
|
||||||
'order': field.get('order', 0),
|
'order': field.get('order', 0),
|
||||||
'values': field.get('values', [])
|
'values': field.get('values', []),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
if 'tab' in field:
|
if 'tab' in field:
|
||||||
v['gui']['tab'] = field['tab']
|
v['gui']['tab'] = field['tab']
|
||||||
gui.append(v)
|
gui.append(v)
|
||||||
return gui
|
return gui
|
||||||
|
|
||||||
def addDefaultFields(self, gui: typing.List[typing.Any], flds: typing.List[str]) -> typing.List[typing.Any]:
|
def addDefaultFields(
|
||||||
|
self, gui: typing.List[typing.Any], flds: typing.List[str]
|
||||||
|
) -> typing.List[typing.Any]:
|
||||||
"""
|
"""
|
||||||
Adds default fields (based in a list) to a "gui" description
|
Adds default fields (based in a list) to a "gui" description
|
||||||
:param gui: Gui list where the "default" fielsds will be added
|
:param gui: Gui list where the "default" fielsds will be added
|
||||||
@ -124,66 +129,88 @@ class BaseModelHandler(Handler):
|
|||||||
'priority' and 'small_name', 'short_name', 'tags'
|
'priority' and 'small_name', 'short_name', 'tags'
|
||||||
"""
|
"""
|
||||||
if 'tags' in flds:
|
if 'tags' in flds:
|
||||||
self.addField(gui, {
|
self.addField(
|
||||||
'name': 'tags',
|
gui,
|
||||||
'label': _('Tags'),
|
{
|
||||||
'type': 'taglist',
|
'name': 'tags',
|
||||||
'tooltip': _('Tags for this element'),
|
'label': _('Tags'),
|
||||||
'order': 0 - 105,
|
'type': 'taglist',
|
||||||
})
|
'tooltip': _('Tags for this element'),
|
||||||
|
'order': 0 - 105,
|
||||||
|
},
|
||||||
|
)
|
||||||
if 'name' in flds:
|
if 'name' in flds:
|
||||||
self.addField(gui, {
|
self.addField(
|
||||||
'name': 'name',
|
gui,
|
||||||
'type': 'text',
|
{
|
||||||
'required': True,
|
'name': 'name',
|
||||||
'label': _('Name'),
|
'type': 'text',
|
||||||
'length': 128,
|
'required': True,
|
||||||
'tooltip': _('Name of this element'),
|
'label': _('Name'),
|
||||||
'order': 0 - 100,
|
'length': 128,
|
||||||
})
|
'tooltip': _('Name of this element'),
|
||||||
|
'order': 0 - 100,
|
||||||
|
},
|
||||||
|
)
|
||||||
if 'short_name' in flds:
|
if 'short_name' in flds:
|
||||||
self.addField(gui, {
|
self.addField(
|
||||||
'name': 'short_name',
|
gui,
|
||||||
'type': 'text',
|
{
|
||||||
'label': _('Short name'),
|
'name': 'short_name',
|
||||||
'tooltip': _('Short name for user service visualization'),
|
'type': 'text',
|
||||||
'required': False,
|
'label': _('Short name'),
|
||||||
'length': 32,
|
'tooltip': _('Short name for user service visualization'),
|
||||||
'order': 0 - 95,
|
'required': False,
|
||||||
})
|
'length': 32,
|
||||||
|
'order': 0 - 95,
|
||||||
|
},
|
||||||
|
)
|
||||||
if 'comments' in flds:
|
if 'comments' in flds:
|
||||||
self.addField(gui, {
|
self.addField(
|
||||||
'name': 'comments',
|
gui,
|
||||||
'label': _('Comments'),
|
{
|
||||||
'tooltip': _('Comments for this element'),
|
'name': 'comments',
|
||||||
'length': 256,
|
'label': _('Comments'),
|
||||||
'order': 0 - 90,
|
'tooltip': _('Comments for this element'),
|
||||||
})
|
'length': 256,
|
||||||
|
'order': 0 - 90,
|
||||||
|
},
|
||||||
|
)
|
||||||
if 'priority' in flds:
|
if 'priority' in flds:
|
||||||
self.addField(gui, {
|
self.addField(
|
||||||
'name': 'priority',
|
gui,
|
||||||
'type': 'numeric',
|
{
|
||||||
'label': _('Priority'),
|
'name': 'priority',
|
||||||
'tooltip': _('Selects the priority of this element (lower number means higher priority)'),
|
'type': 'numeric',
|
||||||
'required': True,
|
'label': _('Priority'),
|
||||||
'value': 1,
|
'tooltip': _(
|
||||||
'length': 4,
|
'Selects the priority of this element (lower number means higher priority)'
|
||||||
'order': 0 - 85,
|
),
|
||||||
})
|
'required': True,
|
||||||
|
'value': 1,
|
||||||
|
'length': 4,
|
||||||
|
'order': 0 - 85,
|
||||||
|
},
|
||||||
|
)
|
||||||
if 'small_name' in flds:
|
if 'small_name' in flds:
|
||||||
self.addField(gui, {
|
self.addField(
|
||||||
'name': 'small_name',
|
gui,
|
||||||
'type': 'text',
|
{
|
||||||
'label': _('Label'),
|
'name': 'small_name',
|
||||||
'tooltip': _('Label for this element'),
|
'type': 'text',
|
||||||
'required': True,
|
'label': _('Label'),
|
||||||
'length': 128,
|
'tooltip': _('Label for this element'),
|
||||||
'order': 0 - 80,
|
'required': True,
|
||||||
})
|
'length': 128,
|
||||||
|
'order': 0 - 80,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return gui
|
return gui
|
||||||
|
|
||||||
def ensureAccess(self, obj: models.Model, permission: int, root: bool = False) -> int:
|
def ensureAccess(
|
||||||
|
self, obj: models.Model, permission: int, root: bool = False
|
||||||
|
) -> int:
|
||||||
perm = permissions.getEffectivePermission(self._user, obj, root)
|
perm = permissions.getEffectivePermission(self._user, obj, root)
|
||||||
if perm < permission:
|
if perm < permission:
|
||||||
raise self.accessDenied()
|
raise self.accessDenied()
|
||||||
@ -201,23 +228,25 @@ class BaseModelHandler(Handler):
|
|||||||
Returns a dictionary describing the type (the name, the icon, description, etc...)
|
Returns a dictionary describing the type (the name, the icon, description, etc...)
|
||||||
"""
|
"""
|
||||||
res = self.typeInfo(type_)
|
res = self.typeInfo(type_)
|
||||||
res.update({
|
res.update(
|
||||||
'name': _(type_.name()),
|
{
|
||||||
'type': type_.type(),
|
'name': _(type_.name()),
|
||||||
'description': _(type_.description()),
|
'type': type_.type(),
|
||||||
'icon': type_.icon64().replace('\n', '')
|
'description': _(type_.description()),
|
||||||
})
|
'icon': type_.icon64().replace('\n', ''),
|
||||||
|
}
|
||||||
|
)
|
||||||
if hasattr(type_, 'group'):
|
if hasattr(type_, 'group'):
|
||||||
res['group'] = _(type_.group) # Add group info is it is contained
|
res['group'] = _(type_.group) # Add group info is it is contained
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def processTableFields(
|
def processTableFields(
|
||||||
self,
|
self,
|
||||||
title: str,
|
title: str,
|
||||||
fields: typing.List[typing.Any],
|
fields: typing.List[typing.Any],
|
||||||
row_style: typing.Dict[str, typing.Any],
|
row_style: typing.MutableMapping[str, typing.Any],
|
||||||
subtitle: typing.Optional[str] = None
|
subtitle: typing.Optional[str] = None,
|
||||||
) -> typing.Dict[str, typing.Any]:
|
) -> typing.MutableMapping[str, typing.Any]:
|
||||||
"""
|
"""
|
||||||
Returns a dict containing the table fields description
|
Returns a dict containing the table fields description
|
||||||
"""
|
"""
|
||||||
@ -225,10 +254,12 @@ class BaseModelHandler(Handler):
|
|||||||
'title': title,
|
'title': title,
|
||||||
'fields': fields,
|
'fields': fields,
|
||||||
'row-style': row_style,
|
'row-style': row_style,
|
||||||
'subtitle': subtitle or ''
|
'subtitle': subtitle or '',
|
||||||
}
|
}
|
||||||
|
|
||||||
def readFieldsFromParams(self, fldList: typing.List[str]) -> typing.Dict[str, typing.Any]:
|
def readFieldsFromParams(
|
||||||
|
self, fldList: typing.List[str]
|
||||||
|
) -> typing.Dict[str, typing.Any]:
|
||||||
"""
|
"""
|
||||||
Reads the indicated fields from the parameters received, and if
|
Reads the indicated fields from the parameters received, and if
|
||||||
:param fldList: List of required fields
|
:param fldList: List of required fields
|
||||||
@ -244,26 +275,32 @@ class BaseModelHandler(Handler):
|
|||||||
|
|
||||||
return args
|
return args
|
||||||
|
|
||||||
def fillIntanceFields(self, item: 'ManagedObjectModel', res: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]:
|
def fillIntanceFields(
|
||||||
|
self, item: 'models.Model', res: typing.Dict[str, typing.Any]
|
||||||
|
) -> typing.Dict[str, typing.Any]:
|
||||||
"""
|
"""
|
||||||
For Managed Objects (db element that contains a serialized object), fills a dictionary with the "field" parameters values.
|
For Managed Objects (db element that contains a serialized object), fills a dictionary with the "field" parameters values.
|
||||||
For non managed objects, it does nothing
|
For non managed objects, it does nothing
|
||||||
:param item: Item to extract fields
|
:param item: Item to extract fields
|
||||||
:param res: Dictionary to "extend" with instance key-values pairs
|
:param res: Dictionary to "extend" with instance key-values pairs
|
||||||
"""
|
"""
|
||||||
if hasattr(item, 'getInstance'):
|
if isinstance(item, ManagedObjectModel):
|
||||||
i = item.getInstance()
|
i = item.getInstance()
|
||||||
i.initGui() # Defaults & stuff
|
i.initGui() # Defaults & stuff
|
||||||
value: typing.Any
|
value: typing.Any
|
||||||
for key, value in i.valuesDict().items():
|
for key, value in i.valuesDict().items():
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
value = {"true": True, "false": False}.get(value, value) # Translate "true" & "false" to True & False (booleans)
|
value = {"true": True, "false": False}.get(
|
||||||
|
value, value
|
||||||
|
) # Translate "true" & "false" to True & False (booleans)
|
||||||
logger.debug('%s = %s', key, value)
|
logger.debug('%s = %s', key, value)
|
||||||
res[key] = value
|
res[key] = value
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# Exceptions
|
# Exceptions
|
||||||
def invalidRequestException(self, message: typing.Optional[str] = None) -> HandlerError:
|
def invalidRequestException(
|
||||||
|
self, message: typing.Optional[str] = None
|
||||||
|
) -> HandlerError:
|
||||||
"""
|
"""
|
||||||
Raises an invalid request error with a default translated string
|
Raises an invalid request error with a default translated string
|
||||||
:param message: Custom message to add to exception. If it is None, "Invalid Request" is used
|
:param message: Custom message to add to exception. If it is None, "Invalid Request" is used
|
||||||
@ -271,7 +308,9 @@ class BaseModelHandler(Handler):
|
|||||||
message = message or _('Invalid Request')
|
message = message or _('Invalid Request')
|
||||||
return RequestError('{} {}: {}'.format(message, self.__class__, self._args))
|
return RequestError('{} {}: {}'.format(message, self.__class__, self._args))
|
||||||
|
|
||||||
def invalidResponseException(self, message: typing.Optional[str] = None) -> HandlerError:
|
def invalidResponseException(
|
||||||
|
self, message: typing.Optional[str] = None
|
||||||
|
) -> HandlerError:
|
||||||
message = 'Invalid response' if message is None else message
|
message = 'Invalid response' if message is None else message
|
||||||
return ResponseError(message)
|
return ResponseError(message)
|
||||||
|
|
||||||
@ -279,9 +318,13 @@ class BaseModelHandler(Handler):
|
|||||||
"""
|
"""
|
||||||
Raises a NotFound exception with translated "Method not found" string to current locale
|
Raises a NotFound exception with translated "Method not found" string to current locale
|
||||||
"""
|
"""
|
||||||
return RequestError(_('Method not found in {}: {}').format(self.__class__, self._args))
|
return RequestError(
|
||||||
|
_('Method not found in {}: {}').format(self.__class__, self._args)
|
||||||
|
)
|
||||||
|
|
||||||
def invalidItemException(self, message: typing.Optional[str] = None) -> HandlerError:
|
def invalidItemException(
|
||||||
|
self, message: typing.Optional[str] = None
|
||||||
|
) -> HandlerError:
|
||||||
"""
|
"""
|
||||||
Raises a NotFound exception, with location info
|
Raises a NotFound exception, with location info
|
||||||
"""
|
"""
|
||||||
@ -307,7 +350,9 @@ class BaseModelHandler(Handler):
|
|||||||
"""
|
"""
|
||||||
Invokes a test for an item
|
Invokes a test for an item
|
||||||
"""
|
"""
|
||||||
logger.debug('Called base test for %s --> %s', self.__class__.__name__, self._params)
|
logger.debug(
|
||||||
|
'Called base test for %s --> %s', self.__class__.__name__, self._params
|
||||||
|
)
|
||||||
raise self.invalidMethodException()
|
raise self.invalidMethodException()
|
||||||
|
|
||||||
|
|
||||||
@ -335,6 +380,7 @@ class DetailHandler(BaseModelHandler):
|
|||||||
|
|
||||||
Also accepts GET methods for "custom" methods
|
Also accepts GET methods for "custom" methods
|
||||||
"""
|
"""
|
||||||
|
|
||||||
custom_methods: typing.ClassVar[typing.List[str]] = []
|
custom_methods: typing.ClassVar[typing.List[str]] = []
|
||||||
_parent: typing.Optional['ModelHandler']
|
_parent: typing.Optional['ModelHandler']
|
||||||
_path: str
|
_path: str
|
||||||
@ -344,13 +390,13 @@ class DetailHandler(BaseModelHandler):
|
|||||||
_user: 'User'
|
_user: 'User'
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
parentHandler: 'ModelHandler',
|
parentHandler: 'ModelHandler',
|
||||||
path: str,
|
path: str,
|
||||||
params: typing.Any,
|
params: typing.Any,
|
||||||
*args: str,
|
*args: str,
|
||||||
**kwargs: typing.Any
|
**kwargs: typing.Any
|
||||||
): # pylint: disable=super-init-not-called
|
): # pylint: disable=super-init-not-called
|
||||||
"""
|
"""
|
||||||
Detail Handlers in fact "disabled" handler most initialization, that is no needed because
|
Detail Handlers in fact "disabled" handler most initialization, that is no needed because
|
||||||
parent modelhandler has already done it (so we must access through parent handler)
|
parent modelhandler has already done it (so we must access through parent handler)
|
||||||
@ -363,7 +409,9 @@ class DetailHandler(BaseModelHandler):
|
|||||||
self._kwargs = kwargs
|
self._kwargs = kwargs
|
||||||
self._user = kwargs.get('user', None)
|
self._user = kwargs.get('user', None)
|
||||||
|
|
||||||
def __checkCustom(self, check: str, parent: models.Model, arg: typing.Any = None) -> typing.Any:
|
def __checkCustom(
|
||||||
|
self, check: str, parent: models.Model, arg: typing.Any = None
|
||||||
|
) -> typing.Any:
|
||||||
"""
|
"""
|
||||||
checks curron methods
|
checks curron methods
|
||||||
:param check: Method to check
|
:param check: Method to check
|
||||||
@ -380,7 +428,9 @@ class DetailHandler(BaseModelHandler):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get(self) -> typing.Any: # pylint: disable=too-many-branches,too-many-return-statements
|
def get(
|
||||||
|
self,
|
||||||
|
) -> typing.Any: # pylint: disable=too-many-branches,too-many-return-statements
|
||||||
"""
|
"""
|
||||||
Processes GET method for a detail Handler
|
Processes GET method for a detail Handler
|
||||||
"""
|
"""
|
||||||
@ -409,7 +459,11 @@ class DetailHandler(BaseModelHandler):
|
|||||||
logger.debug('Types: %s', types_)
|
logger.debug('Types: %s', types_)
|
||||||
return types_
|
return types_
|
||||||
if self._args[0] == TABLEINFO:
|
if self._args[0] == TABLEINFO:
|
||||||
return self.processTableFields(self.getTitle(parent), self.getFields(parent), self.getRowStyle(parent))
|
return self.processTableFields(
|
||||||
|
self.getTitle(parent),
|
||||||
|
self.getFields(parent),
|
||||||
|
self.getRowStyle(parent),
|
||||||
|
)
|
||||||
|
|
||||||
# try to get id
|
# try to get id
|
||||||
return self.getItems(parent, processUuid(self._args[0]))
|
return self.getItems(parent, processUuid(self._args[0]))
|
||||||
@ -449,7 +503,9 @@ class DetailHandler(BaseModelHandler):
|
|||||||
raise self.invalidRequestException()
|
raise self.invalidRequestException()
|
||||||
|
|
||||||
logger.debug('Invoking proper saving detail item %s', item)
|
logger.debug('Invoking proper saving detail item %s', item)
|
||||||
return self.saveItem(parent, item)
|
self.saveItem(parent, item)
|
||||||
|
# Empty response
|
||||||
|
return ''
|
||||||
|
|
||||||
def post(self) -> typing.Any:
|
def post(self) -> typing.Any:
|
||||||
"""
|
"""
|
||||||
@ -494,7 +550,9 @@ class DetailHandler(BaseModelHandler):
|
|||||||
# if item is None: # Returns ALL detail items
|
# if item is None: # Returns ALL detail items
|
||||||
# return []
|
# return []
|
||||||
# return {} # Returns one item
|
# return {} # Returns one item
|
||||||
raise NotImplementedError('Must provide an getItems method for {} class'.format(self.__class__))
|
raise NotImplementedError(
|
||||||
|
'Must provide an getItems method for {} class'.format(self.__class__)
|
||||||
|
)
|
||||||
|
|
||||||
# Default save
|
# Default save
|
||||||
def saveItem(self, parent: models.Model, item: typing.Optional[str]) -> None:
|
def saveItem(self, parent: models.Model, item: typing.Optional[str]) -> None:
|
||||||
@ -559,7 +617,9 @@ class DetailHandler(BaseModelHandler):
|
|||||||
# raise RequestError('Gui not provided for this type of object')
|
# raise RequestError('Gui not provided for this type of object')
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def getTypes(self, parent: models.Model, forType: typing.Optional[str]) -> typing.Iterable[typing.Dict[str, typing.Any]]:
|
def getTypes(
|
||||||
|
self, parent: models.Model, forType: typing.Optional[str]
|
||||||
|
) -> typing.Iterable[typing.Dict[str, typing.Any]]:
|
||||||
"""
|
"""
|
||||||
The default is that detail element will not have any types (they are "homogeneous")
|
The default is that detail element will not have any types (they are "homogeneous")
|
||||||
but we provided this method, that can be overridden, in case one detail needs it
|
but we provided this method, that can be overridden, in case one detail needs it
|
||||||
@ -594,12 +654,13 @@ class ModelHandler(BaseModelHandler):
|
|||||||
Note: Instance variables are the variables declared and serialized by modules.
|
Note: Instance variables are the variables declared and serialized by modules.
|
||||||
The only detail that has types within is "Service", child of "Provider"
|
The only detail that has types within is "Service", child of "Provider"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Authentication related
|
# Authentication related
|
||||||
authenticated = True
|
authenticated = True
|
||||||
needs_staff = True
|
needs_staff = True
|
||||||
|
|
||||||
# Which model does this manage, must be a django model ofc
|
# Which model does this manage, must be a django model ofc
|
||||||
model: typing.ClassVar[models.Model]
|
model: typing.ClassVar[typing.Type[models.Model]]
|
||||||
|
|
||||||
# By default, filter is empty
|
# By default, filter is empty
|
||||||
fltr: typing.Optional[str] = None
|
fltr: typing.Optional[str] = None
|
||||||
@ -607,9 +668,13 @@ class ModelHandler(BaseModelHandler):
|
|||||||
# This is an array of tuples of two items, where first is method and second inticates if method needs parent id (normal behavior is it needs it)
|
# This is an array of tuples of two items, where first is method and second inticates if method needs parent id (normal behavior is it needs it)
|
||||||
# For example ('services', True) -- > .../id_parent/services
|
# For example ('services', True) -- > .../id_parent/services
|
||||||
# ('services', False) --> ..../services
|
# ('services', False) --> ..../services
|
||||||
custom_methods: typing.ClassVar[typing.List[typing.Tuple[str, bool]]] = [] # If this model respond to "custom" methods, we will declare them here
|
custom_methods: typing.ClassVar[
|
||||||
|
typing.List[typing.Tuple[str, bool]]
|
||||||
|
] = [] # If this model respond to "custom" methods, we will declare them here
|
||||||
# If this model has details, which ones
|
# If this model has details, which ones
|
||||||
detail: typing.ClassVar[typing.Optional[typing.Dict[str, typing.Type[DetailHandler]]]] = None # Dictionary containing detail routing
|
detail: typing.ClassVar[
|
||||||
|
typing.Optional[typing.Dict[str, typing.Type[DetailHandler]]]
|
||||||
|
] = None # Dictionary containing detail routing
|
||||||
# Put needed fields
|
# Put needed fields
|
||||||
save_fields: typing.ClassVar[typing.List[str]] = []
|
save_fields: typing.ClassVar[typing.List[str]] = []
|
||||||
# Put removable fields before updating
|
# Put removable fields before updating
|
||||||
@ -704,7 +769,7 @@ class ModelHandler(BaseModelHandler):
|
|||||||
del self._params['filter'] # Remove parameter
|
del self._params['filter'] # Remove parameter
|
||||||
logger.debug('Found a filter expression (%s)', self.fltr)
|
logger.debug('Found a filter expression (%s)', self.fltr)
|
||||||
|
|
||||||
def doFilter(self, data: typing.List[typing.Dict[str, typing.Any]]) -> typing.List[typing.Dict[str, typing.Any]]:
|
def doFilter(self, data: typing.Any) -> typing.Any:
|
||||||
# Right now, filtering only supports a single filter, in a future
|
# Right now, filtering only supports a single filter, in a future
|
||||||
# we may improve it
|
# we may improve it
|
||||||
if self.fltr is None:
|
if self.fltr is None:
|
||||||
@ -727,7 +792,7 @@ class ModelHandler(BaseModelHandler):
|
|||||||
|
|
||||||
r = re.compile(s + fnmatch.translate(pattern) + e, re.RegexFlag.IGNORECASE)
|
r = re.compile(s + fnmatch.translate(pattern) + e, re.RegexFlag.IGNORECASE)
|
||||||
|
|
||||||
def fltr_function(item: typing.Dict[str, typing.Any]):
|
def fltr_function(item: typing.MutableMapping[str, typing.Any]):
|
||||||
try:
|
try:
|
||||||
if fld not in item or r.match(item[fld]) is None:
|
if fld not in item or r.match(item[fld]) is None:
|
||||||
return False
|
return False
|
||||||
@ -746,8 +811,10 @@ class ModelHandler(BaseModelHandler):
|
|||||||
|
|
||||||
# Helper to process detail
|
# Helper to process detail
|
||||||
# Details can be managed (writen) by any user that has MANAGEMENT permission over parent
|
# Details can be managed (writen) by any user that has MANAGEMENT permission over parent
|
||||||
def processDetail(self):
|
def processDetail(self) -> typing.Any:
|
||||||
logger.debug('Processing detail %s for with params %s', self._path, self._params)
|
logger.debug(
|
||||||
|
'Processing detail %s for with params %s', self._path, self._params
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
item: models.Model = self.model.objects.filter(uuid=self._args[0])[0]
|
item: models.Model = self.model.objects.filter(uuid=self._args[0])[0]
|
||||||
# If we do not have access to parent to, at least, read...
|
# If we do not have access to parent to, at least, read...
|
||||||
@ -757,14 +824,28 @@ class ModelHandler(BaseModelHandler):
|
|||||||
else:
|
else:
|
||||||
requiredPermission = permissions.PERMISSION_READ
|
requiredPermission = permissions.PERMISSION_READ
|
||||||
|
|
||||||
if permissions.checkPermissions(self._user, item, requiredPermission) is False:
|
if (
|
||||||
logger.debug('Permission for user %s does not comply with %s', self._user, requiredPermission)
|
permissions.checkPermissions(self._user, item, requiredPermission)
|
||||||
|
is False
|
||||||
|
):
|
||||||
|
logger.debug(
|
||||||
|
'Permission for user %s does not comply with %s',
|
||||||
|
self._user,
|
||||||
|
requiredPermission,
|
||||||
|
)
|
||||||
raise self.accessDenied()
|
raise self.accessDenied()
|
||||||
|
|
||||||
detailCls = self.detail[self._args[1]] # pylint: disable=unsubscriptable-object
|
if not self.detail:
|
||||||
|
raise self.invalidRequestException()
|
||||||
|
|
||||||
|
detailCls = self.detail[
|
||||||
|
self._args[1]
|
||||||
|
] # pylint: disable=unsubscriptable-object
|
||||||
args = list(self._args[2:])
|
args = list(self._args[2:])
|
||||||
path = self._path + '/'.join(args[:2])
|
path = self._path + '/'.join(args[:2])
|
||||||
detail = detailCls(self, path, self._params, *args, parent=item, user=self._user)
|
detail = detailCls(
|
||||||
|
self, path, self._params, *args, parent=item, user=self._user
|
||||||
|
)
|
||||||
method = getattr(detail, self._operation)
|
method = getattr(detail, self._operation)
|
||||||
|
|
||||||
return method()
|
return method()
|
||||||
@ -775,7 +856,9 @@ class ModelHandler(BaseModelHandler):
|
|||||||
|
|
||||||
raise Exception('Invalid code executed on processDetail')
|
raise Exception('Invalid code executed on processDetail')
|
||||||
|
|
||||||
def getItems(self, *args, **kwargs) -> typing.Generator[typing.Dict[str, typing.Any], None, None]:
|
def getItems(
|
||||||
|
self, *args, **kwargs
|
||||||
|
) -> typing.Generator[typing.MutableMapping[str, typing.Any], None, None]:
|
||||||
if 'overview' in kwargs:
|
if 'overview' in kwargs:
|
||||||
overview = kwargs['overview']
|
overview = kwargs['overview']
|
||||||
del kwargs['overview']
|
del kwargs['overview']
|
||||||
@ -795,11 +878,20 @@ class ModelHandler(BaseModelHandler):
|
|||||||
del kwargs['query']
|
del kwargs['query']
|
||||||
else:
|
else:
|
||||||
logger.debug('Args: %s, kwargs: %s', args, kwargs)
|
logger.debug('Args: %s, kwargs: %s', args, kwargs)
|
||||||
query = self.model.objects.filter(*args, **kwargs).prefetch_related(*prefetch)
|
query = self.model.objects.filter(*args, **kwargs).prefetch_related(
|
||||||
|
*prefetch
|
||||||
|
)
|
||||||
|
|
||||||
for item in query:
|
for item in query:
|
||||||
try:
|
try:
|
||||||
if permissions.checkPermissions(typing.cast('User', self._user), item, permissions.PERMISSION_READ) is False:
|
if (
|
||||||
|
permissions.checkPermissions(
|
||||||
|
typing.cast('User', self._user),
|
||||||
|
item,
|
||||||
|
permissions.PERMISSION_READ,
|
||||||
|
)
|
||||||
|
is False
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
if kwargs.get('overview', True):
|
if kwargs.get('overview', True):
|
||||||
yield self.item_as_dict_overview(item)
|
yield self.item_as_dict_overview(item)
|
||||||
@ -819,7 +911,7 @@ class ModelHandler(BaseModelHandler):
|
|||||||
self.extractFilter()
|
self.extractFilter()
|
||||||
return self.doFilter(self.doGet())
|
return self.doFilter(self.doGet())
|
||||||
|
|
||||||
def doGet(self): # pylint: disable=too-many-statements,too-many-branches,too-many-return-statements
|
def doGet(self) -> typing.Any:
|
||||||
logger.debug('method GET for %s, %s', self.__class__.__name__, self._args)
|
logger.debug('method GET for %s, %s', self.__class__.__name__, self._args)
|
||||||
nArgs = len(self._args)
|
nArgs = len(self._args)
|
||||||
|
|
||||||
@ -835,7 +927,13 @@ class ModelHandler(BaseModelHandler):
|
|||||||
operation = getattr(self, self._args[1])
|
operation = getattr(self, self._args[1])
|
||||||
item = self.model.objects.get(uuid=self._args[0].lower())
|
item = self.model.objects.get(uuid=self._args[0].lower())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error('Invalid custom method exception %s/%s/%s: %s', self.__class__.__name__, self._args, self._params, e)
|
logger.error(
|
||||||
|
'Invalid custom method exception %s/%s/%s: %s',
|
||||||
|
self.__class__.__name__,
|
||||||
|
self._args,
|
||||||
|
self._params,
|
||||||
|
e,
|
||||||
|
)
|
||||||
raise self.invalidMethodException()
|
raise self.invalidMethodException()
|
||||||
|
|
||||||
return operation(item)
|
return operation(item)
|
||||||
@ -855,9 +953,14 @@ class ModelHandler(BaseModelHandler):
|
|||||||
if self._args[0] == TYPES:
|
if self._args[0] == TYPES:
|
||||||
return list(self.getTypes())
|
return list(self.getTypes())
|
||||||
if self._args[0] == TABLEINFO:
|
if self._args[0] == TABLEINFO:
|
||||||
return self.processTableFields(self.table_title, self.table_fields, self.table_row_style, self.table_subtitle)
|
return self.processTableFields(
|
||||||
|
self.table_title,
|
||||||
|
self.table_fields,
|
||||||
|
self.table_row_style,
|
||||||
|
self.table_subtitle,
|
||||||
|
)
|
||||||
if self._args[0] == GUI:
|
if self._args[0] == GUI:
|
||||||
return self.getGui(None)
|
return self.getGui('')
|
||||||
|
|
||||||
# get item ID
|
# get item ID
|
||||||
try:
|
try:
|
||||||
@ -890,7 +993,9 @@ class ModelHandler(BaseModelHandler):
|
|||||||
if nArgs != 2:
|
if nArgs != 2:
|
||||||
raise self.invalidRequestException()
|
raise self.invalidRequestException()
|
||||||
try:
|
try:
|
||||||
item = self.model.objects.get(uuid=self._args[0].lower()) # DB maybe case sensitive??, anyway, uuids are stored in lowercase
|
item = self.model.objects.get(
|
||||||
|
uuid=self._args[0].lower()
|
||||||
|
) # DB maybe case sensitive??, anyway, uuids are stored in lowercase
|
||||||
return self.getLogs(item)
|
return self.getLogs(item)
|
||||||
except Exception:
|
except Exception:
|
||||||
raise self.invalidItemException()
|
raise self.invalidItemException()
|
||||||
@ -901,8 +1006,7 @@ class ModelHandler(BaseModelHandler):
|
|||||||
|
|
||||||
raise self.invalidRequestException() # Will not return
|
raise self.invalidRequestException() # Will not return
|
||||||
|
|
||||||
|
def post(self) -> typing.Any:
|
||||||
def post(self):
|
|
||||||
"""
|
"""
|
||||||
Processes a POST request
|
Processes a POST request
|
||||||
"""
|
"""
|
||||||
@ -914,7 +1018,7 @@ class ModelHandler(BaseModelHandler):
|
|||||||
|
|
||||||
raise self.invalidMethodException() # Will not return
|
raise self.invalidMethodException() # Will not return
|
||||||
|
|
||||||
def put(self): # pylint: disable=too-many-branches, too-many-statements
|
def put(self) -> typing.Any:
|
||||||
"""
|
"""
|
||||||
Processes a PUT request
|
Processes a PUT request
|
||||||
"""
|
"""
|
||||||
@ -926,7 +1030,10 @@ class ModelHandler(BaseModelHandler):
|
|||||||
if len(self._args) > 1: # Detail?
|
if len(self._args) > 1: # Detail?
|
||||||
return self.processDetail()
|
return self.processDetail()
|
||||||
|
|
||||||
self.ensureAccess(self.model(), permissions.PERMISSION_ALL, root=True) # Must have write permissions to create, modify, etc..
|
# Here, self.model() indicates an "django model object with default params"
|
||||||
|
self.ensureAccess(
|
||||||
|
self.model(), permissions.PERMISSION_ALL, root=True
|
||||||
|
) # Must have write permissions to create, modify, etc..
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Extract fields
|
# Extract fields
|
||||||
@ -954,11 +1061,16 @@ class ModelHandler(BaseModelHandler):
|
|||||||
item.__dict__.update(args) # Update fields from args
|
item.__dict__.update(args) # Update fields from args
|
||||||
|
|
||||||
# Now if tags, update them
|
# Now if tags, update them
|
||||||
if tags:
|
if isinstance(item, TaggingMixin):
|
||||||
logger.debug('Updating tags: %s', tags)
|
if tags:
|
||||||
item.tags.set([Tag.objects.get_or_create(tag=val)[0] for val in tags if val != ''])
|
logger.debug('Updating tags: %s', tags)
|
||||||
elif isinstance(tags, list): # Present, but list is empty (will be proccesed on "if" else)
|
item.tags.set(
|
||||||
item.tags.clear()
|
[Tag.objects.get_or_create(tag=val)[0] for val in tags if val != '']
|
||||||
|
)
|
||||||
|
elif isinstance(
|
||||||
|
tags, list
|
||||||
|
): # Present, but list is empty (will be proccesed on "if" else)
|
||||||
|
item.tags.clear()
|
||||||
|
|
||||||
except self.model.DoesNotExist:
|
except self.model.DoesNotExist:
|
||||||
raise NotFound('Item not found')
|
raise NotFound('Item not found')
|
||||||
@ -973,14 +1085,19 @@ class ModelHandler(BaseModelHandler):
|
|||||||
raise RequestError('incorrect invocation to PUT')
|
raise RequestError('incorrect invocation to PUT')
|
||||||
|
|
||||||
if not deleteOnError:
|
if not deleteOnError:
|
||||||
self.checkSave(item) # Will raise an exception if item can't be saved (only for modify operations..)
|
self.checkSave(
|
||||||
|
item
|
||||||
|
) # Will raise an exception if item can't be saved (only for modify operations..)
|
||||||
|
|
||||||
# Store associated object if requested (data_type)
|
# Store associated object if requested (data_type)
|
||||||
try:
|
try:
|
||||||
data_type: typing.Optional[str] = self._params.get('data_type', self._params.get('type'))
|
if isinstance(item, ManagedObjectModel):
|
||||||
if data_type:
|
data_type: typing.Optional[str] = self._params.get(
|
||||||
item.data_type = data_type
|
'data_type', self._params.get('type')
|
||||||
item.data = item.getInstance(self._params).serialize()
|
)
|
||||||
|
if data_type:
|
||||||
|
item.data_type = data_type
|
||||||
|
item.data = item.getInstance(self._params).serialize()
|
||||||
|
|
||||||
item.save()
|
item.save()
|
||||||
|
|
||||||
@ -995,7 +1112,7 @@ class ModelHandler(BaseModelHandler):
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def delete(self) -> str:
|
def delete(self) -> typing.Any:
|
||||||
"""
|
"""
|
||||||
Processes a DELETE request
|
Processes a DELETE request
|
||||||
"""
|
"""
|
||||||
@ -1006,7 +1123,9 @@ class ModelHandler(BaseModelHandler):
|
|||||||
if len(self._args) != 1:
|
if len(self._args) != 1:
|
||||||
raise RequestError('Delete need one and only one argument')
|
raise RequestError('Delete need one and only one argument')
|
||||||
|
|
||||||
self.ensureAccess(self.model(), permissions.PERMISSION_ALL, root=True) # Must have write permissions to delete
|
self.ensureAccess(
|
||||||
|
self.model(), permissions.PERMISSION_ALL, root=True
|
||||||
|
) # Must have write permissions to delete
|
||||||
|
|
||||||
try:
|
try:
|
||||||
item = self.model.objects.get(uuid=self._args[0].lower())
|
item = self.model.objects.get(uuid=self._args[0].lower())
|
||||||
|
@ -55,6 +55,10 @@ from uds.core.auths import Authenticator as AuthenticatorInstance
|
|||||||
|
|
||||||
from uds.models import User, Authenticator
|
from uds.models import User, Authenticator
|
||||||
|
|
||||||
|
# Not imported at runtime, just for type checking
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
from uds.core.util.request import ExtendedHttpRequest
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
authLogger = logging.getLogger('authLog')
|
authLogger = logging.getLogger('authLog')
|
||||||
@ -110,11 +114,11 @@ def webLoginRequired(admin: typing.Union[bool, str] = False) -> typing.Callable[
|
|||||||
if admin == 'admin', needs admin
|
if admin == 'admin', needs admin
|
||||||
"""
|
"""
|
||||||
def decorator(view_func: typing.Callable[..., RT]) -> typing.Callable[..., RT]:
|
def decorator(view_func: typing.Callable[..., RT]) -> typing.Callable[..., RT]:
|
||||||
def _wrapped_view(request: HttpRequest, *args, **kwargs) -> RT:
|
def _wrapped_view(request: 'ExtendedHttpRequest', *args, **kwargs) -> RT:
|
||||||
"""
|
"""
|
||||||
Wrapped function for decorator
|
Wrapped function for decorator
|
||||||
"""
|
"""
|
||||||
if request.user is None:
|
if not request.user:
|
||||||
# url = request.build_absolute_uri(GlobalConfig.LOGIN_URL.get())
|
# url = request.build_absolute_uri(GlobalConfig.LOGIN_URL.get())
|
||||||
# if GlobalConfig.REDIRECT_TO_HTTPS.getBool() is True:
|
# if GlobalConfig.REDIRECT_TO_HTTPS.getBool() is True:
|
||||||
# url = url.replace('http://', 'https://')
|
# url = url.replace('http://', 'https://')
|
||||||
|
@ -129,7 +129,7 @@ class Cache:
|
|||||||
now = getSqlDatetime()
|
now = getSqlDatetime()
|
||||||
try:
|
try:
|
||||||
DBCache.objects.create(
|
DBCache.objects.create(
|
||||||
owner=self._owner, key=key, value=value, created=now, validity=validity
|
owner=self._owner, key=key, value=strValue, created=now, validity=validity
|
||||||
) # @UndefinedVariable
|
) # @UndefinedVariable
|
||||||
except Exception:
|
except Exception:
|
||||||
try:
|
try:
|
||||||
|
@ -141,7 +141,7 @@ class CalendarChecker:
|
|||||||
memCache = caches['memory']
|
memCache = caches['memory']
|
||||||
|
|
||||||
# First, try to get data from cache if it is valid
|
# First, try to get data from cache if it is valid
|
||||||
cacheKey = str(hash(self.calendar.modified)) + str(dtime.date().toordinal()) + self.calendar.uuid + 'checker'
|
cacheKey = str(self.calendar.modified.toordinal()) + str(dtime.date().toordinal()) + self.calendar.uuid + 'checker'
|
||||||
# First, check "local memory cache", and if not found, from DB cache
|
# First, check "local memory cache", and if not found, from DB cache
|
||||||
cached = memCache.get(cacheKey) or CalendarChecker.cache.get(cacheKey, None)
|
cached = memCache.get(cacheKey) or CalendarChecker.cache.get(cacheKey, None)
|
||||||
|
|
||||||
|
@ -52,6 +52,9 @@ class ExtendedHttpRequest(HttpRequest):
|
|||||||
os: DictAsObj
|
os: DictAsObj
|
||||||
user: typing.Optional[User] # type: ignore # HttpRequests users "user" for it own, but we redefine it because base is not used...
|
user: typing.Optional[User] # type: ignore # HttpRequests users "user" for it own, but we redefine it because base is not used...
|
||||||
|
|
||||||
|
class ExtendedHttpRequestWithUser(ExtendedHttpRequest):
|
||||||
|
user: User
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_requests: typing.Dict[int, typing.Tuple[weakref.ref, datetime.datetime]] = {}
|
_requests: typing.Dict[int, typing.Tuple[weakref.ref, datetime.datetime]] = {}
|
||||||
|
@ -35,7 +35,8 @@ import logging
|
|||||||
# Utility imports
|
# Utility imports
|
||||||
from .util import getSqlDatetime, getSqlDatetimeAsUnix, NEVER, NEVER_UNIX
|
from .util import getSqlDatetime, getSqlDatetimeAsUnix, NEVER, NEVER_UNIX
|
||||||
|
|
||||||
# Imports all models so they are available for migrations
|
# Imports all models so they are available for migrations, etc..
|
||||||
|
from .managed_object_model import ManagedObjectModel
|
||||||
|
|
||||||
# Permissions
|
# Permissions
|
||||||
from .permissions import Permissions
|
from .permissions import Permissions
|
||||||
@ -104,7 +105,7 @@ from .account_usage import AccountUsage
|
|||||||
from .proxy import Proxy
|
from .proxy import Proxy
|
||||||
|
|
||||||
# Tagging
|
# Tagging
|
||||||
from .tag import Tag
|
from .tag import Tag, TaggingMixin
|
||||||
|
|
||||||
# Utility
|
# Utility
|
||||||
from .dbfile import DBFile
|
from .dbfile import DBFile
|
||||||
|
@ -138,7 +138,7 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
|
|||||||
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)
|
||||||
"""
|
"""
|
||||||
if chkDateTime is None:
|
if chkDateTime is None:
|
||||||
chkDateTime = typing.cast('datetime.datetime', getSqlDatetime())
|
chkDateTime = getSqlDatetime()
|
||||||
|
|
||||||
access = self.fallbackAccess
|
access = self.fallbackAccess
|
||||||
# Let's see if we can access by current datetime
|
# Let's see if we can access by current datetime
|
||||||
|
@ -151,7 +151,7 @@ class User(UUIDModel):
|
|||||||
"""
|
"""
|
||||||
if self.parent:
|
if self.parent:
|
||||||
try:
|
try:
|
||||||
usr = User.objects.prefetch_related('groups').get(uuid=self.parent)
|
usr = User.objects.prefetch_related('authenticator', 'groups').get(uuid=self.parent)
|
||||||
except Exception: # If parent do not exists
|
except Exception: # If parent do not exists
|
||||||
usr = self
|
usr = self
|
||||||
else:
|
else:
|
||||||
|
@ -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,
|
||||||
@ -35,7 +35,14 @@ from django.utils.translation import ugettext
|
|||||||
from django.utils import formats
|
from django.utils import formats
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from uds.models import ServicePool, Transport, Network, ServicePoolGroup, MetaPool, getSqlDatetime
|
from uds.models import (
|
||||||
|
ServicePool,
|
||||||
|
Transport,
|
||||||
|
Network,
|
||||||
|
ServicePoolGroup,
|
||||||
|
MetaPool,
|
||||||
|
getSqlDatetime,
|
||||||
|
)
|
||||||
from uds.core.util.config import GlobalConfig
|
from uds.core.util.config import GlobalConfig
|
||||||
from uds.core.util import html
|
from uds.core.util import html
|
||||||
|
|
||||||
@ -43,14 +50,18 @@ from uds.core.managers import userServiceManager
|
|||||||
|
|
||||||
# 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.core.util.request import ExtendedHttpRequest
|
from uds.core.util.request import ExtendedHttpRequestWithUser
|
||||||
from uds.core.util.tools import DictAsObj
|
from uds.core.util.tools import DictAsObj
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def getServicesData(request: 'ExtendedHttpRequest') -> typing.Dict[str, typing.Any]: # pylint: disable=too-many-locals, too-many-branches, too-many-statements
|
def getServicesData(
|
||||||
|
request: 'ExtendedHttpRequestWithUser',
|
||||||
|
) -> typing.Dict[
|
||||||
|
str, typing.Any
|
||||||
|
]: # pylint: disable=too-many-locals, too-many-branches, too-many-statements
|
||||||
"""Obtains the service data dictionary will all available services for this request
|
"""Obtains the service data dictionary will all available services for this request
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
@ -65,22 +76,22 @@ def getServicesData(request: 'ExtendedHttpRequest') -> typing.Dict[str, typing.A
|
|||||||
'autorun': autorun
|
'autorun': autorun
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Session data
|
|
||||||
os: 'DictAsObj' = request.os
|
|
||||||
if not request.user:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
# We look for services for this authenticator groups. User is logged in in just 1 authenticator, so his groups must coincide with those assigned to ds
|
# We look for services for this authenticator groups. User is logged in in just 1 authenticator, so his groups must coincide with those assigned to ds
|
||||||
groups = list(request.user.getGroups())
|
groups = list(request.user.getGroups())
|
||||||
availServicePools = list(ServicePool.getDeployedServicesForGroups(groups, request.user)) # Pass in user to get "number_assigned" to optimize
|
availServicePools = list(
|
||||||
availMetaPools = list(MetaPool.getForGroups(groups, request.user)) # Pass in user to get "number_assigned" to optimize
|
ServicePool.getDeployedServicesForGroups(groups, request.user)
|
||||||
|
) # Pass in user to get "number_assigned" to optimize
|
||||||
|
availMetaPools = list(
|
||||||
|
MetaPool.getForGroups(groups, request.user)
|
||||||
|
) # Pass in user to get "number_assigned" to optimize
|
||||||
now = getSqlDatetime()
|
now = getSqlDatetime()
|
||||||
|
|
||||||
# Information for administrators
|
# Information for administrators
|
||||||
nets = ''
|
nets = ''
|
||||||
validTrans = ''
|
validTrans = ''
|
||||||
|
|
||||||
logger.debug('OS: %s', os['OS'])
|
osName = request.os['OS']
|
||||||
|
logger.debug('OS: %s', osName)
|
||||||
|
|
||||||
if request.user.isStaff():
|
if request.user.isStaff():
|
||||||
nets = ','.join([n.name for n in Network.networksFor(request.ip)])
|
nets = ','.join([n.name for n in Network.networksFor(request.ip)])
|
||||||
@ -99,13 +110,20 @@ def getServicesData(request: 'ExtendedHttpRequest') -> typing.Dict[str, typing.A
|
|||||||
for meta in availMetaPools:
|
for meta in availMetaPools:
|
||||||
# Check that we have access to at least one transport on some of its children
|
# Check that we have access to at least one transport on some of its children
|
||||||
hasUsablePools = False
|
hasUsablePools = False
|
||||||
in_use = typing.cast(typing.Any, meta).number_in_use > 0 # Override, because we used hear annotations
|
in_use = (
|
||||||
|
typing.cast(typing.Any, meta).number_in_use > 0
|
||||||
|
) # Override, because we used hear annotations
|
||||||
for member in meta.members.all():
|
for member in meta.members.all():
|
||||||
# if pool.isInMaintenance():
|
# if pool.isInMaintenance():
|
||||||
# continue
|
# continue
|
||||||
for t in member.pool.transports.all():
|
for t in member.pool.transports.all():
|
||||||
typeTrans = t.getType()
|
typeTrans = t.getType()
|
||||||
if t.getType() and t.validForIp(request.ip) and typeTrans.supportsOs(os['OS']) and t.validForOs(os['OS']):
|
if (
|
||||||
|
t.getType()
|
||||||
|
and t.validForIp(request.ip)
|
||||||
|
and typeTrans.supportsOs(osName)
|
||||||
|
and t.validForOs(osName)
|
||||||
|
):
|
||||||
hasUsablePools = True
|
hasUsablePools = True
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -120,31 +138,39 @@ def getServicesData(request: 'ExtendedHttpRequest') -> typing.Dict[str, typing.A
|
|||||||
|
|
||||||
# If no usable pools, this is not visible
|
# If no usable pools, this is not visible
|
||||||
if hasUsablePools:
|
if hasUsablePools:
|
||||||
group = meta.servicesPoolGroup.as_dict if meta.servicesPoolGroup else ServicePoolGroup.default().as_dict
|
group = (
|
||||||
|
meta.servicesPoolGroup.as_dict
|
||||||
|
if meta.servicesPoolGroup
|
||||||
|
else ServicePoolGroup.default().as_dict
|
||||||
|
)
|
||||||
|
|
||||||
services.append({
|
services.append(
|
||||||
'id': 'M' + meta.uuid,
|
{
|
||||||
'name': meta.name,
|
'id': 'M' + meta.uuid,
|
||||||
'visual_name': meta.visual_name,
|
'name': meta.name,
|
||||||
'description': meta.comments,
|
'visual_name': meta.visual_name,
|
||||||
'group': group,
|
'description': meta.comments,
|
||||||
'transports': [{
|
'group': group,
|
||||||
'id': 'meta',
|
'transports': [
|
||||||
'name': 'meta',
|
{
|
||||||
'link': html.udsMetaLink(request, 'M' + meta.uuid),
|
'id': 'meta',
|
||||||
'priority': 0
|
'name': 'meta',
|
||||||
}],
|
'link': html.udsMetaLink(request, 'M' + meta.uuid),
|
||||||
'imageId': meta.image and meta.image.uuid or 'x',
|
'priority': 0,
|
||||||
'show_transports': False,
|
}
|
||||||
'allow_users_remove': False,
|
],
|
||||||
'allow_users_reset': False,
|
'imageId': meta.image and meta.image.uuid or 'x',
|
||||||
'maintenance': meta.isInMaintenance(),
|
'show_transports': False,
|
||||||
'not_accesible': not meta.isAccessAllowed(now),
|
'allow_users_remove': False,
|
||||||
'in_use': in_use,
|
'allow_users_reset': False,
|
||||||
'to_be_replaced': None,
|
'maintenance': meta.isInMaintenance(),
|
||||||
'to_be_replaced_text': '',
|
'not_accesible': not meta.isAccessAllowed(now),
|
||||||
'custom_calendar_text': meta.calendar_message,
|
'in_use': in_use,
|
||||||
})
|
'to_be_replaced': None,
|
||||||
|
'to_be_replaced_text': '',
|
||||||
|
'custom_calendar_text': meta.calendar_message,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Now generic user service
|
# Now generic user service
|
||||||
for svr in availServicePools:
|
for svr in availServicePools:
|
||||||
@ -155,23 +181,24 @@ def getServicesData(request: 'ExtendedHttpRequest') -> typing.Dict[str, typing.A
|
|||||||
use = str(svr.usage(typing.cast(typing.Any, svr).usage_count)) + '%'
|
use = str(svr.usage(typing.cast(typing.Any, svr).usage_count)) + '%'
|
||||||
|
|
||||||
trans = []
|
trans = []
|
||||||
for t in sorted(svr.transports.all(), key=lambda x: x.priority): # In memory sort, allows reuse prefetched and not too big array
|
for t in sorted(
|
||||||
|
svr.transports.all(), key=lambda x: x.priority
|
||||||
|
): # In memory sort, allows reuse prefetched and not too big array
|
||||||
try:
|
try:
|
||||||
typeTrans = t.getType()
|
typeTrans = t.getType()
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
if t.validForIp(request.ip) and typeTrans.supportsOs(os['OS']) and t.validForOs(os['OS']):
|
if (
|
||||||
|
t.validForIp(request.ip)
|
||||||
|
and typeTrans.supportsOs(osName)
|
||||||
|
and t.validForOs(osName)
|
||||||
|
):
|
||||||
if typeTrans.ownLink:
|
if typeTrans.ownLink:
|
||||||
link = reverse('TransportOwnLink', args=('F' + svr.uuid, t.uuid))
|
link = reverse('TransportOwnLink', args=('F' + svr.uuid, t.uuid))
|
||||||
else:
|
else:
|
||||||
link = html.udsAccessLink(request, 'F' + svr.uuid, t.uuid)
|
link = html.udsAccessLink(request, 'F' + svr.uuid, t.uuid)
|
||||||
trans.append(
|
trans.append(
|
||||||
{
|
{'id': t.uuid, 'name': t.name, 'link': link, 'priority': t.priority}
|
||||||
'id': t.uuid,
|
|
||||||
'name': t.name,
|
|
||||||
'link': link,
|
|
||||||
'priority': t.priority
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# If empty transports, do not include it on list
|
# If empty transports, do not include it on list
|
||||||
@ -190,47 +217,69 @@ def getServicesData(request: 'ExtendedHttpRequest') -> typing.Dict[str, typing.A
|
|||||||
# if ads:
|
# if ads:
|
||||||
# in_use = ads.in_use
|
# in_use = ads.in_use
|
||||||
|
|
||||||
group = svr.servicesPoolGroup.as_dict if svr.servicesPoolGroup else ServicePoolGroup.default().as_dict
|
group = (
|
||||||
|
svr.servicesPoolGroup.as_dict
|
||||||
|
if svr.servicesPoolGroup
|
||||||
|
else ServicePoolGroup.default().as_dict
|
||||||
|
)
|
||||||
|
|
||||||
# Only add toBeReplaced info in case we allow it. This will generate some "overload" on the services
|
# Only add toBeReplaced info in case we allow it. This will generate some "overload" on the services
|
||||||
toBeReplaced = svr.toBeReplaced(request.user) if typing.cast(
|
toBeReplaced = (
|
||||||
typing.Any, svr).pubs_active > 0 and GlobalConfig.NOTIFY_REMOVAL_BY_PUB.getBool(False) else None
|
svr.toBeReplaced(request.user)
|
||||||
|
if typing.cast(typing.Any, svr).pubs_active > 0
|
||||||
|
and GlobalConfig.NOTIFY_REMOVAL_BY_PUB.getBool(False)
|
||||||
|
else None
|
||||||
|
)
|
||||||
# tbr = False
|
# tbr = False
|
||||||
if toBeReplaced:
|
if toBeReplaced:
|
||||||
toBeReplaced = formats.date_format(toBeReplaced, "SHORT_DATETIME_FORMAT")
|
toBeReplaced = formats.date_format(toBeReplaced, "SHORT_DATETIME_FORMAT")
|
||||||
toBeReplacedTxt = ugettext(
|
toBeReplacedTxt = ugettext(
|
||||||
'This service is about to be replaced by a new version. Please, close the session before {} and save all your work to avoid loosing it.').format(toBeReplaced)
|
'This service is about to be replaced by a new version. Please, close the session before {} and save all your work to avoid loosing it.'
|
||||||
|
).format(toBeReplaced)
|
||||||
else:
|
else:
|
||||||
toBeReplacedTxt = ''
|
toBeReplacedTxt = ''
|
||||||
|
|
||||||
def datator(x): return x.replace('{use}', use).replace('{total}', str(svr.max_srvs))
|
def datator(x):
|
||||||
|
return x.replace('{use}', use).replace('{total}', str(svr.max_srvs))
|
||||||
|
|
||||||
services.append({
|
services.append(
|
||||||
'id': 'F' + svr.uuid,
|
{
|
||||||
'name': datator(svr.name),
|
'id': 'F' + svr.uuid,
|
||||||
'visual_name': datator(svr.visual_name.replace('{use}', use).replace('{total}', str(svr.max_srvs))),
|
'name': datator(svr.name),
|
||||||
'description': svr.comments,
|
'visual_name': datator(
|
||||||
'group': group,
|
svr.visual_name.replace('{use}', use).replace(
|
||||||
'transports': trans,
|
'{total}', str(svr.max_srvs)
|
||||||
'imageId': imageId,
|
)
|
||||||
'show_transports': svr.show_transports,
|
),
|
||||||
'allow_users_remove': svr.allow_users_remove,
|
'description': svr.comments,
|
||||||
'allow_users_reset': svr.allow_users_reset,
|
'group': group,
|
||||||
'maintenance': svr.isInMaintenance(),
|
'transports': trans,
|
||||||
'not_accesible': not svr.isAccessAllowed(now),
|
'imageId': imageId,
|
||||||
'in_use': in_use,
|
'show_transports': svr.show_transports,
|
||||||
'to_be_replaced': toBeReplaced,
|
'allow_users_remove': svr.allow_users_remove,
|
||||||
'to_be_replaced_text': toBeReplacedTxt,
|
'allow_users_reset': svr.allow_users_reset,
|
||||||
'custom_calendar_text': svr.calendar_message,
|
'maintenance': svr.isInMaintenance(),
|
||||||
})
|
'not_accesible': not svr.isAccessAllowed(now),
|
||||||
|
'in_use': in_use,
|
||||||
|
'to_be_replaced': toBeReplaced,
|
||||||
|
'to_be_replaced_text': toBeReplacedTxt,
|
||||||
|
'custom_calendar_text': svr.calendar_message,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# logger.debug('Services: %s', services)
|
# logger.debug('Services: %s', services)
|
||||||
|
|
||||||
# Sort services and remove services with no transports...
|
# Sort services and remove services with no transports...
|
||||||
services = [s for s in sorted(services, key=lambda s: s['name'].upper()) if s['transports']]
|
services = [
|
||||||
|
s for s in sorted(services, key=lambda s: s['name'].upper()) if s['transports']
|
||||||
|
]
|
||||||
|
|
||||||
autorun = False
|
autorun = False
|
||||||
if len(services) == 1 and GlobalConfig.AUTORUN_SERVICE.getBool(False) and services[0]['transports']:
|
if (
|
||||||
|
len(services) == 1
|
||||||
|
and GlobalConfig.AUTORUN_SERVICE.getBool(False)
|
||||||
|
and services[0]['transports']
|
||||||
|
):
|
||||||
if request.session.get('autorunDone', '0') == '0':
|
if request.session.get('autorunDone', '0') == '0':
|
||||||
request.session['autorunDone'] = '1'
|
request.session['autorunDone'] = '1'
|
||||||
autorun = True
|
autorun = True
|
||||||
@ -241,5 +290,5 @@ def getServicesData(request: 'ExtendedHttpRequest') -> typing.Dict[str, typing.A
|
|||||||
'ip': request.ip,
|
'ip': request.ip,
|
||||||
'nets': nets,
|
'nets': nets,
|
||||||
'transports': validTrans,
|
'transports': validTrans,
|
||||||
'autorun': autorun
|
'autorun': autorun,
|
||||||
}
|
}
|
||||||
|
@ -51,14 +51,16 @@ from uds.web.util import services
|
|||||||
|
|
||||||
# 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 django.http import HttpRequest # pylint: disable=ungrouped-imports
|
from uds.core.util.request import ExtendedHttpRequest, ExtendedHttpRequestWithUser
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@webLoginRequired(admin=False)
|
@webLoginRequired(admin=False)
|
||||||
def transportOwnLink(request: 'HttpRequest', idService: str, idTransport: str):
|
def transportOwnLink(request: 'ExtendedHttpRequestWithUser', idService: str, idTransport: str):
|
||||||
response: typing.MutableMapping[str, typing.Any] = {}
|
response: typing.MutableMapping[str, typing.Any] = {}
|
||||||
|
|
||||||
|
# For type checkers to "be happy"
|
||||||
try:
|
try:
|
||||||
res = userServiceManager().getService(request.user, request.os, request.ip, idService, idTransport)
|
res = userServiceManager().getService(request.user, request.os, request.ip, idService, idTransport)
|
||||||
ip, userService, iads, trans, itrans = res # pylint: disable=unused-variable
|
ip, userService, iads, trans, itrans = res # pylint: disable=unused-variable
|
||||||
@ -87,7 +89,7 @@ def transportOwnLink(request: 'HttpRequest', idService: str, idTransport: str):
|
|||||||
|
|
||||||
|
|
||||||
@cache_page(3600, key_prefix='img', cache='memory')
|
@cache_page(3600, key_prefix='img', cache='memory')
|
||||||
def transportIcon(request: 'HttpRequest', idTrans: str) -> HttpResponse:
|
def transportIcon(request: 'ExtendedHttpRequest', idTrans: str) -> HttpResponse:
|
||||||
try:
|
try:
|
||||||
transport: Transport = Transport.objects.get(uuid=processUuid(idTrans))
|
transport: Transport = Transport.objects.get(uuid=processUuid(idTrans))
|
||||||
return HttpResponse(transport.getInstance().icon(), content_type='image/png')
|
return HttpResponse(transport.getInstance().icon(), content_type='image/png')
|
||||||
@ -96,7 +98,7 @@ def transportIcon(request: 'HttpRequest', idTrans: str) -> HttpResponse:
|
|||||||
|
|
||||||
|
|
||||||
@cache_page(3600, key_prefix='img', cache='memory')
|
@cache_page(3600, key_prefix='img', cache='memory')
|
||||||
def serviceImage(request: 'HttpRequest', idImage: str) -> HttpResponse:
|
def serviceImage(request: 'ExtendedHttpRequest', idImage: str) -> HttpResponse:
|
||||||
try:
|
try:
|
||||||
icon = Image.objects.get(uuid=processUuid(idImage))
|
icon = Image.objects.get(uuid=processUuid(idImage))
|
||||||
return icon.imageResponse()
|
return icon.imageResponse()
|
||||||
@ -112,7 +114,7 @@ def serviceImage(request: 'HttpRequest', idImage: str) -> HttpResponse:
|
|||||||
|
|
||||||
@webLoginRequired(admin=False)
|
@webLoginRequired(admin=False)
|
||||||
@never_cache
|
@never_cache
|
||||||
def userServiceEnabler(request: 'HttpRequest', idService: str, idTransport: str) -> HttpResponse:
|
def userServiceEnabler(request: 'ExtendedHttpRequestWithUser', idService: str, idTransport: str) -> HttpResponse:
|
||||||
# Maybe we could even protect this even more by limiting referer to own server /? (just a meditation..)
|
# Maybe we could even protect this even more by limiting referer to own server /? (just a meditation..)
|
||||||
logger.debug('idService: %s, idTransport: %s', idService, idTransport)
|
logger.debug('idService: %s, idTransport: %s', idService, idTransport)
|
||||||
url = ''
|
url = ''
|
||||||
@ -166,12 +168,12 @@ def userServiceEnabler(request: 'HttpRequest', idService: str, idTransport: str)
|
|||||||
content_type='application/json'
|
content_type='application/json'
|
||||||
)
|
)
|
||||||
|
|
||||||
def closer(request: 'HttpRequest') -> HttpResponse:
|
def closer(request: 'ExtendedHttpRequest') -> HttpResponse:
|
||||||
return HttpResponse('<html><body onload="window.close()"></body></html>')
|
return HttpResponse('<html><body onload="window.close()"></body></html>')
|
||||||
|
|
||||||
@webLoginRequired(admin=False)
|
@webLoginRequired(admin=False)
|
||||||
@never_cache
|
@never_cache
|
||||||
def action(request: 'HttpRequest', idService: str, actionString: str) -> HttpResponse:
|
def action(request: 'ExtendedHttpRequestWithUser', idService: str, actionString: str) -> HttpResponse:
|
||||||
userService = userServiceManager().locateUserService(request.user, idService, create=False)
|
userService = userServiceManager().locateUserService(request.user, idService, create=False)
|
||||||
response: typing.Any = None
|
response: typing.Any = None
|
||||||
rebuild: bool = False
|
rebuild: bool = False
|
||||||
|
Loading…
x
Reference in New Issue
Block a user