diff --git a/server/src/uds/core/managers/__init__.py b/server/src/uds/core/managers/__init__.py index 36c57feb2..459c5a337 100644 --- a/server/src/uds/core/managers/__init__.py +++ b/server/src/uds/core/managers/__init__.py @@ -45,36 +45,36 @@ if typing.TYPE_CHECKING: def cryptoManager() -> 'CryptoManager': - from .crypto import CryptoManager # pylint: disable=redefined-outer-name + from .crypto import CryptoManager return CryptoManager.manager() -def taskManager() -> typing.Type['TaskManager']: - from .task import TaskManager # pylint: disable=redefined-outer-name +def taskManager() -> 'TaskManager': + from .task import TaskManager - return TaskManager + return TaskManager.manager() def downloadsManager() -> 'DownloadsManager': - from .downloads import DownloadsManager # pylint: disable=redefined-outer-name + from .downloads import DownloadsManager return DownloadsManager.manager() def logManager() -> 'LogManager': - from .log import LogManager # pylint: disable=redefined-outer-name + from .log import LogManager return LogManager.manager() def userServiceManager() -> 'UserServiceManager': - from .user_service import UserServiceManager # pylint: disable=redefined-outer-name + from .user_service import UserServiceManager return UserServiceManager.manager() def publicationManager() -> 'PublicationManager': - from .publication import PublicationManager # pylint: disable=redefined-outer-name + from .publication import PublicationManager return PublicationManager.manager() diff --git a/server/src/uds/core/managers/crypto.py b/server/src/uds/core/managers/crypto.py index 2dba7a26a..0f32dc763 100644 --- a/server/src/uds/core/managers/crypto.py +++ b/server/src/uds/core/managers/crypto.py @@ -49,6 +49,8 @@ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from django.conf import settings +from uds.core.util import singleton + logger = logging.getLogger(__name__) if typing.TYPE_CHECKING: @@ -58,9 +60,7 @@ if typing.TYPE_CHECKING: from cryptography.hazmat.primitives.asymmetric.dh import DHPrivateKey -class CryptoManager: - instance = None - +class CryptoManager(metaclass=singleton.Singleton): def __init__(self): self._rsa = serialization.load_pem_private_key( settings.RSA_KEY.encode(), password=None, backend=default_backend() @@ -87,9 +87,7 @@ class CryptoManager: @staticmethod def manager() -> 'CryptoManager': - if CryptoManager.instance is None: - CryptoManager.instance = CryptoManager() - return CryptoManager.instance + return CryptoManager() # Singleton pattern will return always the same instance def encrypt(self, value: str) -> str: return codecs.encode( @@ -117,7 +115,7 @@ class CryptoManager: label=None, ), ) - except Exception: # If fails, try old method + except Exception: # Old method is not supported logger.exception('Decripting: %s', value) return 'decript error' # logger.debug('Decripted: %s %s', data, decrypted) diff --git a/server/src/uds/core/managers/downloads.py b/server/src/uds/core/managers/downloads.py index 68f8e100e..d9d53535d 100644 --- a/server/src/uds/core/managers/downloads.py +++ b/server/src/uds/core/managers/downloads.py @@ -39,12 +39,12 @@ from wsgiref.util import FileWrapper from django.http import HttpResponse, Http404 from uds.core.managers import cryptoManager - +from uds.core.util import singleton logger = logging.getLogger(__name__) -class DownloadsManager: +class DownloadsManager(metaclass=singleton.Singleton): """ Manager so connectors can register their own downloadables For registering, use at __init__.py of the conecto something like this: @@ -56,17 +56,16 @@ class DownloadsManager: 'application/x-msdos-program') """ - _manager: typing.Optional['DownloadsManager'] = None _downloadables: typing.Dict[str, typing.Dict[str, str]] = {} def __init__(self): self._downloadables = {} @staticmethod - def manager(): - if DownloadsManager._manager is None: - DownloadsManager._manager = DownloadsManager() - return DownloadsManager._manager + def manager() -> 'DownloadsManager': + return ( + DownloadsManager() + ) # Singleton pattern will return always the same instance def registerDownloadable( self, name: str, comment: str, path: str, mime: str = 'application/octet-stream' diff --git a/server/src/uds/core/managers/log.py b/server/src/uds/core/managers/log.py index e55b68a9d..636107959 100644 --- a/server/src/uds/core/managers/log.py +++ b/server/src/uds/core/managers/log.py @@ -36,6 +36,7 @@ import typing from uds import models from uds.core.util.config import GlobalConfig +from uds.core.util import singleton # Not imported at runtime, just for type checking if typing.TYPE_CHECKING: @@ -72,21 +73,17 @@ transDict: typing.Dict[typing.Type['Model'], int] = { } -class LogManager: +class LogManager(metaclass=singleton.Singleton): """ Manager for logging (at database) events """ - _manager: typing.Optional['LogManager'] = None - def __init__(self): pass @staticmethod def manager() -> 'LogManager': - if not LogManager._manager: - LogManager._manager = LogManager() - return LogManager._manager + return LogManager() # Singleton pattern will return always the same instance def __log( self, @@ -204,7 +201,9 @@ class LogManager: self.__clearLogs(owner_type, wichObject.id) # type: ignore else: logger.debug( - 'Requested clearLogs for a type of object not covered: %s: %s', type(wichObject), wichObject + 'Requested clearLogs for a type of object not covered: %s: %s', + type(wichObject), + wichObject, ) for line in traceback.format_stack(limit=5): logger.debug('>> %s', line) diff --git a/server/src/uds/core/managers/publication.py b/server/src/uds/core/managers/publication.py index e027d76ab..67534b035 100644 --- a/server/src/uds/core/managers/publication.py +++ b/server/src/uds/core/managers/publication.py @@ -45,6 +45,8 @@ from uds.core.util import log from uds.models import ServicePoolPublication, getSqlDatetime, ServicePool +from uds.core.util import singleton + if typing.TYPE_CHECKING: from uds.core import services @@ -255,13 +257,11 @@ class PublicationFinishChecker(DelayedTask): ) -class PublicationManager: +class PublicationManager(metaclass=singleton.Singleton): """ Manager responsible of controlling publications """ - _manager: typing.Optional['PublicationManager'] = None - def __init__(self): pass @@ -270,9 +270,9 @@ class PublicationManager: """ Returns the singleton to this manager """ - if not PublicationManager._manager: - PublicationManager._manager = PublicationManager() - return PublicationManager._manager + return ( + PublicationManager() + ) # Singleton pattern will return always the same instance def publish( self, servicePool: ServicePool, changeLog: typing.Optional[str] = None diff --git a/server/src/uds/core/managers/stats.py b/server/src/uds/core/managers/stats.py index ed3a68b07..186a335bb 100644 --- a/server/src/uds/core/managers/stats.py +++ b/server/src/uds/core/managers/stats.py @@ -35,6 +35,7 @@ import logging import typing from uds.core.util.config import GlobalConfig +from uds.core.util import singleton from uds.models import StatsCounters from uds.models import getSqlDatetime, getSqlDatetimeAsUnix from uds.models import StatsEvents @@ -53,7 +54,7 @@ REVERSE_FLDS_EQUIV: typing.Mapping[str, str] = { } -class StatsManager: +class StatsManager(metaclass=singleton.Singleton): """ Manager for statistics, so we can provide usefull info about platform usage @@ -62,16 +63,12 @@ class StatsManager: are assigned, are in use, in cache, etc... """ - _manager: typing.Optional['StatsManager'] = None - def __init__(self): pass @staticmethod - def manager(): - if not StatsManager._manager: - StatsManager._manager = StatsManager() - return StatsManager._manager + def manager() -> 'StatsManager': + return StatsManager() # Singleton pattern will return always the same instance def __doCleanup(self, model): minTime = time.mktime( diff --git a/server/src/uds/core/managers/task.py b/server/src/uds/core/managers/task.py index b4f992184..97f41f712 100644 --- a/server/src/uds/core/managers/task.py +++ b/server/src/uds/core/managers/task.py @@ -41,6 +41,7 @@ from uds.core.jobs.scheduler import Scheduler from uds.core.jobs.delayed_task_runner import DelayedTaskRunner from uds.core import jobs from uds.core.util.config import GlobalConfig +from uds.core.util import singleton logger = logging.getLogger(__name__) @@ -66,9 +67,16 @@ class DelayedTaskThread(BaseThread): DelayedTaskRunner.runner().notifyTermination() -class TaskManager: +class TaskManager(metaclass=singleton.Singleton): keepRunning: bool = True + def __init__(self): + pass + + @staticmethod + def manager() -> 'TaskManager': + return TaskManager() + @staticmethod def sigTerm(sigNum, frame): """ @@ -80,24 +88,20 @@ class TaskManager: Take a look at killTaskManager.sh :-) """ logger.info("Caught term signal, finishing task manager") - TaskManager.keepRunning = False + TaskManager.manager().keepRunning = False - @staticmethod - def registerJob(jobType: typing.Type[jobs.Job]) -> None: + def registerJob(self, jobType: typing.Type[jobs.Job]) -> None: jobName = jobType.friendly_name jobs.factory().insert(jobName, jobType) - @staticmethod - def registerScheduledTasks() -> None: - + def registerScheduledTasks(self) -> None: logger.info("Registering sheduled tasks") # Simply import this to make workers "auto import themself" from uds.core import workers # @UnusedImport pylint: disable=unused-import - @staticmethod - def run() -> None: - TaskManager.keepRunning = True + def run(self) -> None: + self.keepRunning = True # Don't know why, but with django 1.8, must "reset" connections so them do not fail on first access... # Is simmilar to https://code.djangoproject.com/ticket/21597#comment:29 @@ -106,7 +110,7 @@ class TaskManager: # Releases owned schedules so anyone can access them... Scheduler.releaseOwnShedules() - TaskManager.registerScheduledTasks() + self.registerScheduledTasks() noSchedulers: int = GlobalConfig.SCHEDULER_THREADS.getInt() noDelayedTasks: int = GlobalConfig.DELAYED_TASKS_THREADS.getInt() diff --git a/server/src/uds/core/managers/user_preferences.py b/server/src/uds/core/managers/user_preferences.py index f49bbca77..beed5cdc0 100644 --- a/server/src/uds/core/managers/user_preferences.py +++ b/server/src/uds/core/managers/user_preferences.py @@ -38,6 +38,8 @@ import typing from django import forms from django.utils.translation import ugettext as _, ugettext_lazy +from uds.core.util import singleton + if typing.TYPE_CHECKING: from uds.models import User @@ -45,8 +47,7 @@ logger = logging.getLogger(__name__) # UserPrefs is DEPRECATED # Currently not used anywhere -class UserPrefsManager: - _manager: typing.Optional['UserPrefsManager'] = None +class UserPrefsManager(metaclass=singleton.Singleton): _prefs: typing.Dict[str, typing.Dict] def __init__(self): @@ -54,9 +55,7 @@ class UserPrefsManager: @staticmethod def manager() -> 'UserPrefsManager': - if UserPrefsManager._manager is None: - UserPrefsManager._manager = UserPrefsManager() - return UserPrefsManager._manager + return UserPrefsManager() def __nameFor(self, module, name): return module + "_" + name diff --git a/server/src/uds/core/managers/user_service.py b/server/src/uds/core/managers/user_service.py index 388ef2781..0722baf98 100644 --- a/server/src/uds/core/managers/user_service.py +++ b/server/src/uds/core/managers/user_service.py @@ -58,6 +58,7 @@ from uds.models import ( ) from uds.models.meta_pool import MetaPoolMember from uds.core import services, transports +from uds.core.util import singleton from uds.core.util.stats import events from .userservice import comms @@ -67,17 +68,13 @@ logger = logging.getLogger(__name__) traceLogger = logging.getLogger('traceLog') -class UserServiceManager: - _manager: typing.Optional['UserServiceManager'] = None - +class UserServiceManager(metaclass=singleton.Singleton): def __init__(self): pass @staticmethod def manager() -> 'UserServiceManager': - if not UserServiceManager._manager: - UserServiceManager._manager = UserServiceManager() - return UserServiceManager._manager + return UserServiceManager() # Singleton pattern will return always the same instance @staticmethod def getCacheStateFilter(level: int) -> Q: diff --git a/server/src/uds/core/util/singleton.py b/server/src/uds/core/util/singleton.py new file mode 100644 index 000000000..b6af47884 --- /dev/null +++ b/server/src/uds/core/util/singleton.py @@ -0,0 +1,10 @@ +# Metaclass for singleton pattern +class Singleton(type): + def __init__(self, *args, **kwargs): + self.__instance = None + super().__init__(*args, **kwargs) + + def __call__(self, *args, **kwargs): + if self.__instance is None: + self.__instance = super().__call__(*args, **kwargs) + return self.__instance