Fixes (Basically formating & type checking fixes

This commit is contained in:
Adolfo Gómez García 2021-07-13 13:36:42 +02:00
parent d497235eeb
commit 13cbfe26c7
26 changed files with 590 additions and 419 deletions

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -83,13 +83,13 @@ class AutoAttributes(Serializable):
def __getattribute__(self, name):
if name.startswith('_') and name[1:] in self.attrs:
return self.attrs[name[1:]].getValue()
return object.__getattribute__(self, name)
return super().__getattribute__(name)
def __setattr__(self, name, value):
if name.startswith('_') and name[1:] in self.attrs:
self.attrs[name[1:]].setValue(value)
else:
object.__setattr__(self, name, value)
super().__setattr__(name, value)
def declare(self, **kwargs):
d = {}
@ -100,7 +100,15 @@ class AutoAttributes(Serializable):
def marshal(self) -> bytes:
for k, v in self.attrs.items():
logger.debug('Marshall Autoattributes: %s=%s', k, v.getValue())
return codecs.encode(b'\2'.join([b'%s\1%s' % (k.encode('utf8'), pickle.dumps(v, protocol=0)) for k, v in self.attrs.items()]), 'bz2')
return codecs.encode(
b'\2'.join(
[
b'%s\1%s' % (k.encode('utf8'), pickle.dumps(v, protocol=0))
for k, v in self.attrs.items()
]
),
'bz2',
)
def unmarshal(self, data: bytes) -> None:
if not data: # Can be empty

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -63,8 +63,15 @@ class Config:
HIDDEN_FIELD: int = 6 # Not visible on "admin" config edition
class Value:
def __init__(self, section: 'Config.Section', key: str, default: str = '', crypt: bool = False, longText: bool = False, **kwargs):
def __init__(
self,
section: 'Config.Section',
key: str,
default: str = '',
crypt: bool = False,
longText: bool = False,
**kwargs
):
logger.debug('Var: %s %s KWARGS: %s', section, key, kwargs)
self._type: int = kwargs.get('type', -1)
@ -92,9 +99,13 @@ class Config:
try:
if force or self._data is None:
# logger.debug('Accessing db config {0}.{1}'.format(self._section.name(), self._key))
readed = DBConfig.objects.get(section=self._section.name(), key=self._key) # @UndefinedVariable
readed = DBConfig.objects.get(
section=self._section.name(), key=self._key
) # @UndefinedVariable
self._data = readed.value
self._crypt = [self._crypt, True][readed.crypt] # True has "higher" precedende than False
self._crypt = [self._crypt, True][
readed.crypt
] # True has "higher" precedende than False
self._longText = readed.long
if self._type != -1 and self._type != readed.field_type:
readed.field_type = self._type
@ -119,11 +130,19 @@ class Config:
try:
return int(self.get(force))
except Exception:
logger.error('Value for %s.%s is invalid (integer expected)', self._section, self._key)
logger.error(
'Value for %s.%s is invalid (integer expected)',
self._section,
self._key,
)
try:
return int(self._default)
except Exception:
logger.error('Default value for %s.%s is also invalid (integer expected)', self._section, self._key)
logger.error(
'Default value for %s.%s is also invalid (integer expected)',
self._section,
self._key,
)
return -1
def getBool(self, force: bool = False) -> bool:
@ -159,17 +178,32 @@ class Config:
# Editable here means that this configuration value can be edited by admin directly (generally, that this is a "clean text" value)
logger.debug('Saving config %s.%s as %s', self._section.name(), self._key, value)
logger.debug(
'Saving config %s.%s as %s', self._section.name(), self._key, value
)
try:
obj, _ = DBConfig.objects.get_or_create(section=self._section.name(), key=self._key) # @UndefinedVariable
obj.value, obj.crypt, obj.long, obj.field_type = value, self._crypt, self._longText, self._type
obj, _ = DBConfig.objects.get_or_create(
section=self._section.name(), key=self._key
) # @UndefinedVariable
obj.value, obj.crypt, obj.long, obj.field_type = (
value,
self._crypt,
self._longText,
self._type,
)
obj.save()
except Exception:
if 'migrate' in sys.argv: # During migration, set could be saved as part of initialization...
if (
'migrate' in sys.argv
): # During migration, set could be saved as part of initialization...
return
logger.exception('Exception')
# Probably a migration issue, just ignore it
logger.info("Could not save configuration key %s.%s", self._section.name(), self._key)
logger.info(
"Could not save configuration key %s.%s",
self._section.name(),
self._key,
)
class Section:
def __init__(self, sectionName: str):
@ -192,7 +226,14 @@ class Config:
return Config.Section(sectionName)
@staticmethod
def value(section: Section, key: str, default: str, crypt: bool = False, longText: bool = False, **kwargs) -> 'Config.Value':
def value(
section: Section,
key: str,
default: str,
crypt: bool = False,
longText: bool = False,
**kwargs
) -> 'Config.Value':
return Config.Value(section, key, default, crypt, longText, **kwargs)
@staticmethod
@ -216,7 +257,9 @@ class Config:
def update(section, key, value, checkType=False) -> bool:
# If cfg value does not exists, simply ignore request
try:
cfg = DBConfig.objects.filter(section=section, key=key)[0] # @UndefinedVariable
cfg = DBConfig.objects.filter(section=section, key=key)[
0
] # @UndefinedVariable
if checkType and cfg.field_type in (Config.READ_FIELD, Config.HIDDEN_FIELD):
return False # Skip non writable elements
@ -234,121 +277,224 @@ class GlobalConfig:
"""
Simple helper to keep track of global configuration
"""
SESSION_EXPIRE_TIME: Config.Value = Config.section(GLOBAL_SECTION).value('sessionExpireTime', '24', type=Config.NUMERIC_FIELD) # Max session duration (in use) after a new publishment has been made
SESSION_EXPIRE_TIME: Config.Value = Config.section(GLOBAL_SECTION).value(
'sessionExpireTime', '24', type=Config.NUMERIC_FIELD
) # Max session duration (in use) after a new publishment has been made
# Delay between cache checks. reducing this number will increase cache generation speed but also will load service providers
CACHE_CHECK_DELAY: Config.Value = Config.section(GLOBAL_SECTION).value('cacheCheckDelay', '19', type=Config.NUMERIC_FIELD)
CACHE_CHECK_DELAY: Config.Value = Config.section(GLOBAL_SECTION).value(
'cacheCheckDelay', '19', type=Config.NUMERIC_FIELD
)
# Delayed task number of threads PER SERVER, with higher number of threads, deplayed task will complete sooner, but it will give more load to overall system
DELAYED_TASKS_THREADS: Config.Value = Config.section(GLOBAL_SECTION).value('delayedTasksThreads', '4', type=Config.NUMERIC_FIELD)
DELAYED_TASKS_THREADS: Config.Value = Config.section(GLOBAL_SECTION).value(
'delayedTasksThreads', '4', type=Config.NUMERIC_FIELD
)
# Number of scheduler threads running PER SERVER, with higher number of threads, deplayed task will complete sooner, but it will give more load to overall system
SCHEDULER_THREADS: Config.Value = Config.section(GLOBAL_SECTION).value('schedulerThreads', '3', type=Config.NUMERIC_FIELD)
SCHEDULER_THREADS: Config.Value = Config.section(GLOBAL_SECTION).value(
'schedulerThreads', '3', type=Config.NUMERIC_FIELD
)
# Waiting time before removing "errored" and "removed" publications, cache, and user assigned machines. Time is in seconds
CLEANUP_CHECK: Config.Value = Config.section(GLOBAL_SECTION).value('cleanupCheck', '3607', type=Config.NUMERIC_FIELD)
CLEANUP_CHECK: Config.Value = Config.section(GLOBAL_SECTION).value(
'cleanupCheck', '3607', type=Config.NUMERIC_FIELD
)
# Time to maintaing "info state" items before removing it, in seconds
KEEP_INFO_TIME: Config.Value = Config.section(GLOBAL_SECTION).value('keepInfoTime', '14401', type=Config.NUMERIC_FIELD) # Defaults to 2 days 172800?? better 4 hours xd
KEEP_INFO_TIME: Config.Value = Config.section(GLOBAL_SECTION).value(
'keepInfoTime', '14401', type=Config.NUMERIC_FIELD
) # Defaults to 2 days 172800?? better 4 hours xd
# Max number of services to be "preparing" at same time
MAX_PREPARING_SERVICES: Config.Value = Config.section(GLOBAL_SECTION).value('maxPreparingServices', '15', type=Config.NUMERIC_FIELD) # Defaults to 15 services at once (per service provider)
MAX_PREPARING_SERVICES: Config.Value = Config.section(GLOBAL_SECTION).value(
'maxPreparingServices', '15', type=Config.NUMERIC_FIELD
) # Defaults to 15 services at once (per service provider)
# Max number of service to be at "removal" state at same time
MAX_REMOVING_SERVICES: Config.Value = Config.section(GLOBAL_SECTION).value('maxRemovingServices', '15', type=Config.NUMERIC_FIELD) # Defaults to 15 services at once (per service provider)
MAX_REMOVING_SERVICES: Config.Value = Config.section(GLOBAL_SECTION).value(
'maxRemovingServices', '15', type=Config.NUMERIC_FIELD
) # Defaults to 15 services at once (per service provider)
# If we ignore limits (max....)
IGNORE_LIMITS: Config.Value = Config.section(GLOBAL_SECTION).value('ignoreLimits', '0', type=Config.BOOLEAN_FIELD)
IGNORE_LIMITS: Config.Value = Config.section(GLOBAL_SECTION).value(
'ignoreLimits', '0', type=Config.BOOLEAN_FIELD
)
# Number of services to initiate removal per run of CacheCleaner
USER_SERVICE_CLEAN_NUMBER: Config.Value = Config.section(GLOBAL_SECTION).value('userServiceCleanNumber', '8', type=Config.NUMERIC_FIELD) # Defaults to 3 per wun
USER_SERVICE_CLEAN_NUMBER: Config.Value = Config.section(GLOBAL_SECTION).value(
'userServiceCleanNumber', '8', type=Config.NUMERIC_FIELD
) # Defaults to 3 per wun
# Removal Check time for cache, publications and deployed services
REMOVAL_CHECK: Config.Value = Config.section(GLOBAL_SECTION).value('removalCheck', '31', type=Config.NUMERIC_FIELD) # Defaults to 30 seconds
REMOVAL_CHECK: Config.Value = Config.section(GLOBAL_SECTION).value(
'removalCheck', '31', type=Config.NUMERIC_FIELD
) # Defaults to 30 seconds
# Login URL: deprecated & not used anymore
# LOGIN_URL: Config.Value = Config.section(GLOBAL_SECTION).value('loginUrl', '/uds/page/login', type=Config.TEXT_FIELD) # Defaults to /login
# Session duration
# USER_SESSION_LENGTH: Config.Value = Config.section(SECURITY_SECTION).value('userSessionLength', '14400', type=Config.NUMERIC_FIELD) # Defaults to 4 hours
# Superuser (do not need to be at database!!!)
SUPER_USER_LOGIN: Config.Value = Config.section(SECURITY_SECTION).value('superUser', 'root', type=Config.TEXT_FIELD)
SUPER_USER_LOGIN: Config.Value = Config.section(SECURITY_SECTION).value(
'superUser', 'root', type=Config.TEXT_FIELD
)
# Superuser password (do not need to be at database!!!)
SUPER_USER_PASS: Config.Value = Config.section(SECURITY_SECTION).valueCrypt('rootPass', 'udsmam0', type=Config.TEXT_FIELD)
SUPER_USER_PASS: Config.Value = Config.section(SECURITY_SECTION).valueCrypt(
'rootPass', 'udsmam0', type=Config.TEXT_FIELD
)
# Idle time before closing session on admin
SUPER_USER_ALLOW_WEBACCESS: Config.Value = Config.section(SECURITY_SECTION).value('allowRootWebAccess', '1', type=Config.BOOLEAN_FIELD)
SUPER_USER_ALLOW_WEBACCESS: Config.Value = Config.section(SECURITY_SECTION).value(
'allowRootWebAccess', '1', type=Config.BOOLEAN_FIELD
)
# Time an admi session can be idle before being "logged out"
# ADMIN_IDLE_TIME: Config.Value = Config.section(SECURITY_SECTION).value('adminIdleTime', '14400', type=Config.NUMERIC_FIELD) # Defaults to 4 hous
# Time betwen checks of unused services by os managers
# Unused services will be invoked for every machine assigned but not in use AND that has been assigned at least this time
# (only if os manager asks for this characteristic)
CHECK_UNUSED_TIME: Config.Value = Config.section(GLOBAL_SECTION).value('checkUnusedTime', '631', type=Config.NUMERIC_FIELD) # Defaults to 10 minutes
CHECK_UNUSED_DELAY: Config.Value = Config.section(GLOBAL_SECTION).value('checkUnusedDelay', '300', type=Config.NUMERIC_FIELD) # Defaults to 10 minutes
CHECK_UNUSED_TIME: Config.Value = Config.section(GLOBAL_SECTION).value(
'checkUnusedTime', '631', type=Config.NUMERIC_FIELD
) # Defaults to 10 minutes
CHECK_UNUSED_DELAY: Config.Value = Config.section(GLOBAL_SECTION).value(
'checkUnusedDelay', '300', type=Config.NUMERIC_FIELD
) # Defaults to 10 minutes
# Default CSS Used: REMOVED! (keep the for for naw, for reference, but will be cleaned on future...)
# CSS: Config.Value = Config.section(GLOBAL_SECTION).value('css', settings.STATIC_URL + 'css/uds.css', type=Config.TEXT_FIELD)
# Max logins before blocking an account
MAX_LOGIN_TRIES: Config.Value = Config.section(SECURITY_SECTION).value('maxLoginTries', '5', type=Config.NUMERIC_FIELD)
MAX_LOGIN_TRIES: Config.Value = Config.section(SECURITY_SECTION).value(
'maxLoginTries', '5', type=Config.NUMERIC_FIELD
)
# Block time in second for an user that makes too many mistakes, 5 minutes default
LOGIN_BLOCK: Config.Value = Config.section(SECURITY_SECTION).value('loginBlockTime', '300', type=Config.NUMERIC_FIELD)
LOGIN_BLOCK_IP: Config.Value = Config.section(SECURITY_SECTION).value('Block ip on login failure', '0', type=Config.BOOLEAN_FIELD)
LOGIN_BLOCK: Config.Value = Config.section(SECURITY_SECTION).value(
'loginBlockTime', '300', type=Config.NUMERIC_FIELD
)
LOGIN_BLOCK_IP: Config.Value = Config.section(SECURITY_SECTION).value(
'Block ip on login failure', '0', type=Config.BOOLEAN_FIELD
)
# Do autorun of service if just one service.
# 0 = No autorun, 1 = Autorun at login
# In a future, maybe necessary another value "2" that means that autorun always
AUTORUN_SERVICE: Config.Value = Config.section(GLOBAL_SECTION).value('autorunService', '0', type=Config.BOOLEAN_FIELD)
AUTORUN_SERVICE: Config.Value = Config.section(GLOBAL_SECTION).value(
'autorunService', '0', type=Config.BOOLEAN_FIELD
)
# Redirect HTTP to HTTPS
REDIRECT_TO_HTTPS: Config.Value = Config.section(GLOBAL_SECTION).value('redirectToHttps', '0', type=Config.BOOLEAN_FIELD)
REDIRECT_TO_HTTPS: Config.Value = Config.section(GLOBAL_SECTION).value(
'redirectToHttps', '0', type=Config.BOOLEAN_FIELD
)
# Max time needed to get a service "fully functional" before it's considered "failed" and removed
# The time is in seconds
MAX_INITIALIZING_TIME: Config.Value = Config.section(GLOBAL_SECTION).value('maxInitTime', '3601', type=Config.NUMERIC_FIELD)
MAX_REMOVAL_TIME: Config.Value = Config.section(GLOBAL_SECTION).value('maxRemovalTime', '86400', type=Config.NUMERIC_FIELD)
MAX_INITIALIZING_TIME: Config.Value = Config.section(GLOBAL_SECTION).value(
'maxInitTime', '3601', type=Config.NUMERIC_FIELD
)
MAX_REMOVAL_TIME: Config.Value = Config.section(GLOBAL_SECTION).value(
'maxRemovalTime', '86400', type=Config.NUMERIC_FIELD
)
# Maximum logs per user service
MAX_LOGS_PER_ELEMENT: Config.Value = Config.section(GLOBAL_SECTION).value('maxLogPerElement', '100', type=Config.NUMERIC_FIELD)
MAX_LOGS_PER_ELEMENT: Config.Value = Config.section(GLOBAL_SECTION).value(
'maxLogPerElement', '100', type=Config.NUMERIC_FIELD
)
# Time to restrain a deployed service in case it gives some errors at some point
RESTRAINT_TIME: Config.Value = Config.section(GLOBAL_SECTION).value('restrainTime', '600', type=Config.NUMERIC_FIELD)
RESTRAINT_TIME: Config.Value = Config.section(GLOBAL_SECTION).value(
'restrainTime', '600', type=Config.NUMERIC_FIELD
)
# Number of errors that must occurr in RESTRAIN_TIME to restrain deployed service
RESTRAINT_COUNT: Config.Value = Config.section(GLOBAL_SECTION).value('restrainCount', '3', type=Config.NUMERIC_FIELD)
RESTRAINT_COUNT: Config.Value = Config.section(GLOBAL_SECTION).value(
'restrainCount', '3', type=Config.NUMERIC_FIELD
)
# Statistics duration, in days
STATS_DURATION: Config.Value = Config.section(GLOBAL_SECTION).value('statsDuration', '365', type=Config.NUMERIC_FIELD)
STATS_DURATION: Config.Value = Config.section(GLOBAL_SECTION).value(
'statsDuration', '365', type=Config.NUMERIC_FIELD
)
# If disallow login using /login url, and must go to an authenticator
DISALLOW_GLOBAL_LOGIN: Config.Value = Config.section(GLOBAL_SECTION).value('disallowGlobalLogin', '0', type=Config.BOOLEAN_FIELD)
DISALLOW_GLOBAL_LOGIN: Config.Value = Config.section(GLOBAL_SECTION).value(
'disallowGlobalLogin', '0', type=Config.BOOLEAN_FIELD
)
# Allos preferences access to users
NOTIFY_REMOVAL_BY_PUB: Config.Value = Config.section(GLOBAL_SECTION).value('Notify on new publication', '0', type=Config.BOOLEAN_FIELD)
NOTIFY_REMOVAL_BY_PUB: Config.Value = Config.section(GLOBAL_SECTION).value(
'Notify on new publication', '0', type=Config.BOOLEAN_FIELD
)
# Allowed "trusted sources" for request
TRUSTED_SOURCES: Config.Value = Config.section(SECURITY_SECTION).value('Trusted Hosts', '*', type=Config.TEXT_FIELD)
TRUSTED_SOURCES: Config.Value = Config.section(SECURITY_SECTION).value(
'Trusted Hosts', '*', type=Config.TEXT_FIELD
)
# Allow clients to notify their own ip (if set), or use always the request extracted IP
HONOR_CLIENT_IP_NOTIFY: Config.Value = Config.section(SECURITY_SECTION).value('honorClientNotifyIP', '0', type=Config.BOOLEAN_FIELD)
HONOR_CLIENT_IP_NOTIFY: Config.Value = Config.section(SECURITY_SECTION).value(
'honorClientNotifyIP', '0', type=Config.BOOLEAN_FIELD
)
# If there is a proxy in front of us
BEHIND_PROXY: Config.Value = Config.section(SECURITY_SECTION).value('Behind a proxy', '0', type=Config.BOOLEAN_FIELD)
BEHIND_PROXY: Config.Value = Config.section(SECURITY_SECTION).value(
'Behind a proxy', '0', type=Config.BOOLEAN_FIELD
)
# If we use new logout mechanics
EXCLUSIVE_LOGOUT: Config.Value = Config.section(SECURITY_SECTION).value('Exclusive Logout', '0', type=Config.BOOLEAN_FIELD)
EXCLUSIVE_LOGOUT: Config.Value = Config.section(SECURITY_SECTION).value(
'Exclusive Logout', '0', type=Config.BOOLEAN_FIELD
)
# Enable/Disable Actor attack detection ip blocking
BLOCK_ACTOR_FAILURES: Config.Value = Config.section(SECURITY_SECTION).value('Block actor failures', '1', type=Config.BOOLEAN_FIELD)
BLOCK_ACTOR_FAILURES: Config.Value = Config.section(SECURITY_SECTION).value(
'Block actor failures', '1', type=Config.BOOLEAN_FIELD
)
# Max session length configuration values
SESSION_DURATION_ADMIN: Config.Value = Config.section(SECURITY_SECTION).value('Session timeout for Admin', '14400', type=Config.NUMERIC_FIELD)
SESSION_DURATION_USER: Config.Value = Config.section(SECURITY_SECTION).value('Session timeout for User', '14400', type=Config.NUMERIC_FIELD)
SESSION_DURATION_ADMIN: Config.Value = Config.section(SECURITY_SECTION).value(
'Session timeout for Admin', '14400', type=Config.NUMERIC_FIELD
)
SESSION_DURATION_USER: Config.Value = Config.section(SECURITY_SECTION).value(
'Session timeout for User', '14400', type=Config.NUMERIC_FIELD
)
RELOAD_TIME: Config.Value = Config.section(GLOBAL_SECTION).value('Page reload Time', '300', type=Config.NUMERIC_FIELD)
RELOAD_TIME: Config.Value = Config.section(GLOBAL_SECTION).value(
'Page reload Time', '300', type=Config.NUMERIC_FIELD
)
LIMITED_BY_CALENDAR_TEXT: Config.Value = Config.section(GLOBAL_SECTION).value('Calendar access denied text', '', type=Config.TEXT_FIELD) # Defaults to Nothing
LIMITED_BY_CALENDAR_TEXT: Config.Value = Config.section(GLOBAL_SECTION).value(
'Calendar access denied text', '', type=Config.TEXT_FIELD
) # Defaults to Nothing
# Custom message for error when limiting by calendar
# If convert username to lowercase
LOWERCASE_USERNAME: Config.Value = Config.section(SECURITY_SECTION).value('Convert username to lowercase', '1', type=Config.BOOLEAN_FIELD)
LOWERCASE_USERNAME: Config.Value = Config.section(SECURITY_SECTION).value(
'Convert username to lowercase', '1', type=Config.BOOLEAN_FIELD
)
# Global UDS ID (common for all servers on the same cluster)
UDS_ID: Config.Value = Config.section(GLOBAL_SECTION).value('UDS ID', cryptoManager().uuid(), type=Config.READ_FIELD)
UDS_ID: Config.Value = Config.section(GLOBAL_SECTION).value(
'UDS ID', cryptoManager().uuid(), type=Config.READ_FIELD
)
# Site display name & copyright info
SITE_NAME: Config.Value = Config.section(CUSTOM_SECTION).value('Site name', 'UDS Enterprise', type=Config.TEXT_FIELD)
SITE_COPYRIGHT: Config.Value = Config.section(CUSTOM_SECTION).value('Site copyright info', '© Virtual Cable S.L.U.', type=Config.TEXT_FIELD)
SITE_COPYRIGHT_LINK: Config.Value = Config.section(CUSTOM_SECTION).value('Site copyright link', 'https://www.udsenterprise.com', type=Config.TEXT_FIELD)
SITE_LOGO_NAME: Config.Value = Config.section(CUSTOM_SECTION).value('Logo name', 'UDS', type=Config.TEXT_FIELD)
SITE_CSS: Config.Value = Config.section(CUSTOM_SECTION).value('CSS', '', type=Config.LONGTEXT_FIELD)
SITE_INFO: Config.Value = Config.section(CUSTOM_SECTION).value('Site information', '', type=Config.LONGTEXT_FIELD)
SITE_FILTER_ONTOP: Config.Value = Config.section(CUSTOM_SECTION).value('Show Filter on Top', '0', type=Config.BOOLEAN_FIELD)
SITE_FILTER_MIN: Config.Value = Config.section(CUSTOM_SECTION).value('Min. Services to show filter', '8', type=Config.NUMERIC_FIELD)
SITE_NAME: Config.Value = Config.section(CUSTOM_SECTION).value(
'Site name', 'UDS Enterprise', type=Config.TEXT_FIELD
)
SITE_COPYRIGHT: Config.Value = Config.section(CUSTOM_SECTION).value(
'Site copyright info', '© Virtual Cable S.L.U.', type=Config.TEXT_FIELD
)
SITE_COPYRIGHT_LINK: Config.Value = Config.section(CUSTOM_SECTION).value(
'Site copyright link', 'https://www.udsenterprise.com', type=Config.TEXT_FIELD
)
SITE_LOGO_NAME: Config.Value = Config.section(CUSTOM_SECTION).value(
'Logo name', 'UDS', type=Config.TEXT_FIELD
)
SITE_CSS: Config.Value = Config.section(CUSTOM_SECTION).value(
'CSS', '', type=Config.LONGTEXT_FIELD
)
SITE_INFO: Config.Value = Config.section(CUSTOM_SECTION).value(
'Site information', '', type=Config.LONGTEXT_FIELD
)
SITE_FILTER_ONTOP: Config.Value = Config.section(CUSTOM_SECTION).value(
'Show Filter on Top', '0', type=Config.BOOLEAN_FIELD
)
SITE_FILTER_MIN: Config.Value = Config.section(CUSTOM_SECTION).value(
'Min. Services to show filter', '8', type=Config.NUMERIC_FIELD
)
EXPERIMENTAL_FEATURES: Config.Value = Config.section(GLOBAL_SECTION).value('Experimental Features', '0', type=Config.BOOLEAN_FIELD)
EXPERIMENTAL_FEATURES: Config.Value = Config.section(GLOBAL_SECTION).value(
'Experimental Features', '0', type=Config.BOOLEAN_FIELD
)
# Admin config variables
ADMIN_PAGESIZE: Config.Value = Config.section(ADMIN_SECTION).value('List page size', '10', type=Config.NUMERIC_FIELD)
ADMIN_TRUSTED_SOURCES: Config.Value = Config.section(ADMIN_SECTION).value('Trusted Hosts for Admin', '*', type=Config.TEXT_FIELD)
ADMIN_PAGESIZE: Config.Value = Config.section(ADMIN_SECTION).value(
'List page size', '10', type=Config.NUMERIC_FIELD
)
ADMIN_TRUSTED_SOURCES: Config.Value = Config.section(ADMIN_SECTION).value(
'Trusted Hosts for Admin', '*', type=Config.TEXT_FIELD
)
_initDone = False
@ -381,4 +527,6 @@ class GlobalConfig:
# GlobalConfig.UDS_THEME.setParams(['html5', 'semantic'])
except Exception:
logger.debug('Config table do not exists!!!, maybe we are installing? :-)')
logger.debug(
'Config table do not exists!!!, maybe we are installing? :-)'
)

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -39,10 +39,14 @@ logger = logging.getLogger(__name__)
def testServer(host: str, port: typing.Union[int, str], timeOut: float = 4) -> bool:
try:
logger.debug('Checking connection to %s:%s with %s seconds timeout', host, port, timeOut)
logger.debug(
'Checking connection to %s:%s with %s seconds timeout', host, port, timeOut
)
sock = socket.create_connection((host, int(port)), timeOut)
sock.close()
except Exception as e:
logger.debug('Exception checking %s:%s with %s timeout: %s', host, port, timeOut, e)
logger.debug(
'Exception checking %s:%s with %s timeout: %s', host, port, timeOut, e
)
return False
return True

View File

@ -1,43 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
"""
# Default values
STYLESHEET_DEFAULT = '''
.mat-toolbar.mat-primary {
background-color: #455A64;
}
'''
ICON_DEFAULT = '''
'''

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -47,7 +47,9 @@ RT = typing.TypeVar('RT')
# Default is to deny IE < 9
def denyBrowsers(
browsers: typing.Optional[typing.List[str]] = None,
errorResponse: typing.Callable = lambda request: errors.errorView(request, errors.BROWSER_NOT_SUPPORTED)
errorResponse: typing.Callable = lambda request: errors.errorView(
request, errors.BROWSER_NOT_SUPPORTED
),
) -> typing.Callable[[typing.Callable[..., RT]], typing.Callable[..., RT]]:
"""
Decorator to set protection to access page
@ -82,7 +84,12 @@ def deprecated(func: typing.Callable[..., RT]) -> typing.Callable[..., RT]:
def new_func(*args, **kwargs) -> RT:
try:
caller = inspect.stack()[1]
logger.warning('Call to deprecated function %s from %s:%s.', func.__name__, caller[1], caller[2])
logger.warning(
'Call to deprecated function %s from %s:%s.',
func.__name__,
caller[1],
caller[2],
)
except Exception:
logger.info('No stack info on deprecated function call %s', func.__name__)
@ -92,8 +99,8 @@ def deprecated(func: typing.Callable[..., RT]) -> typing.Callable[..., RT]:
def ensureConected(func: typing.Callable[..., RT]) -> typing.Callable[..., RT]:
"""This decorator calls "connect" method of the class of the wrapped object
"""
"""This decorator calls "connect" method of the class of the wrapped object"""
@wraps(func)
def new_func(*args, **kwargs) -> RT:
args[0].connect()
@ -102,7 +109,6 @@ def ensureConected(func: typing.Callable[..., RT]) -> typing.Callable[..., RT]:
return new_func
# Decorator that allows us a "fast&clean" caching system on service providers
#
# Decorator for caching
@ -110,9 +116,13 @@ def ensureConected(func: typing.Callable[..., RT]) -> typing.Callable[..., RT]:
def allowCache(
cachePrefix: str,
cacheTimeout: int,
cachingArgs: typing.Optional[typing.Union[typing.List[int], typing.Tuple[int], int]] = None,
cachingKWArgs: typing.Optional[typing.Union[typing.List[str], typing.Tuple[str], str]] = None,
cachingKeyFnc: typing.Optional[typing.Callable[[typing.Any], str]] = None
cachingArgs: typing.Optional[
typing.Union[typing.List[int], typing.Tuple[int], int]
] = None,
cachingKWArgs: typing.Optional[
typing.Union[typing.List[str], typing.Tuple[str], str]
] = None,
cachingKeyFnc: typing.Optional[typing.Callable[[typing.Any], str]] = None,
) -> typing.Callable[[typing.Callable[..., RT]], typing.Callable[..., RT]]:
"""Decorator that give us a "quick& clean" caching feature on service providers.
@ -130,11 +140,19 @@ def allowCache(
def wrapper(*args, **kwargs) -> RT:
argList: typing.List[str] = []
if cachingArgs:
ar = [cachingArgs] if not isinstance(cachingArgs, (list, tuple)) else cachingArgs
ar = (
[cachingArgs]
if not isinstance(cachingArgs, (list, tuple))
else cachingArgs
)
argList = [args[i] if i < len(args) else '' for i in ar]
if cachingKWArgs:
kw = [cachingKWArgs] if not isinstance(cachingKWArgs, (list, tuple)) else cachingKWArgs
kw = (
[cachingKWArgs]
if not isinstance(cachingKWArgs, (list, tuple))
else cachingKWArgs
)
argList += [str(kwargs.get(i, '')) for i in kw]
if argList:
@ -156,7 +174,13 @@ def allowCache(
# Maybe returned data is not serializable. In that case, cache will fail but no harm is done with this
args[0].cache.put(cacheKey, data, cacheTimeout)
except Exception as e:
logger.debug('Data for %s is not serializable on call to %s, not cached. %s (%s)', cacheKey, fnc.__name__, data, e)
logger.debug(
'Data for %s is not serializable on call to %s, not cached. %s (%s)',
cacheKey,
fnc.__name__,
data,
e,
)
return data
return wrapper

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016-2019 Virtual Cable S.L.
# Copyright (c) 2016-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -112,14 +112,16 @@ class FileStorage(Storage):
if self.cache is None:
return
dbf = DictAsObj({
dbf = DictAsObj(
{
'name': f.name,
'uuid': f.uuid,
'size': f.size,
'data': f.data,
'created': f.created,
'modified': f.modified
})
'modified': f.modified,
}
)
self.cache.set(self._getKey(f.name), dbf, 3600) # Cache defaults to one hour
@ -131,7 +133,6 @@ class FileStorage(Storage):
def _open(self, name, mode='rb'):
f = io.BytesIO(self._dbFileForReadOnly(name).data)
f.name = name
f.mode = mode
return File(f)
def _save(self, name, content):
@ -140,7 +141,9 @@ class FileStorage(Storage):
f = self._dbFileForReadWrite(name)
except DBFile.DoesNotExist:
now = getSqlDatetime()
f = DBFile.objects.create(owner=self.owner, name=name, created=now, modified=now)
f = DBFile.objects.create(
owner=self.owner, name=name, created=now, modified=now
)
f.data = content.read()
f.modified = getSqlDatetime()

View File

@ -59,10 +59,14 @@ def udsLink(request: 'HttpRequest', ticket: str, scrambler: str) -> str:
else:
proto = 'uds'
return "{}://{}{}/{}".format(proto, request.build_absolute_uri('/').split('//')[1], ticket, scrambler)
return "{}://{}{}/{}".format(
proto, request.build_absolute_uri('/').split('//')[1], ticket, scrambler
)
def udsAccessLink(request: 'HttpRequest', serviceId: str, transportId: typing.Optional[str]) -> str:
def udsAccessLink(
request: 'HttpRequest', serviceId: str, transportId: typing.Optional[str]
) -> str:
'''
If transportId (uuid) is None, this will be a metaLink
'''
@ -73,7 +77,12 @@ def parseDate(dateToParse) -> datetime.date:
if get_language() == 'fr':
date_format = '%d/%m/%Y'
else:
date_format = formats.get_format('SHORT_DATE_FORMAT').replace('Y', '%Y').replace('m', '%m').replace('d', '%d') # pylint: disable=maybe-no-member
date_format = (
formats.get_format('SHORT_DATE_FORMAT')
.replace('Y', '%Y')
.replace('m', '%m')
.replace('d', '%d')
) # pylint: disable=maybe-no-member
return datetime.datetime.strptime(dateToParse, date_format).date()

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -43,12 +43,30 @@ logger = logging.getLogger(__name__)
useLogger = logging.getLogger('useLog')
# Logging levels
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6)) # @UndefinedVariable
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (
10000 * (x + 1) for x in range(6)
) # @UndefinedVariable
# Logging sources
INTERNAL, ACTOR, TRANSPORT, OSMANAGER, UNKNOWN, WEB, ADMIN, SERVICE = ('internal', 'actor', 'transport', 'osmanager', 'unknown', 'web', 'admin', 'service')
INTERNAL, ACTOR, TRANSPORT, OSMANAGER, UNKNOWN, WEB, ADMIN, SERVICE = (
'internal',
'actor',
'transport',
'osmanager',
'unknown',
'web',
'admin',
'service',
)
OTHERSTR, DEBUGSTR, INFOSTR, WARNSTR, ERRORSTR, FATALSTR = ('OTHER', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL')
OTHERSTR, DEBUGSTR, INFOSTR, WARNSTR, ERRORSTR, FATALSTR = (
'OTHER',
'DEBUG',
'INFO',
'WARN',
'ERROR',
'FATAL',
)
# Names for defined log levels
__nameLevels = {
@ -57,7 +75,7 @@ __nameLevels = {
WARNSTR: WARN,
ERRORSTR: ERROR,
FATALSTR: FATAL,
OTHERSTR: OTHER
OTHERSTR: OTHER,
}
# Reverse dict of names
@ -83,7 +101,7 @@ def useLog(
srcIP: typing.Optional[str] = None,
srcUser: typing.Optional[str] = None,
userServiceName: typing.Optional[str] = None,
poolName: typing.Optional[str] = None
poolName: typing.Optional[str] = None,
) -> None:
"""
Logs an "use service" event (logged from actors)
@ -99,7 +117,20 @@ def useLog(
userServiceName = 'unknown' if userServiceName is None else userServiceName
poolName = 'unknown' if poolName is None else poolName
useLogger.info('|'.join([type_, serviceUniqueId, serviceIp, srcIP, srcUser, username, userServiceName, poolName]))
useLogger.info(
'|'.join(
[
type_,
serviceUniqueId,
serviceIp,
srcIP,
srcUser,
username,
userServiceName,
poolName,
]
)
)
def doLog(
@ -107,13 +138,15 @@ def doLog(
level: int,
message: str,
source: str = UNKNOWN,
avoidDuplicates: bool = True
avoidDuplicates: bool = True,
) -> None:
logger.debug('%s %s %s', wichObject, level, message)
logManager().doLog(wichObject, level, message, source, avoidDuplicates)
def getLogs(wichObject: 'Model', limit: typing.Optional[int] = None) -> typing.List[typing.Dict]:
def getLogs(
wichObject: 'Model', limit: typing.Optional[int] = None
) -> typing.List[typing.Dict]:
"""
Get the logs associated with "wichObject", limiting to "limit" (default is GlobalConfig.MAX_LOGS_PER_ELEMENT)
"""

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2020 Virtual Cable S.L.
# Copyright (c) 2014-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -43,10 +43,10 @@ import uds.dispatchers # pylint: disable=unused-import
logger = logging.getLogger(__name__)
patterns: typing.List = []
patterns: typing.List[typing.Any] = []
def loadModulesUrls():
def loadModulesUrls() -> typing.List[typing.Any]:
logger.debug('Looking for dispatching modules')
if not patterns:
logger.debug('Looking for patterns')
@ -56,11 +56,11 @@ def loadModulesUrls():
for _, name, _ in pkgutil.iter_modules([pkgpath]):
fullModName = '{}.{}.urls'.format(modName, name)
try:
# mod = __import__(fullModName, globals(), locals(), ['urlpatterns'], 0)
mod = importlib.import_module(fullModName)
logger.debug('Loaded mod %s, url %s', mod, mod.urlpatterns)
urlpatterns: typing.List[typing.Any] = getattr(mod, 'urlpatterns')
logger.debug('Loaded mod %s, url %s', mod, urlpatterns)
# Append patters from mod
for up in mod.urlpatterns:
for up in urlpatterns:
patterns.append(up)
except Exception:
logger.exception('Loading patterns')

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Virtual Cable S.L.
# Copyright (c) 2012 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -89,7 +89,6 @@ def networkFromString(strNets: str) -> NetworkType:
return typing.cast(NetworkType, networksFromString(strNets, False))
# pylint: disable=too-many-return-statements, too-many-branches, too-many-statements
def networksFromString(
strNets: str, allowMultipleNetworks: bool = True
) -> typing.Union[NetworklistType, NetworkType]:
@ -109,12 +108,12 @@ def networksFromString(
inputString = strNets
logger.debug('Getting network from %s', strNets)
def check(*args):
def check(*args) -> None:
for n in args:
if int(n) < 0 or int(n) > 255:
raise Exception()
def toNum(*args):
def toNum(*args) -> int:
start = 256 * 256 * 256
val = 0
for n in args:
@ -122,7 +121,7 @@ def networksFromString(
start >>= 8
return val
def maskFromBits(nBits):
def maskFromBits(nBits: int) -> int:
v = 0
for n in range(nBits):
v |= 1 << (31 - n)

View File

@ -156,12 +156,3 @@ def getOsFromUA(
res.Version = match.groups(1)[0] # type: ignore
return res
def getOsFromRequest(request):
try:
return request.os
except Exception:
request.os = getOsFromUA(request.META.get('HTTP_USER_AGENT'))
return request.os

View File

@ -54,10 +54,16 @@ def clean(obj: 'Model') -> None:
def getPermissions(obj: 'Model') -> typing.List[models.Permissions]:
return list(models.Permissions.enumeratePermissions(object_type=ot.getObjectType(obj), object_id=obj.pk))
return list(
models.Permissions.enumeratePermissions(
object_type=ot.getObjectType(obj), object_id=obj.pk
)
)
def getEffectivePermission(user: 'models.User', obj: 'Model', root: bool = False) -> int:
def getEffectivePermission(
user: 'models.User', obj: 'Model', root: bool = False
) -> int:
try:
if user.is_admin:
return PERMISSION_ALL
@ -68,23 +74,49 @@ def getEffectivePermission(user: 'models.User', obj: 'Model', root: bool = False
# Just check permissions for staff members
# root means for "object type" not for an object
if root is False:
return models.Permissions.getPermissions(user=user, groups=user.groups.all(), object_type=ot.getObjectType(obj), object_id=obj.pk)
return models.Permissions.getPermissions(
user=user,
groups=user.groups.all(),
object_type=ot.getObjectType(obj),
object_id=obj.pk,
)
return models.Permissions.getPermissions(user=user, groups=user.groups.all(), object_type=ot.getObjectType(obj))
return models.Permissions.getPermissions(
user=user, groups=user.groups.all(), object_type=ot.getObjectType(obj)
)
except Exception:
return PERMISSION_NONE
def addUserPermission(user: 'models.User', obj: 'Model', permission: int = PERMISSION_READ):
def addUserPermission(
user: 'models.User', obj: 'Model', permission: int = PERMISSION_READ
):
# Some permissions added to some object types needs at least READ_PERMISSION on parent
models.Permissions.addPermission(user=user, object_type=ot.getObjectType(obj), object_id=obj.pk, permission=permission)
models.Permissions.addPermission(
user=user,
object_type=ot.getObjectType(obj),
object_id=obj.pk,
permission=permission,
)
def addGroupPermission(group: 'models.Group', obj: 'Model', permission: int = PERMISSION_READ):
models.Permissions.addPermission(group=group, object_type=ot.getObjectType(obj), object_id=obj.pk, permission=permission)
def addGroupPermission(
group: 'models.Group', obj: 'Model', permission: int = PERMISSION_READ
):
models.Permissions.addPermission(
group=group,
object_type=ot.getObjectType(obj),
object_id=obj.pk,
permission=permission,
)
def checkPermissions(user: 'models.User', obj: 'Model', permission: int = PERMISSION_ALL, root: bool = False):
def checkPermissions(
user: 'models.User',
obj: 'Model',
permission: int = PERMISSION_ALL,
root: bool = False,
):
return getEffectivePermission(user, obj, root) >= permission

View File

@ -49,7 +49,7 @@ class ExtendedHttpRequest(HttpRequest):
ip: str
ip_proxy: str
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
class ExtendedHttpRequestWithUser(ExtendedHttpRequest):
@ -72,6 +72,7 @@ def getRequest() -> ExtendedHttpRequest:
return ExtendedHttpRequest()
def delCurrentRequest() -> None:
ident = getIdent()
logger.debug('Deleting %s', ident)
@ -83,6 +84,7 @@ def delCurrentRequest() -> None:
except Exception:
logger.exception('Deleting stored request')
def cleanOldRequests() -> None:
logger.debug('Cleaning stuck requests from %s', _requests)
# No request lives 60 seconds, so 60 seconds is fine

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -39,6 +39,7 @@ class State:
This class represents possible states for objects at database.
Take in consideration that objects do not have to support all states, they are here for commodity
"""
ACTIVE = 'A'
BLOCKED = 'B'
CANCELED = 'C'
@ -82,7 +83,7 @@ class State:
MAINTENANCE: _('In maintenance'),
WAITING_OS: _('Waiting OS'),
SLOWED_DOWN: _('Too many preparing services'),
META_MEMBER: _('Meta member')
META_MEMBER: _('Meta member'),
}
# States that are merely for "information" to the user. They don't contain any usable instance

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -32,6 +32,7 @@
"""
import typing
class StateQueue:
_queue: typing.List[typing.Any]
_current: typing.Optional[typing.Any]
@ -41,7 +42,10 @@ class StateQueue:
self._current = None
def __str__(self):
res = '<StateQueue Current: %s, Queue: (%s)>' % (self._current, ','.join(state for state in self._queue))
res = '<StateQueue Current: %s, Queue: (%s)>' % (
self._current,
','.join(state for state in self._queue),
)
return res
def clearQueue(self) -> None:
@ -60,10 +64,11 @@ class StateQueue:
def contains(self, state: typing.Any) -> bool:
# if self._queue.co
for s in self._queue:
if s == state:
return True
return False
return state in self._queue
# for s in self._queue:
# if s == state:
# return True
# return False
def push_back(self, state: typing.Any) -> None:
self._queue.append(state)

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,

View File

@ -1,95 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2013 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from threading import Thread
import logging
import queue
logger = logging.getLogger(__name__)
DEFAULT_QUEUE_SIZE = 32
class Worker(Thread):
def __init__(self, tasks):
Thread.__init__(self)
self._tasks = tasks
self._stop = False
self.start()
def notifyStop(self):
self._stop = True
def run(self):
while self._stop is False:
try:
func, args, kargs = self._tasks.get(block=True, timeout=1)
except queue.Empty:
continue
try:
func(*args, **kargs)
except Exception:
logger.exception('ThreadPool Worker')
self._tasks.task_done()
class ThreadPool:
def __init__(self, num_threads, queueSize=DEFAULT_QUEUE_SIZE):
self._tasks = queue.Queue(queueSize)
self._numThreads = num_threads
self._threads = []
def add_task(self, func, *args, **kargs):
"""
Add a task to the queue
"""
if not self._threads:
for _ in range(self._numThreads):
self._threads.append(Worker(self._tasks))
self._tasks.put((func, args, kargs))
def wait_completion(self):
"""
Wait for completion of all the tasks in the queue
"""
self._tasks.join()
# Now we will close all running tasks
# In case new tasks are inserted after using this, new threads will be created
# to handle tasks
for n in self._threads:
n.notifyStop()
for n in self._threads:
n.join()
self._threads = []

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -43,13 +43,16 @@ import django.template.defaultfilters as filters
from uds.core import services
class DictAsObj(dict):
"""
Returns a mix between a dict and an obj
Can be accesses as .xxxx or ['xxx']
"""
def __init__(self, dct: typing.Optional[typing.Dict[str, typing.Any]] = None, **kwargs):
def __init__(
self, dct: typing.Optional[typing.Dict[str, typing.Any]] = None, **kwargs
):
if dct:
self.__dict__.update(dct)
self.__dict__.update(kwargs)
@ -58,14 +61,11 @@ class DictAsObj(dict):
return self.__dict__[key]
def __unicode__(self):
return ', '.join(
'{}={}'.format(v, self.__dict__[v]) for v in self.__dict__
)
return ', '.join('{}={}'.format(v, self.__dict__[v]) for v in self.__dict__)
# pylint: disable=protected-access
class CaseInsensitiveDict(dict):
@classmethod
def _k(cls, key):
return key.lower() if isinstance(key, str) else key
@ -87,13 +87,19 @@ class CaseInsensitiveDict(dict):
return super(CaseInsensitiveDict, self).__contains__(self.__class__._k(key))
def pop(self, key, *args, **kwargs):
return super(CaseInsensitiveDict, self).pop(self.__class__._k(key), *args, **kwargs)
return super(CaseInsensitiveDict, self).pop(
self.__class__._k(key), *args, **kwargs
)
def get(self, key, *args, **kwargs):
return super(CaseInsensitiveDict, self).get(self.__class__._k(key), *args, **kwargs)
return super(CaseInsensitiveDict, self).get(
self.__class__._k(key), *args, **kwargs
)
def setdefault(self, key, *args, **kwargs):
return super(CaseInsensitiveDict, self).setdefault(self.__class__._k(key), *args, **kwargs)
return super(CaseInsensitiveDict, self).setdefault(
self.__class__._k(key), *args, **kwargs
)
def update(self, E=None, **F):
if E is None:
@ -106,6 +112,7 @@ class CaseInsensitiveDict(dict):
v = super(CaseInsensitiveDict, self).pop(k)
self.__setitem__(k, v)
def asList(value: typing.Any) -> typing.List[typing.Any]:
if isinstance(value, list):
return value
@ -116,6 +123,7 @@ def asList(value: typing.Any) -> typing.List[typing.Any]:
except Exception:
return [value]
def packageRelativeFile(moduleName: str, fileName: str) -> str:
"""
Helper to get image path from relative to a module.
@ -143,6 +151,7 @@ def secondsToTimeString(seconds: int) -> str:
hours %= 24
return ugettext('{} days {:d}:{:02d}:{:02d}').format(days, hours, minutes, seconds)
def checkValidBasename(baseName: str, length: int = -1) -> None:
""" "Checks if the basename + length is valid for services. Raises an exception if not valid"
@ -158,16 +167,25 @@ def checkValidBasename(baseName: str, length: int = -1) -> None:
None -- [description]
"""
if re.match(r'^[a-zA-Z0-9][a-zA-Z0-9-]*$', baseName) is None:
raise services.Service.ValidationException(ugettext('The basename is not a valid for a hostname'))
raise services.Service.ValidationException(
ugettext('The basename is not a valid for a hostname')
)
if length == 0:
raise services.Service.ValidationException(ugettext('The length of basename plus length must be greater than 0'))
raise services.Service.ValidationException(
ugettext('The length of basename plus length must be greater than 0')
)
if length != -1 and len(baseName) + length > 15:
raise services.Service.ValidationException(ugettext('The length of basename plus length must not be greater than 15'))
raise services.Service.ValidationException(
ugettext('The length of basename plus length must not be greater than 15')
)
if baseName.isdigit():
raise services.Service.ValidationException(ugettext('The machine name can\'t be only numbers'))
raise services.Service.ValidationException(
ugettext('The machine name can\'t be only numbers')
)
def removeControlCharacters(s: str) -> str:
return ''.join(ch for ch in s if unicodedata.category(ch)[0] != "C")

View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Virtual Cable S.L.
# Copyright (c) 2012-2019 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +11,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -30,10 +29,6 @@
"""
@author: Adolfo Gómez, dkmaster at dkmon dot com
"""
# Helper to acommodate all unique generators in one place
# pylint: disable=unused-import
from .unique_gid_generator import UniqueGIDGenerator
from .unique_mac_generator import UniqueMacGenerator
from .unique_name_generator import UniqueNameGenerator

View File

@ -38,7 +38,6 @@ logger = logging.getLogger(__name__)
class UniqueGIDGenerator(UniqueIDGenerator):
def __init__(self, owner, baseName=None):
super().__init__('id', owner, baseName)

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -54,18 +54,24 @@ class UniqueIDGenerator:
_owner: str
_baseName: str
def __init__(self, typeName: str, owner: str, baseName: typing.Optional[str] = None):
def __init__(
self, typeName: str, owner: str, baseName: typing.Optional[str] = None
):
self._owner = owner + typeName
self._baseName = 'uds' if baseName is None else baseName
def setBaseName(self, newBaseName: str):
self._baseName = newBaseName
def __filter(self, rangeStart: int, rangeEnd: int = MAX_SEQ, forUpdate: bool = False) -> QuerySet:
def __filter(
self, rangeStart: int, rangeEnd: int = MAX_SEQ, forUpdate: bool = False
) -> QuerySet:
# Order is defined on UniqueId model, and is '-seq' by default (so this gets items in sequence order)
# if not for update, do not use the clause :)
obj = UniqueId.objects.select_for_update() if forUpdate else UniqueId.objects
return obj.filter(basename=self._baseName, seq__gte=rangeStart, seq__lte=rangeEnd) # @UndefinedVariable
return obj.filter(
basename=self._baseName, seq__gte=rangeStart, seq__lte=rangeEnd
) # @UndefinedVariable
def get(self, rangeStart: int = 0, rangeEnd: int = MAX_SEQ) -> int:
"""
@ -99,7 +105,9 @@ class UniqueIDGenerator:
if not item:
# logger.debug('No free found, creating new one')
try:
last = flt.filter(assigned=True)[0] # DB Returns correct order so the 0 item is the last
last = flt.filter(assigned=True)[
0
] # DB Returns correct order so the 0 item is the last
seq = last.seq + 1
except IndexError: # If there is no assigned at database
seq = rangeStart
@ -108,7 +116,13 @@ class UniqueIDGenerator:
return -1 # No ids free in range
# May ocurr on some circustance that a concurrency access gives same item twice, in this case, we
# will get an "duplicate key error",
UniqueId.objects.create(owner=self._owner, basename=self._baseName, seq=seq, assigned=True, stamp=stamp) # @UndefinedVariable
UniqueId.objects.create(
owner=self._owner,
basename=self._baseName,
seq=seq,
assigned=True,
stamp=stamp,
) # @UndefinedVariable
break
except OperationalError: # Locked, may ocurr for example on sqlite. We will wait a bit
# logger.exception('Got database locked')
@ -125,24 +139,20 @@ class UniqueIDGenerator:
return seq
def transfer(self, seq: int, toUidGen: 'UniqueIDGenerator') -> bool:
self.__filter(
0, forUpdate=True
).filter(
owner=self._owner, seq=seq
).update(
owner=toUidGen._owner, basename=toUidGen._baseName, stamp=getSqlDatetimeAsUnix() # pylint: disable=protected-access
self.__filter(0, forUpdate=True).filter(owner=self._owner, seq=seq).update(
owner=toUidGen._owner,
basename=toUidGen._baseName,
stamp=getSqlDatetimeAsUnix(), # pylint: disable=protected-access
)
return True
def free(self, seq) -> None:
logger.debug('Freeing seq %s from %s (%s)', seq, self._owner, self._baseName)
with transaction.atomic():
flt = self.__filter(
0, forUpdate=True
).filter(
owner=self._owner, seq=seq
).update(
owner='', assigned=False, stamp=getSqlDatetimeAsUnix()
flt = (
self.__filter(0, forUpdate=True)
.filter(owner=self._owner, seq=seq)
.update(owner='', assigned=False, stamp=getSqlDatetimeAsUnix())
)
if flt > 0:
self.__purge()
@ -157,13 +167,21 @@ class UniqueIDGenerator:
# logger.exception('Error here')
seq = 0
with transaction.atomic():
self.__filter(seq).delete() # Clean ups all unassigned after last assigned in this range
self.__filter(
seq
).delete() # Clean ups all unassigned after last assigned in this range
def release(self) -> None:
UniqueId.objects.select_for_update().filter(owner=self._owner).update(assigned=False, owner='', stamp=getSqlDatetimeAsUnix()) # @UndefinedVariable
UniqueId.objects.select_for_update().filter(owner=self._owner).update(
assigned=False, owner='', stamp=getSqlDatetimeAsUnix()
) # @UndefinedVariable
self.__purge()
def releaseOlderThan(self, stamp=None) -> None:
stamp = getSqlDatetimeAsUnix() if stamp is None else stamp
UniqueId.objects.select_for_update().filter(owner=self._owner, stamp__lt=stamp).update(assigned=False, owner='', stamp=stamp) # @UndefinedVariable
UniqueId.objects.select_for_update().filter(
owner=self._owner, stamp__lt=stamp
).update(
assigned=False, owner='', stamp=stamp
) # @UndefinedVariable
self.__purge()

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -39,8 +39,7 @@ logger = logging.getLogger(__name__)
class UniqueMacGenerator(UniqueIDGenerator):
def __init__(self, owner: str):
def __init__(self, owner: str) -> None:
super().__init__('mac', owner, '\tmac')
def __toInt(self, mac: str) -> int:
@ -52,14 +51,14 @@ class UniqueMacGenerator(UniqueIDGenerator):
return re.sub(r"(..)", r"\1:", "%0*X" % (12, seq))[:-1]
# noinspection PyMethodOverriding
def get(self, macRange: str) -> str: # type: ignore # pylint: disable=arguments-differ
def get(self, macRange: str) -> str: # type: ignore
firstMac, lastMac = macRange.split('-')
return self.__toMac(super().get(self.__toInt(firstMac), self.__toInt(lastMac)))
def transfer(self, mac: str, toUMgen: 'UniqueMacGenerator'): # type: ignore # pylint: disable=arguments-differ
def transfer(self, mac: str, toUMgen: 'UniqueMacGenerator'): # type: ignore
super().transfer(self.__toInt(mac), toUMgen)
def free(self, mac: str): # pylint: disable=arguments-differ
def free(self, mac: str) -> None:
super().free(self.__toInt(mac))
# Release is inherited, no mod needed

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2019 Virtual Cable S.L.
# Copyright (c) 2014-2019 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -39,13 +39,13 @@ from uds.core.module import Module
logger = logging.getLogger(__name__)
def validateNumeric(
numericStr: str,
value: str,
minValue: typing.Optional[int] = None,
maxValue: typing.Optional[int] = None,
returnAsInteger: bool = True,
fieldName: typing.Optional[str] = None
) -> typing.Union[str, int]:
fieldName: typing.Optional[str] = None,
) -> int:
"""
Validates that a numeric value is valid
:param numericStr: Numeric value to check (as string)
@ -55,41 +55,53 @@ def validateNumeric(
:param fieldName: If present, the name of the field for "Raising" exceptions, defaults to "Numeric value"
:return: Raises Module.Validation exception if is invalid, else return the value "fixed"
"""
numericStr = numericStr.replace(' ', '')
value = value.replace(' ', '')
fieldName = fieldName or _('Numeric')
try:
numeric = int(numericStr)
numeric = int(value)
if minValue is not None and numeric < minValue:
raise Module.ValidationException(_('{0} must be greater than or equal to {1}'.format(fieldName, minValue)))
raise Module.ValidationException(
_(
'{0} must be greater than or equal to {1}'.format(
fieldName, minValue
)
)
)
if maxValue is not None and numeric > maxValue:
raise Module.ValidationException(_('{0} must be lower than or equal to {1}'.format(fieldName, maxValue)))
raise Module.ValidationException(
_('{0} must be lower than or equal to {1}'.format(fieldName, maxValue))
)
numericStr = str(numeric)
value = str(numeric)
except ValueError:
raise Module.ValidationException(_('{0} contains invalid characters').format(fieldName))
raise Module.ValidationException(
_('{0} contains invalid characters').format(fieldName)
)
if returnAsInteger:
return int(numericStr)
return int(value)
return numericStr
def validateHostname(hostname: str, maxLength: int, asPattern: bool) -> str:
if len(hostname) > maxLength:
raise Module.ValidationException(_('{} exceeds maximum host name length.').format(hostname))
raise Module.ValidationException(
_('{} exceeds maximum host name length.').format(hostname)
)
if hostname[-1] == ".":
hostname = hostname[:-1] # strip exactly one dot from the right, if present
if asPattern:
allowed = re.compile("(?!-)[A-Z\d-]{1,63}$", re.IGNORECASE)
allowed = re.compile(r'(?!-)[A-Z\d-]{1,63}$', re.IGNORECASE)
else:
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
allowed = re.compile(r'(?!-)[A-Z\d-]{1,63}(?<!-)$', re.IGNORECASE)
if not all(allowed.match(x) for x in hostname.split(".")):
raise Module.ValidationException(_('{} is not a valid hostname').format(hostname))
raise Module.ValidationException(
_('{} is not a valid hostname').format(hostname)
)
return hostname
@ -101,17 +113,17 @@ def validatePort(portStr: str) -> int:
:param returnAsInteger: if True, returns value as integer, if not, as string
:return: Raises Module.Validation exception if is invalid, else return the value "fixed"
"""
return typing.cast(int, validateNumeric(portStr, minValue=0, maxValue=65535, returnAsInteger=True, fieldName='Port'))
return validateNumeric(portStr, minValue=0, maxValue=65535, fieldName='Port')
def validateTimeout(timeOutStr) -> int:
def validateTimeout(timeOutStr: str) -> int:
"""
Validates that a timeout value is valid
:param timeOutStr: timeout to validate
:param returnAsInteger: if True, returns value as integer, if not, as string
:return: Raises Module.Validation exception if is invalid, else return the value "fixed"
"""
return typing.cast(int, validateNumeric(timeOutStr, minValue=0, returnAsInteger=True, fieldName='Timeout'))
return validateNumeric(timeOutStr, minValue=0, fieldName='Timeout')
def validateMacRange(macRange: str) -> str:
@ -123,7 +135,9 @@ def validateMacRange(macRange: str) -> str:
# Removes white spaces and all to uppercase
macRange = macRange.upper().replace(' ', '')
macRE = re.compile(r'^([0-9A-F]{2}[:]){5}([0-9A-F]{2})$') # In fact, it could be XX-XX-XX-XX-XX-XX, but we use - as range separator
macRE = re.compile(
r'^([0-9A-F]{2}[:]){5}([0-9A-F]{2})$'
) # In fact, it could be XX-XX-XX-XX-XX-XX, but we use - as range separator
try:
macRangeStart, macRangeEnd = macRange.split('-')
@ -133,6 +147,10 @@ def validateMacRange(macRange: str) -> str:
if macRangeStart > macRangeEnd:
raise Exception()
except Exception:
raise Module.ValidationException(_('Invalid mac range. Mac range must be in format XX:XX:XX:XX:XX:XX-XX:XX:XX:XX:XX:XX'))
raise Module.ValidationException(
_(
'Invalid mac range. Mac range must be in format XX:XX:XX:XX:XX:XX-XX:XX:XX:XX:XX:XX'
)
)
return macRange

View File

@ -1,12 +1,7 @@
"""
Created on Jul 11, 2016
@author: dkmaster
"""
# -*- coding: utf-8 -*-
#
# Copyright (c) 2013-2019 Virtual Cable S.L.
# Copyright (c) 2013-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -17,7 +12,7 @@ Created on Jul 11, 2016
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -31,13 +26,20 @@ Created on Jul 11, 2016
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
@author: dkmaster@dkmon.com
"""
import typing
from collections import defaultdict
from xml.etree import cElementTree
def etree_to_dict(t):
d = {t.tag: {} if t.attrib else None}
d = {}
if t.attrib:
d.update({t.tag: {}})
children = list(t)
if children:
dd = defaultdict(list)
@ -56,5 +58,6 @@ def etree_to_dict(t):
d[t.tag] = text
return d
def parse(xml_string: str) -> typing.Dict:
return etree_to_dict(cElementTree.XML(xml_string))