1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-01-10 01:17:59 +03:00

More linting (a lot more :) )

This commit is contained in:
Adolfo Gómez García 2023-04-15 16:33:53 +02:00
parent ed90cd3995
commit a058b61276
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
42 changed files with 368 additions and 299 deletions

View File

@ -11,7 +11,7 @@ clear-cache-post-run=no
# Load and enable all available extensions. Use --list-extensions to see a list # Load and enable all available extensions. Use --list-extensions to see a list
# all available extensions. # all available extensions.
#enable-all-extensions= enable-all-extensions=yes
# In error mode, messages with a category besides ERROR or FATAL are # In error mode, messages with a category besides ERROR or FATAL are
# suppressed, and no reports are done by default. Error mode is compatible with # suppressed, and no reports are done by default. Error mode is compatible with
@ -278,34 +278,34 @@ exclude-too-few-public-methods=
ignored-parents= ignored-parents=
# Maximum number of arguments for function / method. # Maximum number of arguments for function / method.
max-args=5 max-args=10
# Maximum number of attributes for a class (see R0902). # Maximum number of attributes for a class (see R0902).
max-attributes=7 max-attributes=12
# Maximum number of boolean expressions in an if statement (see R0916). # Maximum number of boolean expressions in an if statement (see R0916).
max-bool-expr=5 max-bool-expr=5
# Maximum number of branch for function / method body. # Maximum number of branch for function / method body.
max-branches=20 max-branches=24
# Maximum number of locals for function / method body. # Maximum number of locals for function / method body.
max-locals=15 max-locals=24
# Maximum number of parents for a class (see R0901). # Maximum number of parents for a class (see R0901).
max-parents=7 max-parents=7
# Maximum number of public methods for a class (see R0904). # Maximum number of public methods for a class (see R0904).
max-public-methods=20 max-public-methods=32
# Maximum number of return / yield for function / method body. # Maximum number of return / yield for function / method body.
max-returns=6 max-returns=6
# Maximum number of statements in function / method body. # Maximum number of statements in function / method body.
max-statements=50 max-statements=64
# Minimum number of public methods for a class (see R0903). # Minimum number of public methods for a class (see R0903).
min-public-methods=2 min-public-methods=1
[EXCEPTIONS] [EXCEPTIONS]
@ -428,7 +428,8 @@ disable=raw-checker-failed,
broad-except, broad-except,
no-name-in-module, # Too many false positives... :( no-name-in-module, # Too many false positives... :(
import-error, import-error,
too-many-lines too-many-lines,
redefined-builtin,
# Enable the message, report, category or checker with the given id(s). You can # Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option # either give multiple identifier separated by comma (,) or put this option

View File

@ -93,7 +93,7 @@ class Client(Handler):
# error += ' (code {0:04X})'.format(errorCode) # error += ' (code {0:04X})'.format(errorCode)
error = _( error = _(
'Your service is being created. Please, wait while we complete it' 'Your service is being created. Please, wait while we complete it'
) + ' ({}%)'.format(int(errorCode * 25)) ) + f' ({int(errorCode)*25}%)'
res['error'] = error res['error'] = error
res['retryable'] = '1' if retryable else '0' res['retryable'] = '1' if retryable else '0'

View File

@ -459,10 +459,10 @@ def webPassword(request: HttpRequest) -> str:
return cryptoManager().symDecrpyt( return cryptoManager().symDecrpyt(
passkey, getUDSCookie(request) passkey, getUDSCookie(request)
) # recover as original unicode string ) # recover as original unicode string
else: # No session, get from _session instead, this is an "client" REST request # No session, get from _session instead, this is an "client" REST request
return cryptoManager().symDecrpyt( return cryptoManager().symDecrpyt(
getattr(request, '_cryptedpass'), getattr(request, '_scrambler') getattr(request, '_cryptedpass'), getattr(request, '_scrambler')
) )
def webLogout( def webLogout(

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # pylint: disable=unused-argument # this has a lot of "default" methods, so we need to ignore unused arguments most of the time
# #
# Copyright (c) 2012-2020 Virtual Cable S.L.U. # Copyright (c) 2012-2020 Virtual Cable S.L.U.
# All rights reserved. # All rights reserved.
@ -33,7 +34,6 @@ Base module for all authenticators
""" """
import enum import enum
import logging import logging
from re import A
import typing import typing
from django.utils.translation import gettext_noop as _ from django.utils.translation import gettext_noop as _
@ -65,6 +65,7 @@ class AuthenticationSuccess(enum.IntEnum):
OK = 1 OK = 1
REDIRECT = 2 REDIRECT = 2
class AuthenticationInternalUrl(enum.Enum): class AuthenticationInternalUrl(enum.Enum):
""" """
Enumeration for authentication success Enumeration for authentication success
@ -78,6 +79,7 @@ class AuthenticationInternalUrl(enum.Enum):
""" """
return reverse(self.value) return reverse(self.value)
class AuthenticationResult(typing.NamedTuple): class AuthenticationResult(typing.NamedTuple):
success: AuthenticationSuccess success: AuthenticationSuccess
url: typing.Optional[str] = None url: typing.Optional[str] = None
@ -186,8 +188,8 @@ class Authenticator(Module):
# : If this authenticators casues a temporal block of an user on repeated login failures # : If this authenticators casues a temporal block of an user on repeated login failures
blockUserOnLoginFailures: typing.ClassVar[bool] = True blockUserOnLoginFailures: typing.ClassVar[bool] = True
from .user import User from .user import User # pylint: disable=import-outside-toplevel
from .group import Group from .group import Group # pylint: disable=import-outside-toplevel
# : The type of user provided, normally standard user will be enough. # : The type of user provided, normally standard user will be enough.
# : This is here so if we need it in some case, we can write our own # : This is here so if we need it in some case, we can write our own
@ -213,10 +215,12 @@ class Authenticator(Module):
@param environment: Environment for the authenticator @param environment: Environment for the authenticator
@param values: Values passed to element @param values: Values passed to element
""" """
from uds.models import Authenticator as AuthenticatorModel from uds.models import ( # pylint: disable=import-outside-toplevel
Authenticator as AuthenticatorModel,
)
self._dbAuth = dbAuth or AuthenticatorModel() # Fake dbAuth if not provided self._dbAuth = dbAuth or AuthenticatorModel() # Fake dbAuth if not provided
super(Authenticator, self).__init__(environment, values) super().__init__(environment, values)
self.initialize(values) self.initialize(values)
def initialize(self, values: typing.Optional[typing.Dict[str, typing.Any]]) -> None: def initialize(self, values: typing.Optional[typing.Dict[str, typing.Any]]) -> None:
@ -249,9 +253,9 @@ class Authenticator(Module):
user param is a database user object user param is a database user object
""" """
from uds.core.auths.groups_manager import ( from uds.core.auths.groups_manager import ( # pylint: disable=import-outside-toplevel
GroupsManager, GroupsManager,
) # pylint: disable=redefined-outer-name )
if self.isExternalSource: if self.isExternalSource:
groupsManager = GroupsManager(self._dbAuth) groupsManager = GroupsManager(self._dbAuth)
@ -268,7 +272,7 @@ class Authenticator(Module):
This method will allow us to know where to do redirection in case This method will allow us to know where to do redirection in case
we need to use callback for authentication we need to use callback for authentication
""" """
from .auth import authCallbackUrl from .auth import authCallbackUrl # pylint: disable=import-outside-toplevel
return authCallbackUrl(self.dbAuthenticator()) return authCallbackUrl(self.dbAuthenticator())
@ -276,7 +280,7 @@ class Authenticator(Module):
""" """
Helper method to return info url for this authenticator Helper method to return info url for this authenticator
""" """
from .auth import authInfoUrl from .auth import authInfoUrl # pylint: disable=import-outside-toplevel
return authInfoUrl(self.dbAuthenticator()) return authInfoUrl(self.dbAuthenticator())
@ -394,18 +398,26 @@ class Authenticator(Module):
""" """
return FAILED_AUTH return FAILED_AUTH
def isAccesibleFrom(self, request: 'HttpRequest'): def isAccesibleFrom(self, request: 'HttpRequest') -> bool:
""" """
Used by the login interface to determine if the authenticator is visible on the login page. Used by the login interface to determine if the authenticator is visible on the login page.
""" """
from uds.core.util.request import ExtendedHttpRequest from uds.core.util.request import ( # pylint: disable=import-outside-toplevel
from uds.models import Authenticator as dbAuth ExtendedHttpRequest,
)
from uds.models import ( # pylint: disable=import-outside-toplevel
Authenticator as dbAuth,
)
return self._dbAuth.state != dbAuth.DISABLED and self._dbAuth.validForIp( return self._dbAuth.state != dbAuth.DISABLED and self._dbAuth.validForIp(
typing.cast('ExtendedHttpRequest', request).ip typing.cast('ExtendedHttpRequest', request).ip
) )
def transformUsername(self, username: str, request: 'ExtendedHttpRequest') -> str: def transformUsername(
self,
username: str,
request: 'ExtendedHttpRequest',
) -> str:
""" """
On login, this method get called so we can "transform" provided user name. On login, this method get called so we can "transform" provided user name.
@ -462,7 +474,11 @@ class Authenticator(Module):
""" """
return self.authenticate(username, credentials, groupsManager, request) return self.authenticate(username, credentials, groupsManager, request)
def logout(self, request: 'ExtendedHttpRequest', username: str) -> AuthenticationResult: def logout(
self,
request: 'ExtendedHttpRequest',
username: str,
) -> AuthenticationResult:
""" """
Invoked whenever an user logs out. Invoked whenever an user logs out.
@ -491,7 +507,10 @@ class Authenticator(Module):
return SUCCESS_AUTH return SUCCESS_AUTH
def webLogoutHook( def webLogoutHook(
self, username: str, request: 'HttpRequest', response: 'HttpResponse' self,
username: str,
request: 'HttpRequest',
response: 'HttpResponse',
) -> None: ) -> None:
''' '''
Invoked on web logout of an user Invoked on web logout of an user

View File

@ -64,7 +64,7 @@ class JobThread(threading.Thread):
_freq: int _freq: int
def __init__(self, jobInstance: 'Job', dbJob: DBScheduler) -> None: def __init__(self, jobInstance: 'Job', dbJob: DBScheduler) -> None:
super(JobThread, self).__init__() super().__init__()
self._jobInstance = jobInstance self._jobInstance = jobInstance
self._dbJobId = dbJob.id self._dbJobId = dbJob.id
self._freq = dbJob.frecuency self._freq = dbJob.frecuency

View File

@ -85,7 +85,7 @@ class CryptoManager(metaclass=singleton.Singleton):
while len(key) < length: while len(key) < length:
key += key # Dup key key += key # Dup key
kl: typing.List[int] = [v for v in key] kl: typing.List[int] = list(key)
pos = 0 pos = 0
while len(kl) > length: while len(kl) > length:
kl[pos] ^= kl[length] kl[pos] ^= kl[length]
@ -279,17 +279,18 @@ class CryptoManager(metaclass=singleton.Singleton):
return secrets.compare_digest( return secrets.compare_digest(
hashlib.sha3_256(value).hexdigest(), hashValue[8:] hashlib.sha3_256(value).hexdigest(), hashValue[8:]
) )
elif hashValue[:12] == '{SHA256SALT}': if hashValue[:12] == '{SHA256SALT}':
# Extract 16 chars salt and hash # Extract 16 chars salt and hash
salt = hashValue[12:28].encode() salt = hashValue[12:28].encode()
value = salt + value value = salt + value
return secrets.compare_digest( return secrets.compare_digest(
hashlib.sha3_256(value).hexdigest(), hashValue[28:] hashlib.sha3_256(value).hexdigest(), hashValue[28:]
) )
else: # Old sha1
return secrets.compare_digest( # Old sha1
hashValue, str(hashlib.sha1(value).hexdigest()) # nosec: Old compatibility SHA1, not used anymore but need to be supported return secrets.compare_digest(
) # nosec: Old compatibility SHA1, not used anymore but need to be supported hashValue, str(hashlib.sha1(value).hexdigest()) # nosec: Old compatibility SHA1, not used anymore but need to be supported
) # nosec: Old compatibility SHA1, not used anymore but need to be supported
def uuid(self, obj: typing.Any = None) -> str: def uuid(self, obj: typing.Any = None) -> str:
""" """

View File

@ -106,7 +106,7 @@ class DownloadsManager(metaclass=singleton.Singleton):
memory at once. The FileWrapper will turn the file object into an memory at once. The FileWrapper will turn the file object into an
iterator for chunks of 8KB. iterator for chunks of 8KB.
""" """
wrapper = FileWrapper(open(filename, 'rb')) wrapper = FileWrapper(open(filename, 'rb')) # pylint: disable=consider-using-with
response = HttpResponse(wrapper, content_type=mime) response = HttpResponse(wrapper, content_type=mime)
response['Content-Length'] = os.path.getsize(filename) response['Content-Length'] = os.path.getsize(filename)
response['Content-Disposition'] = 'attachment; filename=' + name response['Content-Disposition'] = 'attachment; filename=' + name

View File

@ -149,7 +149,7 @@ class PublicationFinishChecker(DelayedTask):
""" """
def __init__(self, publication: ServicePoolPublication) -> None: def __init__(self, publication: ServicePoolPublication) -> None:
super(PublicationFinishChecker, self).__init__() super().__init__()
self._publishId = publication.id self._publishId = publication.id
self._state = publication.state self._state = publication.state

View File

@ -1021,12 +1021,15 @@ class UserServiceManager(metaclass=singleton.Singleton):
sortedPools = sorted( sortedPools = sorted(
sortPools, key=operator.itemgetter(0) sortPools, key=operator.itemgetter(0)
) # sort by priority (first element) ) # sort by priority (first element)
pools: typing.List[ServicePool] = [ pools: typing.List[ServicePool] = []
p[1] for p in sortedPools if p[1].usage() < 100 and p[1].isUsable() poolsFull: typing.List[ServicePool] = []
] for p in sortedPools:
poolsFull: typing.List[ServicePool] = [ if not p[1].isUsable():
p[1] for p in sortedPools if p[1].usage() == 100 and p[1].isUsable() continue
] if p[1].usage() == 100:
poolsFull.append(p[1])
else:
pools.append(p[1])
logger.debug('Pools: %s/%s', pools, poolsFull) logger.debug('Pools: %s/%s', pools, poolsFull)

View File

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*- # pylint: disable=unused-argument # this has a lot of "default" methods, so we need to ignore unused arguments most of the time
# #
# Copyright (c) 2022 Virtual Cable S.L.U. # Copyright (c) 2022 Virtual Cable S.L.U.
@ -300,7 +300,6 @@ class MFA(Module):
This method allows to reset the MFA state of an user. This method allows to reset the MFA state of an user.
Normally, this will do nothing, but for persistent MFA data (as Google Authenticator), this will remove the data. Normally, this will do nothing, but for persistent MFA data (as Google Authenticator), this will remove the data.
""" """
pass
@staticmethod @staticmethod
def getUserId(user: 'User') -> str: def getUserId(user: 'User') -> str:

View File

@ -216,11 +216,16 @@ def surfaceChart(
if data.get('wireframe', False): if data.get('wireframe', False):
axis.plot_wireframe( axis.plot_wireframe(
x, y, z, rstride=1, cstride=1, cmap=cm.coolwarm # type: ignore x,
y,
z,
rstride=1,
cstride=1,
cmap=cm.coolwarm, # pylint: disable=no-member # type: ignore
) )
else: else:
axis.plot_surface( axis.plot_surface(
x, y, z, rstride=1, cstride=1, cmap=cm.coolwarm # type: ignore x, y, z, rstride=1, cstride=1, cmap=cm.coolwarm # type: ignore # pylint: disable=no-member
) )
axis.set_title(data.get('title', '')) axis.set_title(data.get('title', ''))

View File

@ -48,14 +48,20 @@ logger = logging.getLogger(__name__)
class Report(UserInterface): class Report(UserInterface):
mime_type: typing.ClassVar[str] = 'application/pdf' # Report returns pdfs by default, but could be anything else mime_type: typing.ClassVar[
str
] = 'application/pdf' # Report returns pdfs by default, but could be anything else
name: typing.ClassVar[str] = _('Base Report') # Report name name: typing.ClassVar[str] = _('Base Report') # Report name
group: typing.ClassVar[str] = '' # So we can "group" reports by kind? group: typing.ClassVar[str] = '' # So we can "group" reports by kind?
encoded: typing.ClassVar[bool] = True # If the report is mean to be encoded (binary reports as PDFs == True, text reports must be False so utf-8 is correctly threated encoded: typing.ClassVar[
bool
] = True # If the report is mean to be encoded (binary reports as PDFs == True, text reports must be False so utf-8 is correctly threated
uuid: typing.ClassVar[str] = '' uuid: typing.ClassVar[str] = ''
description: str = _('Base report') # Report description description: str = _('Base report') # Report description
filename: str = 'file.pdf' # Filename that will be returned as 'hint' on rest report request filename: str = (
'file.pdf' # Filename that will be returned as 'hint' on rest report request
)
@classmethod @classmethod
def translated_name(cls): def translated_name(cls):
@ -81,35 +87,42 @@ class Report(UserInterface):
@classmethod @classmethod
def getUuid(cls): def getUuid(cls):
if cls.uuid is None: if cls.uuid is None:
raise Exception('Class does not includes an uuid!!!: {}'.format(cls)) raise Exception(f'Class does not includes an uuid!!!: {cls}')
return cls.uuid return cls.uuid
@staticmethod @staticmethod
def asPDF(html: str, header: typing.Optional[str] = None, water: typing.Optional[str] = None, images: typing.Optional[typing.Dict[str, bytes]] = None) -> bytes: def asPDF(
html: str,
header: typing.Optional[str] = None,
water: typing.Optional[str] = None,
images: typing.Optional[typing.Dict[str, bytes]] = None,
) -> bytes:
""" """
Renders an html as PDF. Renders an html as PDF.
Uses the "report.css" as stylesheet Uses the "report.css" as stylesheet
""" """
# url fetcher for weasyprint # url fetcher for weasyprint
def report_fetcher(url: str, timeout=10, ssl_context=None) -> typing.Dict: def report_fetcher(
url: str, timeout=10, ssl_context=None # pylint: disable=unused-argument
) -> typing.Dict:
logger.debug('Getting url for weasyprint %s', url) logger.debug('Getting url for weasyprint %s', url)
if url.startswith('stock://'): if url.startswith('stock://'):
imagePath = stock.getStockImagePath(url[8:]) imagePath = stock.getStockImagePath(url[8:])
with open(imagePath, 'rb') as f: with open(imagePath, 'rb') as f:
image = f.read() image = f.read()
return dict(string=image, mime_type='image/png') return {'string': image, 'mime_type': 'image/png'}
if url.startswith('image://'): if url.startswith('image://'):
img: typing.Optional[bytes] = b'' # Empty image img: typing.Optional[bytes] = b'' # Empty image
if images: if images:
img = images.get(url[8:]) img = images.get(url[8:])
logger.debug('Getting image %s? %s', url[8:], img is not None) logger.debug('Getting image %s? %s', url[8:], img is not None)
return dict(string=img, mime_type='image/png') return {'string': img, 'mime_type': 'image/png'}
return default_url_fetcher(url) return default_url_fetcher(url)
with open(stock.getStockCssPath('report.css'), 'r') as f: with open(stock.getStockCssPath('report.css'), 'r', encoding='utf-8') as f:
css = f.read() css = f.read()
css = ( css = (
@ -117,13 +130,20 @@ class Report(UserInterface):
.replace('{page}', _('Page')) .replace('{page}', _('Page'))
.replace('{of}', _('of')) .replace('{of}', _('of'))
.replace('{water}', water or 'UDS Report') .replace('{water}', water or 'UDS Report')
.replace('{printed}', _('Printed in {now:%Y, %b %d} at {now:%H:%M}').format(now=datetime.datetime.now())) .replace(
'{printed}',
_('Printed in {now:%Y, %b %d} at {now:%H:%M}').format(
now=datetime.datetime.now()
),
)
) )
h = HTML(string=html, url_fetcher=report_fetcher) h = HTML(string=html, url_fetcher=report_fetcher)
c = CSS(string=css) c = CSS(string=css)
return typing.cast(bytes, h.write_pdf(stylesheets=[c])) # Return a new bytes object return typing.cast(
bytes, h.write_pdf(stylesheets=[c])
) # Return a new bytes object
@staticmethod @staticmethod
def templateAsPDF(templateName, dct, header=None, water=None, images=None) -> bytes: def templateAsPDF(templateName, dct, header=None, water=None, images=None) -> bytes:
@ -184,4 +204,4 @@ class Report(UserInterface):
return typing.cast(str, data) return typing.cast(str, data)
def __str__(self): def __str__(self):
return 'Report {} with uuid {}'.format(self.name, self.uuid) return f'Report {self.name} with uuid {self.uuid}'

View File

@ -32,13 +32,16 @@
""" """
import typing import typing
from uds.core.exceptions import UDSException
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from uds.models import UserService, Transport from uds.models import UserService, Transport
class ServiceException(Exception): class ServiceException(UDSException):
def __init__(self, *args, **kwargs): """
super(ServiceException, self).__init__(*args) Base class for all service exceptions
"""
class UnsupportedException(ServiceException): class UnsupportedException(ServiceException):

View File

@ -37,7 +37,6 @@ from uds.core.module import Module
from uds.core.environment import Environment from uds.core.environment import Environment
from uds.core.util import log from uds.core.util import log
from uds.core.util.config import GlobalConfig
from uds.core.ui import gui from uds.core.ui import gui
# Not imported at runtime, just for type checking # Not imported at runtime, just for type checking
@ -213,7 +212,7 @@ class ServiceProvider(Module):
""" """
Logs a message with requested level associated with this service Logs a message with requested level associated with this service
""" """
from uds.models import Provider as DBProvider from uds.models import Provider as DBProvider # pylint: disable=import-outside-toplevel
if self.getUuid(): if self.getUuid():
log.doLog( log.doLog(

View File

@ -33,10 +33,11 @@
import typing import typing
import logging import logging
from uds.core.util import factory
from .provider import ServiceProvider from .provider import ServiceProvider
from .service import Service from .service import Service
from uds.core.util import factory
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -30,6 +30,7 @@
""" """
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
""" """
import abc
import typing import typing
from uds.core.environment import Environmentable from uds.core.environment import Environmentable
@ -158,6 +159,7 @@ class Publication(Environmentable, Serializable):
def getUuid(self) -> str: def getUuid(self) -> str:
return self._uuid return self._uuid
@abc.abstractmethod
def publish(self) -> str: def publish(self) -> str:
""" """
This method is invoked whenever the administrator requests a new publication. This method is invoked whenever the administrator requests a new publication.
@ -192,11 +194,10 @@ class Publication(Environmentable, Serializable):
this method. this method.
""" """
raise NotImplementedError( raise NotImplementedError(
'publish method for class {0} not provided! '.format( f'publish method for class {self.__class__.__name__} not provided! '
self.__class__.__name__
)
) )
@abc.abstractmethod
def checkState(self) -> str: def checkState(self) -> str:
""" """
This is a task method. As that, the expected return values are This is a task method. As that, the expected return values are
@ -222,9 +223,7 @@ class Publication(Environmentable, Serializable):
this method. this method.
""" """
raise NotImplementedError( raise NotImplementedError(
'checkState method for class {0} not provided!!!'.format( f'checkState method for class {self.__class__.__name__} not provided!!!'
self.__class__.__name__
)
) )
def finish(self) -> None: def finish(self) -> None:
@ -251,6 +250,7 @@ class Publication(Environmentable, Serializable):
""" """
return 'unknown' return 'unknown'
@abc.abstractmethod
def destroy(self) -> str: def destroy(self) -> str:
""" """
This is a task method. As that, the expected return values are This is a task method. As that, the expected return values are
@ -270,9 +270,10 @@ class Publication(Environmentable, Serializable):
this method. this method.
""" """
raise NotImplementedError( raise NotImplementedError(
'destroy method for class {0} not provided!'.format(self.__class__.__name__) f'destroy method for class {self.__class__.__name__} not provided!'
) )
@abc.abstractmethod
def cancel(self) -> str: def cancel(self) -> str:
""" """
This is a task method. As that, the expected return values are This is a task method. As that, the expected return values are
@ -292,11 +293,11 @@ class Publication(Environmentable, Serializable):
this method. this method.
""" """
raise NotImplementedError( raise NotImplementedError(
'cancel method for class {0} not provided!'.format(self.__class__.__name__) f'cancel method for class {self.__class__.__name__} not provided!'
) )
def __str__(self): def __str__(self):
""" """
String method, mainly used for debugging purposes String method, mainly used for debugging purposes
""" """
return "Base Publication" return 'Base Publication'

View File

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*- # pylint: disable=unused-argument # this has a lot of "default" methods, so we need to ignore unused arguments most of the time
# #
# Copyright (c) 2012-2021 Virtual Cable S.L.U. # Copyright (c) 2012-2021 Virtual Cable S.L.U.
@ -281,9 +281,7 @@ class Service(Module):
name returned is unique :-) name returned is unique :-)
""" """
raise Exception( raise Exception(
'The class {0} has been marked as manually asignable but no requestServicesForAssignetion provided!!!'.format( f'The class {self.__class__.__name__} has been marked as manually asignable but no requestServicesForAssignetion provided!!!'
self.__class__.__name__
)
) )
def macGenerator(self) -> typing.Optional['UniqueMacGenerator']: def macGenerator(self) -> typing.Optional['UniqueMacGenerator']:
@ -313,7 +311,9 @@ class Service(Module):
return [] return []
def assignFromAssignables( def assignFromAssignables(
self, assignableId: str, user: 'models.User', userDeployment: UserDeployment self, assignableId: str,
user: 'models.User',
userDeployment: UserDeployment
) -> str: ) -> str:
""" """
Assigns from it internal assignable list to an user Assigns from it internal assignable list to an user
@ -398,7 +398,7 @@ class Service(Module):
""" """
Logs a message with requested level associated with this service Logs a message with requested level associated with this service
""" """
from uds.models import Service as DBService from uds.models import Service as DBService # pylint: disable=import-outside-toplevel
if self.getUuid(): if self.getUuid():
log.doLog( log.doLog(

View File

@ -271,7 +271,9 @@ class UserDeployment(Environmentable, Serializable):
""" """
raise NotImplementedError('Base getUniqueId for User Deployment called!!!') raise NotImplementedError('Base getUniqueId for User Deployment called!!!')
def notifyReadyFromOsManager(self, data: typing.Any) -> str: def notifyReadyFromOsManager(
self, data: typing.Any # pylint: disable=unused-argument
) -> str:
""" """
This is a task method. As that, the excepted return values are This is a task method. As that, the excepted return values are
State values RUNNING, FINISHED or ERROR. State values RUNNING, FINISHED or ERROR.
@ -388,9 +390,7 @@ class UserDeployment(Environmentable, Serializable):
this method. this method.
""" """
raise Exception( raise Exception(
'Base deploy for cache invoked! for class {0}'.format( f'Base deploy for cache invoked! for class {self.__class__.__name__}'
self.__class__.__name__
)
) )
def deployForUser(self, user: 'models.User') -> str: def deployForUser(self, user: 'models.User') -> str:
@ -427,9 +427,7 @@ class UserDeployment(Environmentable, Serializable):
this method. this method.
""" """
raise NotImplementedError( raise NotImplementedError(
'Base deploy for user invoked! for class {0}'.format( f'Base deploy for user invoked! for class {self.__class__.__name__}'
self.__class__.__name__
)
) )
def checkState(self) -> str: def checkState(self) -> str:
@ -456,7 +454,7 @@ class UserDeployment(Environmentable, Serializable):
this method. this method.
""" """
raise NotImplementedError( raise NotImplementedError(
'Base check state invoked! for class {0}'.format(self.__class__.__name__) f'Base check state invoked! for class {self.__class__.__name__}'
) )
def finish(self) -> None: def finish(self) -> None:
@ -473,7 +471,7 @@ class UserDeployment(Environmentable, Serializable):
nothing) nothing)
""" """
def moveToCache(self, newLevel: int) -> str: def moveToCache(self, newLevel: int) -> str: # pylint: disable=unused-argument
""" """
This method is invoked whenever the core needs to move from the current This method is invoked whenever the core needs to move from the current
cache level to a new cache level an user deployment. cache level to a new cache level an user deployment.
@ -564,7 +562,7 @@ class UserDeployment(Environmentable, Serializable):
this method. this method.
""" """
raise NotImplementedError( raise NotImplementedError(
'destroy method for class {0} not provided!'.format(self.__class__.__name__) f'destroy method for class {self.__class__.__name__} not provided!'
) )
def cancel(self) -> str: def cancel(self) -> str:
@ -631,6 +629,7 @@ class UserDeployment(Environmentable, Serializable):
monitors: number of monitors to use monitors: number of monitors to use
""" """
return None return None
def __str__(self): def __str__(self):
""" """
Mainly used for debugging purposses Mainly used for debugging purposses

View File

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*- # pylint: disable=unused-argument # this has a lot of "default" methods, so we need to ignore unused arguments most of the time
# #
# Copyright (c) 2012-2022 Virtual Cable S.L.U. # Copyright (c) 2012-2022 Virtual Cable S.L.U.
@ -30,7 +30,6 @@
""" """
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
""" """
import os
import sys import sys
import codecs import codecs
import logging import logging
@ -156,7 +155,7 @@ class Transport(Module):
Returns a customized error message, that will be used when a service fails to check "isAvailableFor" Returns a customized error message, that will be used when a service fails to check "isAvailableFor"
Override this in yours transports if needed Override this in yours transports if needed
""" """
return "Not accessible (using service ip {})".format(ip) return f'Not accessible (using service ip {ip})'
@classmethod @classmethod
def supportsProtocol(cls, protocol: typing.Union[typing.Iterable, str]): def supportsProtocol(cls, protocol: typing.Union[typing.Iterable, str]):
@ -229,7 +228,7 @@ class Transport(Module):
userService: 'models.UserService', userService: 'models.UserService',
transport: 'models.Transport', transport: 'models.Transport',
ip: str, ip: str,
os: 'DetectedOsInfo', os: 'DetectedOsInfo', # pylint: disable=redefined-outer-name
user: 'models.User', user: 'models.User',
password: str, password: str,
request: 'ExtendedHttpRequestWithUser', request: 'ExtendedHttpRequestWithUser',
@ -263,7 +262,7 @@ class Transport(Module):
userService: 'models.UserService', userService: 'models.UserService',
transport: 'models.Transport', transport: 'models.Transport',
ip: str, ip: str,
os: 'DetectedOsInfo', os: 'DetectedOsInfo', # pylint: disable=redefined-outer-name
user: 'models.User', user: 'models.User',
password: str, password: str,
request: 'ExtendedHttpRequestWithUser', request: 'ExtendedHttpRequestWithUser',
@ -296,12 +295,16 @@ class Transport(Module):
params: Parameters for the return tuple params: Parameters for the return tuple
""" """
# Reads script and signature # Reads script and signature
import os # pylint: disable=import-outside-toplevel
basePath = os.path.dirname( basePath = os.path.dirname(
sys.modules[self.__module__].__file__ or 'not_found' sys.modules[self.__module__].__file__ or 'not_found'
) # Will raise if not found ) # Will raise if not found
script = open(os.path.join(basePath, scriptName), 'r').read() with open(os.path.join(basePath, scriptName), 'r', encoding='utf8') as scriptFile:
signature = open(os.path.join(basePath, scriptName + '.signature'), 'r').read() script = scriptFile.read()
with open(os.path.join(basePath, scriptName + '.signature'), 'r', encoding='utf8') as signatureFile:
signature = signatureFile.read()
return TransportScript( return TransportScript(
script=script, script=script,
@ -320,7 +323,7 @@ class Transport(Module):
Returns a script for the given os and type Returns a script for the given os and type
""" """
return self.getRelativeScript( return self.getRelativeScript(
'scripts/{os}/{type}.py'.format(os=osName, type=type), params f'scripts/{osName}/{type}.py', params
) )
def getLink( def getLink(
@ -328,7 +331,7 @@ class Transport(Module):
userService: 'models.UserService', userService: 'models.UserService',
transport: 'models.Transport', transport: 'models.Transport',
ip: str, ip: str,
os: 'DetectedOsInfo', os: 'DetectedOsInfo', # pylint: disable=redefined-outer-name
user: 'models.User', user: 'models.User',
password: str, password: str,
request: 'ExtendedHttpRequestWithUser', request: 'ExtendedHttpRequestWithUser',

View File

@ -2,4 +2,5 @@
# Allow old serialized autoattributes to load correctly (register path...) # Allow old serialized autoattributes to load correctly (register path...)
# pylint: disable=unused-wildcard-import,wildcard-import
from .auto_attributes import * from .auto_attributes import *

View File

@ -105,7 +105,9 @@ class AutoAttributes(Serializable):
return return
# We keep original data (maybe incomplete) # We keep original data (maybe incomplete)
if data[:2] == b'v1': if data[:2] == b'v1':
self.attrs = pickle.loads(data[2:]) # nosec: pickle is used to load data from trusted source self.attrs = pickle.loads(
data[2:]
) # nosec: pickle is used to load data from trusted source
return return
# We try to load as v0 # We try to load as v0
try: try:
@ -117,16 +119,18 @@ class AutoAttributes(Serializable):
k, v = pair.split(b'\1') k, v = pair.split(b'\1')
# logger.debug('k: %s --- v: %s', k, v) # logger.debug('k: %s --- v: %s', k, v)
try: try:
self.attrs[k.decode()] = pickle.loads(v) # nosec: pickle is used to load data from trusted source self.attrs[k.decode()] = pickle.loads(
v
) # nosec: pickle is used to load data from trusted source
except Exception: # Old encoding on python2, set encoding for loading except Exception: # Old encoding on python2, set encoding for loading
self.attrs[k.decode()] = pickle.loads(v, encoding='utf8') # nosec: pickle is used to load data from trusted source self.attrs[k.decode()] = pickle.loads(
v, encoding='utf8'
) # nosec: pickle is used to load data from trusted source
def __repr__(self) -> str: def __repr__(self) -> str:
return ( return (
'AutoAttributes(' 'AutoAttributes('
+ ', '.join( + ', '.join(f'{k}={v.getType().__name__}' for k, v in self.attrs.items())
'{}={}'.format(k, v.getType().__name__) for k, v in self.attrs.items()
)
+ ')' + ')'
) )
@ -134,7 +138,7 @@ class AutoAttributes(Serializable):
return ( return (
'<AutoAttribute ' '<AutoAttribute '
+ ','.join( + ','.join(
'{} ({}) = {}'.format(k, v.getType(), v.getStrValue()) f'{k} ({v.getType()}) = {v.getStrValue()}'
for k, v in self.attrs.items() for k, v in self.attrs.items()
) )
+ '>' + '>'

View File

@ -59,6 +59,7 @@ from cryptography import fernet
from django.conf import settings from django.conf import settings
# pylint: disable=too-few-public-methods
class _Unassigned: class _Unassigned:
pass pass
@ -97,7 +98,7 @@ def fernet_key(crypt_key: bytes) -> str:
# First, with seed + password, generate a 32 bytes key based on seed + password # First, with seed + password, generate a 32 bytes key based on seed + password
return base64.b64encode(hashlib.sha256(crypt_key).digest()).decode() return base64.b64encode(hashlib.sha256(crypt_key).digest()).decode()
# pylint: disable=unnecessary-dunder-call
class _SerializableField(typing.Generic[T]): class _SerializableField(typing.Generic[T]):
name: str name: str
type: typing.Type[T] type: typing.Type[T]
@ -110,10 +111,9 @@ class _SerializableField(typing.Generic[T]):
def _default(self) -> T: def _default(self) -> T:
if isinstance(self.default, _Unassigned): if isinstance(self.default, _Unassigned):
return self.type() return self.type()
elif callable(self.default): if callable(self.default):
return self.default() return self.default()
else: return self.default
return self.default
def __get__( def __get__(
self, self,
@ -309,13 +309,12 @@ class PasswordField(StringField):
class _FieldNameSetter(type): class _FieldNameSetter(type):
"""Simply adds the name of the field in the class to the field object""" """Simply adds the name of the field in the class to the field object"""
def __new__(cls, name, bases, attrs): def __new__(mcs, name, bases, attrs):
fields = dict()
for k, v in attrs.items(): for k, v in attrs.items():
if isinstance(v, _SerializableField): if isinstance(v, _SerializableField):
v.name = k v.name = k
return super().__new__(cls, name, bases, attrs) return super().__new__(mcs, name, bases, attrs)
class AutoSerializable(metaclass=_FieldNameSetter): class AutoSerializable(metaclass=_FieldNameSetter):
@ -346,8 +345,6 @@ class AutoSerializable(metaclass=_FieldNameSetter):
""" """
return bytes(a ^ b for a, b in zip(data, itertools.cycle(header))) return bytes(a ^ b for a, b in zip(data, itertools.cycle(header)))
return data
def unprocess_data(self, header: bytes, data: bytes) -> bytes: def unprocess_data(self, header: bytes, data: bytes) -> bytes:
"""Process data after unmarshalling """Process data after unmarshalling
@ -367,7 +364,7 @@ class AutoSerializable(metaclass=_FieldNameSetter):
def marshal(self) -> bytes: def marshal(self) -> bytes:
# Iterate over own members and extract fields # Iterate over own members and extract fields
fields = {} fields = {}
for k, v in self.__class__.__dict__.items(): for _, v in self.__class__.__dict__.items():
if isinstance(v, _SerializableField): if isinstance(v, _SerializableField):
fields[v.name] = (str(v.__class__.__name__), v.marshal(self)) fields[v.name] = (str(v.__class__.__name__), v.marshal(self))
@ -435,16 +432,17 @@ class AutoSerializable(metaclass=_FieldNameSetter):
# Remove from data # Remove from data
data = data[8 + name_len + type_name_len + data_len :] data = data[8 + name_len + type_name_len + data_len :]
for k, v in self.__class__.__dict__.items(): for _, v in self.__class__.__dict__.items():
if isinstance(v, _SerializableField): if isinstance(v, _SerializableField):
if v.name in fields and fields[v.name][0] == str(v.__class__.__name__): if v.name in fields and fields[v.name][0] == str(v.__class__.__name__):
v.unmarshal(self, fields[v.name][1]) v.unmarshal(self, fields[v.name][1])
else: else:
if not v.name in fields: if not v.name in fields:
logger.warning(f"Field {v.name} not found in unmarshalled data") logger.warning('Field %s not found in unmarshalled data', v.name)
else: else:
logger.warning( logger.warning(
f"Field {v.name} has wrong type in unmarshalled data (should be {fields[v.name][0]} and is {v.__class__.name}" 'Field %s has wrong type in unmarshalled data (should be %s and is %s',
v.name, fields[v.name][0], v.__class__.__name__,
) )
def __eq__(self, other: typing.Any) -> bool: def __eq__(self, other: typing.Any) -> bool:

View File

@ -107,10 +107,9 @@ class Cache:
Cache.misses += 1 Cache.misses += 1
# logger.debug('key not found: %s', skey) # logger.debug('key not found: %s', skey)
return defValue return defValue
except Exception as e: except Exception:
logger.exception('Error getting cache key: %s', skey) logger.exception('Error getting cache key: %s', skey)
Cache.misses += 1 Cache.misses += 1
# logger.debug('Cache inaccesible: %s:%s', skey, e)
return defValue return defValue
def __getitem__(self, key: typing.Union[str, bytes]) -> typing.Any: def __getitem__(self, key: typing.Union[str, bytes]) -> typing.Any:

View File

@ -55,7 +55,7 @@ ONE_DAY = 3600 * 24
class CalendarChecker: class CalendarChecker:
__slots__ = ('calendar',) __slots__ = ('calendar',)
calendar: Calendar calendar: Calendar
# For performance checking # For performance checking
@ -225,13 +225,12 @@ class CalendarChecker:
return next_event return next_event
def debug(self) -> str: def debug(self) -> str:
return "Calendar checker for {}".format(self.calendar) return f'Calendar checker for {self.calendar}'
@staticmethod @staticmethod
def _cacheKey(key: str) -> str: def _cacheKey(key: str) -> str:
# Returns a valid cache key for all caching backends (memcached, redis, or whatever) # Returns a valid cache key for all caching backends (memcached, redis, or whatever)
# Simple, fastest algorihm is to use md5 # Simple, fastest algorihm is to use md5
h = hashlib.md5() # nosec Thisis just a simpele cache key, no need to use a more secure algorithm return hashlib.md5(
h.update(key.encode('utf-8')) key.encode('utf-8')
return h.hexdigest() ).hexdigest() # nosec simple fast algorithm for cache keys

View File

@ -150,10 +150,10 @@ class Config:
self._data = readed.value self._data = readed.value
self._crypt = readed.crypt or self._crypt self._crypt = readed.crypt or self._crypt
self._longText = readed.long self._longText = readed.long
if self._type != -1 and self._type != readed.field_type: if self._type not in (-1, readed.field_type):
readed.field_type = self._type readed.field_type = self._type
readed.save(update_fields=['field_type']) readed.save(update_fields=['field_type'])
if self._help != '' and self._help != readed.help: if self._help not in ('', readed.help):
readed.help = self._help readed.help = self._help
readed.save( readed.save(
update_fields=['help'] update_fields=['help']
@ -169,9 +169,7 @@ class Config:
self._data = self._default self._data = self._default
except Exception as e: except Exception as e:
logger.info( logger.info(
'Error accessing db config {0}.{1}'.format( 'Error accessing db config %s.%s', self._section.name(), self._key
self._section.name(), self._key
)
) )
logger.exception(e) logger.exception(e)
self._data = self._default self._data = self._default
@ -273,7 +271,7 @@ class Config:
self._data = value self._data = value
def __str__(self) -> str: def __str__(self) -> str:
return '{}.{}'.format(self._section.name(), self._key) return f'{self._section.name()}.{self._key}'
@staticmethod @staticmethod
def section(sectionName): def section(sectionName):
@ -314,7 +312,10 @@ class Config:
cfg: DBConfig = DBConfig.objects.filter(section=section, key=key)[ cfg: DBConfig = DBConfig.objects.filter(section=section, key=key)[
0 # type: ignore # Slicing is not supported by pylance right now 0 # type: ignore # Slicing is not supported by pylance right now
] ]
if checkType and cfg.field_type in (Config.FieldType.READ, Config.FieldType.HIDDEN): if checkType and cfg.field_type in (
Config.FieldType.READ,
Config.FieldType.HIDDEN,
):
return False # Skip non writable elements return False # Skip non writable elements
if cfg.crypt: if cfg.crypt:
@ -565,14 +566,18 @@ class GlobalConfig:
'statsAccumFrequency', 'statsAccumFrequency',
'14400', '14400',
type=Config.FieldType.NUMERIC, type=Config.FieldType.NUMERIC,
help=_('Frequency of stats collection in seconds. Default is 4 hours (14400 seconds)'), help=_(
'Frequency of stats collection in seconds. Default is 4 hours (14400 seconds)'
),
) )
# Statisctis accumulation chunk size, in days # Statisctis accumulation chunk size, in days
STATS_ACCUM_MAX_CHUNK_TIME = Config.section(GLOBAL_SECTION).value( STATS_ACCUM_MAX_CHUNK_TIME = Config.section(GLOBAL_SECTION).value(
'statsAccumMaxChunkTime', 'statsAccumMaxChunkTime',
'7', '7',
type=Config.FieldType.NUMERIC, type=Config.FieldType.NUMERIC,
help=_('Maximum number of time to accumulate on one run. Default is 7 (1 week)'), help=_(
'Maximum number of time to accumulate on one run. Default is 7 (1 week)'
),
) )
# If disallow login showing authenticatiors # If disallow login showing authenticatiors
@ -716,7 +721,10 @@ class GlobalConfig:
help=_('Custom CSS styles applied to the user accesible site'), help=_('Custom CSS styles applied to the user accesible site'),
) )
SITE_INFO: Config.Value = Config.section(CUSTOM_SECTION).value( SITE_INFO: Config.Value = Config.section(CUSTOM_SECTION).value(
'Site information', '', type=Config.FieldType.LONGTEXT, help=_('Site information') 'Site information',
'',
type=Config.FieldType.LONGTEXT,
help=_('Site information'),
) )
SITE_FILTER_ONTOP: Config.Value = Config.section(CUSTOM_SECTION).value( SITE_FILTER_ONTOP: Config.Value = Config.section(CUSTOM_SECTION).value(
'Show Filter on Top', 'Show Filter on Top',
@ -781,7 +789,9 @@ class GlobalConfig:
for v in GlobalConfig.__dict__.values(): for v in GlobalConfig.__dict__.values():
if isinstance(v, Config.Value): if isinstance(v, Config.Value):
v.get() v.get()
logger.debug('Initialized global config value %s=%s', v.key(), v.get()) logger.debug(
'Initialized global config value %s=%s', v.key(), v.get()
)
for c in _getLater: for c in _getLater:
logger.debug('Get later: %s', c) logger.debug('Get later: %s', c)

View File

@ -30,11 +30,7 @@
""" """
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
""" """
import typing
import logging
import socket
logger = logging.getLogger(__name__) # Import alias
# pylint: disable=unused-import
# import Alias
from .net import testConnection as testServer from .net import testConnection as testServer

View File

@ -247,7 +247,7 @@ def allowCache(
keyFnc(args[0] if len(args) > 0 else fnc.__name__).encode('utf-8') keyFnc(args[0] if len(args) > 0 else fnc.__name__).encode('utf-8')
) )
# compute cache key # compute cache key
cacheKey = '{}-{}'.format(cachePrefix, keyHash.hexdigest()) cacheKey = f'{cachePrefix}-{keyHash.hexdigest()}'
cache = getattr(args[0], 'cache', None) or Cache('functionCache') cache = getattr(args[0], 'cache', None) or Cache('functionCache')

View File

@ -41,7 +41,7 @@ def ensure_list(obj: typing.Union[T, typing.Iterable[T]]) -> typing.List[T]:
return [typing.cast(T, obj)] return [typing.cast(T, obj)]
try: try:
return [_ for _ in typing.cast(typing.List[T], obj)] return list(typing.cast(typing.List[T], obj))
except Exception: # Not iterable except Exception: # Not iterable
return [typing.cast(T, obj)] return [typing.cast(T, obj)]

View File

@ -1,19 +1,21 @@
import typing import typing
import logging
from uds.core.util import singleton from uds.core.util import singleton
from uds.core import module from uds.core import module
import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
T = typing.TypeVar('T', bound=module.Module) T = typing.TypeVar('T', bound=module.Module)
V = typing.TypeVar('V') V = typing.TypeVar('V')
class Factory(typing.Generic[V], metaclass=singleton.Singleton): class Factory(typing.Generic[V], metaclass=singleton.Singleton):
''' '''
Generic factory class. Generic factory class.
''' '''
_objects: typing.MutableMapping[str, typing.Type[V]] _objects: typing.MutableMapping[str, typing.Type[V]]
def __init__(self) -> None: def __init__(self) -> None:
@ -46,11 +48,11 @@ class Factory(typing.Generic[V], metaclass=singleton.Singleton):
__getitem__ = get __getitem__ = get
class ModuleFactory(Factory[T]): class ModuleFactory(Factory[T]):
''' '''
Module Factory class. Module Factory class.
''' '''
def providers(self) -> typing.Mapping[str, typing.Type[T]]: def providers(self) -> typing.Mapping[str, typing.Type[T]]:
''' '''
Returns all providers. Returns all providers.

View File

@ -15,12 +15,13 @@
# Modified to add type checking, fix bugs, etc.. by dkmaster@dkmon.com # Modified to add type checking, fix bugs, etc.. by dkmaster@dkmon.com
# pylint: disable=protected-access,too-few-public-methods,unused-argument
import sys import sys
import os import os
import ctypes import ctypes
import errno import errno
import typing import typing
import warnings
import logging import logging
from ctypes.util import find_library from ctypes.util import find_library
@ -44,29 +45,26 @@ if _system == 'Windows':
# #
# We have to fix up c_long and c_ulong so that it matches the # We have to fix up c_long and c_ulong so that it matches the
# Cygwin (and UNIX) sizes when run on Windows. # Cygwin (and UNIX) sizes when run on Windows.
import sys
if sys.maxsize > 0xFFFFFFFF: if sys.maxsize > 0xFFFFFFFF:
c_win_long = ctypes.c_int64 c_win_long = ctypes.c_int64
c_win_ulong = ctypes.c_uint64 c_win_ulong = ctypes.c_uint64
if _system == 'Windows' or _system.startswith('CYGWIN'): if _system == 'Windows' or _system.startswith('CYGWIN'):
class c_timespec(ctypes.Structure): # type: ignore class c_timespec(ctypes.Structure): # type: ignore # pylint: disable=too-few-public-methods
_fields_ = [('tv_sec', c_win_long), ('tv_nsec', c_win_long)] _fields_ = [('tv_sec', c_win_long), ('tv_nsec', c_win_long)]
else: else:
class c_timespec(ctypes.Structure): # type: ignore class c_timespec(ctypes.Structure): # type: ignore # pylint: disable=too-few-public-methods
_fields_ = [('tv_sec', ctypes.c_long), ('tv_nsec', ctypes.c_long)] _fields_ = [('tv_sec', ctypes.c_long), ('tv_nsec', ctypes.c_long)]
class c_utimbuf(ctypes.Structure): class c_utimbuf(ctypes.Structure): # type: ignore # pylint: disable=too-few-public-methods
_fields_ = [('actime', c_timespec), ('modtime', c_timespec)] _fields_ = [('actime', c_timespec), ('modtime', c_timespec)]
class c_stat(ctypes.Structure): class c_stat(ctypes.Structure): # type: ignore # pylint: disable=too-few-public-methods
pass # Platform dependent pass # Platform dependent
@ -92,7 +90,7 @@ if not _libfuse_path:
rootkey, keyname, 0, reg.KEY_READ | reg.KEY_WOW64_32KEY # type: ignore rootkey, keyname, 0, reg.KEY_READ | reg.KEY_WOW64_32KEY # type: ignore
) )
val = str(reg.QueryValueEx(key, valname)[0]) # type: ignore val = str(reg.QueryValueEx(key, valname)[0]) # type: ignore
except WindowsError: # type: ignore except WindowsError: # type: ignore # pylint: disable=undefined-variable
pass pass
finally: finally:
if key is not None: if key is not None:
@ -103,16 +101,19 @@ if not _libfuse_path:
reg.HKEY_LOCAL_MACHINE, r"SOFTWARE\WinFsp", r"InstallDir" # type: ignore reg.HKEY_LOCAL_MACHINE, r"SOFTWARE\WinFsp", r"InstallDir" # type: ignore
) )
if _libfuse_path: if _libfuse_path:
_libfuse_path += r"bin\winfsp-%s.dll" % ( _libfuse_path += (
"x64" if sys.maxsize > 0xFFFFFFFF else "x86" r"bin\winfsp-%s.dll" # pylint: disable=consider-using-f-string
% ( # pylint: disable=consider-using-f-string
"x64" if sys.maxsize > 0xFFFFFFFF else "x86"
)
) )
else: else:
_libfuse_path = find_library('fuse') _libfuse_path = find_library('fuse')
if not _libfuse_path: if not _libfuse_path:
raise EnvironmentError('Unable to find libfuse') raise EnvironmentError('Unable to find libfuse')
else:
_libfuse = ctypes.CDLL(_libfuse_path) _libfuse = ctypes.CDLL(_libfuse_path)
if _system == 'Darwin' and hasattr(_libfuse, 'macfuse_version'): if _system == 'Darwin' and hasattr(_libfuse, 'macfuse_version'):
_system = 'Darwin-MacFuse' _system = 'Darwin-MacFuse'
@ -131,7 +132,6 @@ getxattr_t: typing.Type
if _system in ('Darwin', 'Darwin-MacFuse', 'FreeBSD'): if _system in ('Darwin', 'Darwin-MacFuse', 'FreeBSD'):
ENOTSUP = 45 ENOTSUP = 45
c_dev_t = ctypes.c_int32 c_dev_t = ctypes.c_int32
c_fsblkcnt_t = ctypes.c_ulong c_fsblkcnt_t = ctypes.c_ulong
c_fsfilcnt_t = ctypes.c_ulong c_fsfilcnt_t = ctypes.c_ulong
@ -295,7 +295,7 @@ elif _system == 'Linux':
('st_mtimespec', c_timespec), ('st_mtimespec', c_timespec),
('st_ctimespec', c_timespec), ('st_ctimespec', c_timespec),
] ]
elif _machine == 'ppc64' or _machine == 'ppc64le': elif _machine in ('ppc64', 'ppc64le'):
c_stat._fields_ = [ c_stat._fields_ = [
('st_dev', c_dev_t), ('st_dev', c_dev_t),
('st_ino', ctypes.c_ulong), ('st_ino', ctypes.c_ulong),
@ -392,7 +392,7 @@ elif _system == 'Windows' or _system.startswith('CYGWIN'):
('st_birthtimespec', c_timespec), ('st_birthtimespec', c_timespec),
] ]
else: else:
raise NotImplementedError('%s is not supported.' % _system) raise NotImplementedError(f'{_system} is not supported.')
if _system == 'FreeBSD': if _system == 'FreeBSD':
@ -428,7 +428,6 @@ if _system == 'FreeBSD':
('f_frsize', ctypes.c_ulong), ('f_frsize', ctypes.c_ulong),
] ]
elif _system == 'Windows' or _system.startswith('CYGWIN'): elif _system == 'Windows' or _system.startswith('CYGWIN'):
class c_statvfs(ctypes.Structure): # type: ignore class c_statvfs(ctypes.Structure): # type: ignore
@ -446,7 +445,6 @@ elif _system == 'Windows' or _system.startswith('CYGWIN'):
('f_namemax', c_win_ulong), ('f_namemax', c_win_ulong),
] ]
else: else:
class c_statvfs(ctypes.Structure): # type: ignore class c_statvfs(ctypes.Structure): # type: ignore
@ -481,7 +479,6 @@ if _system == 'Windows' or _system.startswith('CYGWIN'):
('lock_owner', ctypes.c_uint64), ('lock_owner', ctypes.c_uint64),
] ]
else: else:
class fuse_file_info(ctypes.Structure): # type: ignore class fuse_file_info(ctypes.Structure): # type: ignore
@ -713,7 +710,7 @@ class FuseOperations(ctypes.Structure):
def time_of_timespec(ts: c_timespec, use_ns=False): def time_of_timespec(ts: c_timespec, use_ns=False):
return ts.tv_sec * 10 ** 9 + ts.tv_nsec # type: ignore return ts.tv_sec * 10**9 + ts.tv_nsec # type: ignore
def set_st_attrs(st: c_stat, attrs: typing.Mapping[str, int]) -> None: def set_st_attrs(st: c_stat, attrs: typing.Mapping[str, int]) -> None:
@ -723,7 +720,7 @@ def set_st_attrs(st: c_stat, attrs: typing.Mapping[str, int]) -> None:
if timespec is None: if timespec is None:
continue continue
timespec.tv_sec, timespec.tv_nsec = divmod(int(val), 10 ** 9) timespec.tv_sec, timespec.tv_nsec = divmod(int(val), 10**9)
elif hasattr(st, key): elif hasattr(st, key):
setattr(st, key, val) setattr(st, key, val)
@ -749,10 +746,11 @@ def fuse_exit():
class FuseOSError(OSError): class FuseOSError(OSError):
def __init__(self, errno): def __init__(self, errno): # pylint: disable=redefined-outer-name
super(FuseOSError, self).__init__(errno, os.strerror(errno)) super().__init__(errno, os.strerror(errno))
# pylint: disable=too-many-public-methods
class FUSE: class FUSE:
''' '''
This class is the lower level interface and should not be subclassed under This class is the lower level interface and should not be subclassed under
@ -776,7 +774,6 @@ class FUSE:
encoding: typing.Optional[str] = None, encoding: typing.Optional[str] = None,
**kwargs, **kwargs,
) -> None: ) -> None:
''' '''
Setting raw_fi to True will cause FUSE to pass the fuse_file_info Setting raw_fi to True will cause FUSE to pass the fuse_file_info
class as is to Operations, instead of just the fh field. class as is to Operations, instead of just the fh field.
@ -839,8 +836,8 @@ class FUSE:
del self.operations # Invoke the destructor del self.operations # Invoke the destructor
if type(self.__critical_exception) is not Exception: if not isinstance(self.__critical_exception, Exception):
raise self.__critical_exception raise self.__critical_exception # type: ignore
if err: if err:
raise RuntimeError(err) raise RuntimeError(err)
@ -863,37 +860,35 @@ class FUSE:
# private_data field of struct fuse_context # private_data field of struct fuse_context
return func(*args, **kwargs) or 0 return func(*args, **kwargs) or 0
else: try:
try: return func(*args, **kwargs) or 0
return func(*args, **kwargs) or 0
except OSError as e: except OSError as e:
if e.errno > 0: if e.errno > 0:
logger.debug( logger.debug(
"FUSE operation %s raised a %s, returning errno %s.", "FUSE operation %s raised a %s, returning errno %s.",
func.__name__,
type(e),
e.errno,
)
return -e.errno
else:
logger.error(
"FUSE operation %s raised an OSError with negative "
"errno %s, returning errno.EINVAL.",
func.__name__,
e.errno,
exc_info=True,
)
return -errno.EINVAL
except Exception:
logger.error(
"Uncaught exception from FUSE operation %s, "
"returning errno.EINVAL.",
func.__name__, func.__name__,
exc_info=True, type(e),
e.errno,
) )
return -errno.EINVAL return -e.errno
logger.error(
"FUSE operation %s raised an OSError with negative "
"errno %s, returning errno.EINVAL.",
func.__name__,
e.errno,
exc_info=True,
)
return -errno.EINVAL
except Exception:
logger.error(
"Uncaught exception from FUSE operation %s, "
"returning errno.EINVAL.",
func.__name__,
exc_info=True,
)
return -errno.EINVAL
except BaseException as e: except BaseException as e:
if len(args) > 0 and isinstance(args[0], FUSE): if len(args) > 0 and isinstance(args[0], FUSE):
@ -1003,9 +998,7 @@ class FUSE:
logger.debug('Read operation on %s returned %d bytes', path, retsize) logger.debug('Read operation on %s returned %d bytes', path, retsize)
if retsize > size: if retsize > size:
raise RuntimeError( raise RuntimeError(f'read too much data ({retsize} bytes, expected {size})')
"read too much data ({} bytes, expected {})".format(retsize, size)
)
ctypes.memmove(buf, ret, retsize) ctypes.memmove(buf, ret, retsize)
return retsize return retsize
@ -1271,8 +1264,8 @@ class Operations:
def __call__(self, op: str, *args) -> typing.Any: def __call__(self, op: str, *args) -> typing.Any:
try: try:
return getattr(self, op)(*args) return getattr(self, op)(*args)
except AttributeError: except AttributeError as e:
raise FuseOSError(errno.EFAULT) raise FuseOSError(errno.EFAULT) from e
def access(self, path: str, amode: int) -> None: def access(self, path: str, amode: int) -> None:
return return
@ -1297,8 +1290,7 @@ class Operations:
raise FuseOSError(errno.EROFS) raise FuseOSError(errno.EROFS)
def destroy(self, path: str) -> None: def destroy(self, path: str) -> None:
'Called on filesystem destruction. Path is always /' '''Called on filesystem destruction. Path is always /'''
pass
def flush(self, path: typing.Optional[str], fh: typing.Any) -> None: def flush(self, path: typing.Optional[str], fh: typing.Any) -> None:
pass pass
@ -1325,7 +1317,7 @@ class Operations:
if path != '/': if path != '/':
raise FuseOSError(errno.ENOENT) raise FuseOSError(errno.ENOENT)
return dict(st_mode=(S_IFDIR | 0o755), st_nlink=2) return {'st_mode': (S_IFDIR | 0o755), 'st_nlink': 2}
def getxattr(self, path: str, name: str, position: int = 0) -> str: def getxattr(self, path: str, name: str, position: int = 0) -> str:
raise FuseOSError(ENOTSUP) raise FuseOSError(ENOTSUP)
@ -1336,7 +1328,6 @@ class Operations:
Use it instead of __init__ if you start threads on initialization. Use it instead of __init__ if you start threads on initialization.
''' '''
pass
def ioctl( def ioctl(
self, path: str, cmd: bytes, arg: bytes, fip: int, flags: int, data: bytes self, path: str, cmd: bytes, arg: bytes, fip: int, flags: int, data: bytes

View File

@ -50,6 +50,6 @@ def hash_key(key: typing.Union[str, bytes]) -> str:
Returns a hash of the given key Returns a hash of the given key
""" """
if isinstance(key, str): if isinstance(key, str):
return hasher(key.encode('utf-8')).hexdigest() key = key.encode('utf-8')
return hasher(key).hexdigest() return hasher(key).hexdigest()

View File

@ -43,24 +43,23 @@ logger = logging.getLogger(__name__)
def udsLink(request: 'HttpRequest', ticket: str, scrambler: str) -> str: def udsLink(request: 'HttpRequest', ticket: str, scrambler: str) -> str:
if request.is_secure(): if request.is_secure():
proto = 'udss' proto = 'udss'
else: else:
proto = 'uds' proto = 'uds'
return "{}://{}{}/{}".format( return f'{proto}://{request.build_absolute_uri("/")}{ticket}/{scrambler}'
proto, request.build_absolute_uri('/').split('//')[1], ticket, scrambler
)
def udsAccessLink( def udsAccessLink(
request: 'HttpRequest', serviceId: str, transportId: typing.Optional[str] request: 'HttpRequest', # pylint: disable=unused-argument
serviceId: str,
transportId: typing.Optional[str],
) -> str: ) -> str:
''' '''
If transportId (uuid) is None, this will be a metaLink If transportId (uuid) is None, this will be a metaLink
''' '''
return 'udsa://{}/{}'.format(serviceId, transportId or 'meta') return f'udsa://{serviceId}/{transportId or "meta"}'
def parseDate(dateToParse) -> datetime.date: def parseDate(dateToParse) -> datetime.date:
@ -97,4 +96,3 @@ def extractKey(dictionary: typing.Dict, key: typing.Any, **kwargs) -> str:
else: else:
value = default value = default
return value return value

View File

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*- # pylint: disable=no-member
# #
# Copyright (c) 2016-2021 Virtual Cable S.L.U. # Copyright (c) 2016-2021 Virtual Cable S.L.U.
@ -36,6 +36,13 @@ import tempfile
import os.path import os.path
import ldap.filter import ldap.filter
from ldap import (
SCOPE_BASE, # type: ignore
SCOPE_SUBTREE, # type: ignore
SCOPE_ONELEVEL, # type: ignore
# SCOPE_SUBORDINATE, # type: ignore
)
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from uds.core.util import tools from uds.core.util import tools
@ -44,23 +51,16 @@ logger = logging.getLogger(__name__)
LDAPResultType = typing.MutableMapping[str, typing.Any] LDAPResultType = typing.MutableMapping[str, typing.Any]
from ldap import (
SCOPE_BASE, # type: ignore
SCOPE_SUBTREE, # type: ignore
SCOPE_ONELEVEL, # type: ignore
SCOPE_SUBORDINATE, # type: ignore
)
class LDAPError(Exception): class LDAPError(Exception):
@staticmethod @staticmethod
def reraise(e: typing.Any): def reraise(e: typing.Any):
_str = _('Connection error: ') _str = _('Connection error: ')
if hasattr(e, 'message') and isinstance(e.message, dict): if hasattr(e, 'message') and isinstance(e.message, dict):
_str += '{}, {}'.format(e.message.get('info', ''), e.message.get('desc')) _str += f'{e.message.get("info", "")}, {e.message.get("desc", "")}'
else: else:
_str += '{}'.format(e) _str += str(e)
raise LDAPError(_str) raise LDAPError(_str) from e
def escape(value: str): def escape(value: str):
@ -80,7 +80,9 @@ def connection(
timeout: int = 3, timeout: int = 3,
debug: bool = False, debug: bool = False,
verify_ssl: bool = False, verify_ssl: bool = False,
certificate: typing.Optional[str] = None, # Content of the certificate, not the file itself certificate: typing.Optional[
str
] = None, # Content of the certificate, not the file itself
) -> typing.Any: ) -> typing.Any:
""" """
Tries to connect to ldap. If username is None, it tries to connect using user provided credentials. Tries to connect to ldap. If username is None, it tries to connect using user provided credentials.
@ -100,7 +102,7 @@ def connection(
schema = 'ldaps' if ssl else 'ldap' schema = 'ldaps' if ssl else 'ldap'
if port == -1: if port == -1:
port = 636 if ssl else 389 port = 636 if ssl else 389
uri = "{}://{}:{}".format(schema, host, port) uri = f'{schema}://{host}:{port}'
logger.debug('Ldap uri: %s', uri) logger.debug('Ldap uri: %s', uri)
l = ldap.initialize(uri=uri) # type: ignore l = ldap.initialize(uri=uri) # type: ignore
@ -112,30 +114,34 @@ def connection(
certificate = (certificate or '').strip() certificate = (certificate or '').strip()
if ssl: if ssl:
if certificate and verify_ssl: # If not verify_ssl, we don't need the certificate if (
certificate and verify_ssl
): # If not verify_ssl, we don't need the certificate
# Create a semi-temporary ca file, with the content of the certificate # Create a semi-temporary ca file, with the content of the certificate
# The name is from the host, so we can ovwerwrite it if needed # The name is from the host, so we can ovwerwrite it if needed
cert_filename = os.path.join(tempfile.gettempdir(), f'ldap-cert-{host}.pem') cert_filename = os.path.join(
with open(cert_filename, 'w') as f: tempfile.gettempdir(), f'ldap-cert-{host}.pem'
)
with open(cert_filename, 'w', encoding='utf8') as f:
f.write(certificate) f.write(certificate)
l.set_option(ldap.OPT_X_TLS_CACERTFILE, cert_filename) # type: ignore l.set_option(ldap.OPT_X_TLS_CACERTFILE, cert_filename) # type: ignore
if not verify_ssl: if not verify_ssl:
l.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) # type: ignore l.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) # type: ignore
# Disable TLS1 and TLS1.1 # Disable TLS1 and TLS1.1
# 0x304 = TLS1.3, 0x303 = TLS1.2, 0x302 = TLS1.1, 0x301 = TLS1.0, but use ldap module constants # 0x304 = TLS1.3, 0x303 = TLS1.2, 0x302 = TLS1.1, 0x301 = TLS1.0, but use ldap module constants
l.set_option(ldap.OPT_X_TLS_PROTOCOL_MIN, ldap.OPT_X_TLS_PROTOCOL_TLS1_2) # type: ignore l.set_option(ldap.OPT_X_TLS_PROTOCOL_MIN, ldap.OPT_X_TLS_PROTOCOL_TLS1_2) # type: ignore
l.set_option(ldap.OPT_X_TLS_NEWCTX, 0) # type: ignore l.set_option(ldap.OPT_X_TLS_NEWCTX, 0) # type: ignore
l.simple_bind_s(who=username, cred=password) l.simple_bind_s(who=username, cred=password)
except ldap.SERVER_DOWN as e: # type: ignore except ldap.SERVER_DOWN as e: # type: ignore
raise LDAPError(_('Can\'t contact LDAP server') + ': {}'.format(e)) raise LDAPError(_('Can\'t contact LDAP server') + f': {e}') from e
except ldap.LDAPError as e: # type: ignore except ldap.LDAPError as e: # type: ignore
LDAPError.reraise(e) LDAPError.reraise(e)
except Exception as e: except Exception as e:
logger.exception('Exception connection:') logger.exception('Exception connection:')
raise LDAPError('{}'.format(e)) raise LDAPError(str(e)) from e
logger.debug('Connection was successful') logger.debug('Connection was successful')
return l return l
@ -145,8 +151,8 @@ def getAsDict(
con: typing.Any, con: typing.Any,
base: str, base: str,
ldapFilter: str, ldapFilter: str,
attrList: typing.Optional[typing.Iterable[str]]=None, attrList: typing.Optional[typing.Iterable[str]] = None,
sizeLimit: int=100, sizeLimit: int = 100,
scope=SCOPE_SUBTREE, scope=SCOPE_SUBTREE,
) -> typing.Generator[LDAPResultType, None, None]: ) -> typing.Generator[LDAPResultType, None, None]:
""" """
@ -156,7 +162,7 @@ def getAsDict(
logger.debug('Filter: %s, attr list: %s', ldapFilter, attrList) logger.debug('Filter: %s, attr list: %s', ldapFilter, attrList)
if attrList: if attrList:
attrList = [i for i in attrList] # Ensures iterable is a list attrList = list(attrList) # Ensures iterable is a list
res = None res = None
try: try:
@ -172,7 +178,7 @@ def getAsDict(
LDAPError.reraise(e) LDAPError.reraise(e)
except Exception as e: except Exception as e:
logger.exception('Exception connection:') logger.exception('Exception connection:')
raise LDAPError('{}'.format(e)) raise LDAPError(str(e)) from e
logger.debug('Result of search %s on %s: %s', ldapFilter, base, res) logger.debug('Result of search %s on %s: %s', ldapFilter, base, res)
@ -214,9 +220,9 @@ def getFirst(
""" """
value = ldap.filter.escape_filter_chars(value) value = ldap.filter.escape_filter_chars(value)
attrList = [field] + [i for i in attributes] if attributes else [] attrList = [field] + list(attributes) if attributes else []
ldapFilter = '(&(objectClass={})({}={}))'.format(objectClass, field, value) ldapFilter = f'(&(objectClass={objectClass})({field}={value}))'
try: try:
obj = next(getAsDict(con, base, ldapFilter, attrList, sizeLimit)) obj = next(getAsDict(con, base, ldapFilter, attrList, sizeLimit))
@ -246,9 +252,11 @@ def getRootDSE(con: typing.Any) -> typing.Optional[LDAPResultType]:
@param cont: Connection to LDAP server @param cont: Connection to LDAP server
@return: None if root DSE is not found, an dictionary of LDAP entry attributes if found (all in unicode on py2, str on py3). @return: None if root DSE is not found, an dictionary of LDAP entry attributes if found (all in unicode on py2, str on py3).
""" """
return next(getAsDict( return next(
con=con, getAsDict(
base='', con=con,
ldapFilter='(objectClass=*)', base='',
scope=SCOPE_BASE, ldapFilter='(objectClass=*)',
)) scope=SCOPE_BASE,
)
)

View File

@ -37,6 +37,7 @@ logger = logging.getLogger(__name__)
T = typing.TypeVar('T', bound=typing.Any) T = typing.TypeVar('T', bound=typing.Any)
# We want to write something like this: # We want to write something like this:
# (('<arg>', '<arg2>', 'literal', '<other_arg>', '<other_arg2>', 'literal2', ...), callback) # (('<arg>', '<arg2>', 'literal', '<other_arg>', '<other_arg2>', 'literal2', ...), callback)
# Where callback is a function that will be called with the arguments in the order they are # Where callback is a function that will be called with the arguments in the order they are
@ -71,19 +72,26 @@ def match(
continue continue
# Check if all the arguments match # Check if all the arguments match
match = True doMatch = True
for i, arg in enumerate(arg_list): for i, arg in enumerate(arg_list):
if matcher[0][i].startswith('<') and matcher[0][i].endswith('>'): if matcher[0][i].startswith('<') and matcher[0][i].endswith('>'):
continue continue
if arg != matcher[0][i]: if arg != matcher[0][i]:
match = False doMatch = False
break break
if match: if doMatch:
# All the arguments match, call the callback # All the arguments match, call the callback
return matcher[1](*[arg for i, arg in enumerate(arg_list) if matcher[0][i].startswith('<') and matcher[0][i].endswith('>')]) return matcher[1](
*[
arg
for i, arg in enumerate(arg_list)
if matcher[0][i].startswith('<') and matcher[0][i].endswith('>')
]
)
logger.warning('No match found for %s with %s', arg_list, args) logger.warning('No match found for %s with %s', arg_list, args)
# Invoke error callback # Invoke error callback
error() error()
return None # In fact, error is expected to raise an exception, so this is never reached

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2015-2019 Virtual Cable S.L. # Copyright (c) 2015-2019 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,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice, # * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation # this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution. # 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 # may be used to endorse or promote products derived from this software
# without specific prior written permission. # without specific prior written permission.
# #
@ -31,7 +31,7 @@
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
""" """
from .common import ( from .common import ( # pylint: disable=unused-import
ACTIVE, ACTIVE,
INACTIVE, INACTIVE,
) )

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2015-2019 Virtual Cable S.L. # Copyright (c) 2015-2023 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,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice, # * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation # this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution. # 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 # may be used to endorse or promote products derived from this software
# without specific prior written permission. # without specific prior written permission.
# #
@ -28,7 +28,7 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
""" """
@author: Adolfo Gómez, dkmaster at dkmon dot com Author: Adolfo Gómez, dkmaster at dkmon dot com
""" """
# pylint: disable=unused-import # pylint: disable=unused-import
from .common import ERROR, FINISHED, RUNNING # @UnusedImport from .common import ERROR, FINISHED, RUNNING

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2015-2019 Virtual Cable S.L. # Copyright (c) 2015-2023 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,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice, # * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation # this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution. # 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 # may be used to endorse or promote products derived from this software
# without specific prior written permission. # without specific prior written permission.
# #
@ -40,4 +40,4 @@ from .common import (
CANCELED, CANCELED,
LAUNCHING, LAUNCHING,
PREPARING, PREPARING,
) # @UnusedImport )

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2015-2019 Virtual Cable S.L. # Copyright (c) 2015-2023 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,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice, # * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation # this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution. # 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 # may be used to endorse or promote products derived from this software
# without specific prior written permission. # without specific prior written permission.
# #
@ -32,4 +32,4 @@
""" """
# pylint: disable=unused-import # pylint: disable=unused-import
from .common import ACTIVE, REMOVABLE, REMOVING, REMOVED # @UnusedImport from .common import ACTIVE, REMOVABLE, REMOVING, REMOVED

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2015-2021 Virtual Cable S.L.U. # Copyright (c) 2015-2023 Virtual Cable S.L.U.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
@ -32,4 +32,4 @@
""" """
# pylint: disable=unused-import # pylint: disable=unused-import
from .common import FOR_EXECUTE # @UnusedImport from .common import FOR_EXECUTE

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2015-2021 Virtual Cable S.L.U. # Copyright (c) 2015-2023 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,
@ -41,4 +41,4 @@ from .common import (
REMOVED, REMOVED,
INFO_STATES, INFO_STATES,
VALID_STATES, VALID_STATES,
) # @UnusedImport )

View File

@ -67,6 +67,7 @@ CounterClass = typing.TypeVar(
OT_PROVIDER, OT_SERVICE, OT_SERVICEPOOL, OT_AUTHENTICATOR = range(4) OT_PROVIDER, OT_SERVICE, OT_SERVICEPOOL, OT_AUTHENTICATOR = range(4)
# Helpers # Helpers
def _get_Id(obj): def _get_Id(obj):
return obj.id if obj.id != -1 else None return obj.id if obj.id != -1 else None
@ -244,7 +245,7 @@ def getAcumCounters(
since=since, since=since,
points=points, points=points,
) )
# Data initialization # Data initialization
def _initializeData() -> None: def _initializeData() -> None:

View File

@ -63,7 +63,7 @@ if typing.TYPE_CHECKING:
# Os Manager # Os Manager
ET_OSMANAGER_INIT, ET_OSMANAGER_INIT,
ET_OSMANAGER_READY, ET_OSMANAGER_READY,
ET_OSMANAGER_RELEASE ET_OSMANAGER_RELEASE,
) = range(11) ) = range(11)
# Events names # Events names
@ -81,20 +81,14 @@ EVENT_NAMES: typing.Mapping[int, str] = {
ET_OSMANAGER_RELEASE: 'OS Manager release', ET_OSMANAGER_RELEASE: 'OS Manager release',
} }
( (OT_PROVIDER, OT_SERVICE, OT_SERVICEPOOL, OT_AUTHENTICATOR, OT_OSMANAGER) = range(5)
OT_PROVIDER,
OT_SERVICE,
OT_SERVICEPOOL,
OT_AUTHENTICATOR,
OT_OSMANAGER
) = range(5)
TYPES_NAMES: typing.Mapping[int, str] = { TYPES_NAMES: typing.Mapping[int, str] = {
OT_PROVIDER: 'Provider', OT_PROVIDER: 'Provider',
OT_SERVICE: 'Service', OT_SERVICE: 'Service',
OT_SERVICEPOOL: 'Deployed', OT_SERVICEPOOL: 'Deployed',
OT_AUTHENTICATOR: 'Authenticator', OT_AUTHENTICATOR: 'Authenticator',
OT_OSMANAGER: 'OS Manager' OT_OSMANAGER: 'OS Manager',
} }
MODEL_TO_EVENT: typing.Mapping[typing.Type['models.Model'], int] = { MODEL_TO_EVENT: typing.Mapping[typing.Type['models.Model'], int] = {
@ -102,7 +96,7 @@ MODEL_TO_EVENT: typing.Mapping[typing.Type['models.Model'], int] = {
Service: OT_SERVICE, Service: OT_SERVICE,
Provider: OT_PROVIDER, Provider: OT_PROVIDER,
Authenticator: OT_AUTHENTICATOR, Authenticator: OT_AUTHENTICATOR,
OSManager: OT_OSMANAGER OSManager: OT_OSMANAGER,
} }
# Events data (fld1, fld2, fld3, fld4): # Events data (fld1, fld2, fld3, fld4):
@ -154,21 +148,22 @@ MODEL_TO_EVENT: typing.Mapping[typing.Type['models.Model'], int] = {
# OT_OSMANAGER_RELEASE: -> On OsManager # OT_OSMANAGER_RELEASE: -> On OsManager
# (servicepool_uuid, '', userservice_uuid) # (servicepool_uuid, '', userservice_uuid)
# Helpers # Helpers
# get owner by type and id # get owner by type and id
def getOwner(ownerType: int, ownerId: int) -> typing.Optional['models.Model']: def getOwner(ownerType: int, ownerId: int) -> typing.Optional['models.Model']:
if ownerType == OT_PROVIDER: if ownerType == OT_PROVIDER:
return Provider.objects.get(pk=ownerId) return Provider.objects.get(pk=ownerId)
elif ownerType == OT_SERVICE: if ownerType == OT_SERVICE:
return Service.objects.get(pk=ownerId) return Service.objects.get(pk=ownerId)
elif ownerType == OT_SERVICEPOOL: if ownerType == OT_SERVICEPOOL:
return ServicePool.objects.get(pk=ownerId) return ServicePool.objects.get(pk=ownerId)
elif ownerType == OT_AUTHENTICATOR: if ownerType == OT_AUTHENTICATOR:
return Authenticator.objects.get(pk=ownerId) return Authenticator.objects.get(pk=ownerId)
elif ownerType == OT_OSMANAGER: if ownerType == OT_OSMANAGER:
return OSManager.objects.get(pk=ownerId) return OSManager.objects.get(pk=ownerId)
else: return None
return None
class EventTupleType(typing.NamedTuple): class EventTupleType(typing.NamedTuple):
stamp: datetime.datetime stamp: datetime.datetime
@ -188,13 +183,14 @@ class EventTupleType(typing.NamedTuple):
def __str__(self) -> str: def __str__(self) -> str:
# Convert Event type to string first # Convert Event type to string first
eventName = EVENT_NAMES[self.event_type] eventName = EVENT_NAMES[self.event_type]
return '{} {} {} {} {} {}'.format(self.stamp, eventName, self.fld1, self.fld2, self.fld3, self.fld4 ) return (
f'{self.stamp} {eventName} {self.fld1} {self.fld2} {self.fld3} {self.fld4}'
)
EventClass = typing.Union[Provider, Service, ServicePool, Authenticator] EventClass = typing.Union[Provider, Service, ServicePool, Authenticator]
def addEvent(obj: EventClass, eventType: int, **kwargs) -> bool: def addEvent(obj: EventClass, eventType: int, **kwargs) -> bool:
""" """
Adds a event stat to specified object Adds a event stat to specified object
@ -237,7 +233,11 @@ def getEvents(
owner_id = obj.pk owner_id = obj.pk
for i in StatsManager.manager().getEvents( for i in StatsManager.manager().getEvents(
MODEL_TO_EVENT[objType], eventType, owner_id=owner_id, since=kwargs.get('since'), to=kwargs.get('to') MODEL_TO_EVENT[objType],
eventType,
owner_id=owner_id,
since=kwargs.get('since'),
to=kwargs.get('to'),
): ):
yield EventTupleType( yield EventTupleType(
datetime.datetime.fromtimestamp(i.stamp), datetime.datetime.fromtimestamp(i.stamp),
@ -247,8 +247,9 @@ def getEvents(
i.fld4, i.fld4,
i.event_type, i.event_type,
) )
# tail the events table
# tail the events table
def tailEvents(sleepTime: int = 2) -> typing.Generator[EventTupleType, None, None]: def tailEvents(sleepTime: int = 2) -> typing.Generator[EventTupleType, None, None]:
fromId = None fromId = None
while True: while True:
@ -263,4 +264,3 @@ def tailEvents(sleepTime: int = 2) -> typing.Generator[EventTupleType, None, Non
) )
fromId = i.pk if i.pk > (fromId or 0) else fromId fromId = i.pk if i.pk > (fromId or 0) else fromId
time.sleep(sleepTime) time.sleep(sleepTime)