From 613f4efd315972f7fa311942b8a9f3ca7a08e6f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Wed, 5 Jun 2019 08:14:45 +0200 Subject: [PATCH] fixes for python3 & type hinting --- .gitignore | 1 + server/src/uds/REST/handlers.py | 33 +++---- server/src/uds/REST/methods/providers.py | 36 ++++---- server/src/uds/REST/methods/reports.py | 16 ++-- server/src/uds/REST/methods/services_pools.py | 6 +- server/src/uds/REST/methods/services_usage.py | 4 +- server/src/uds/REST/methods/system.py | 9 +- server/src/uds/REST/methods/tickets.py | 16 ++-- server/src/uds/REST/model.py | 87 ++++++++++--------- server/src/uds/core/services/BaseService.py | 34 ++++---- .../src/uds/services/Xen/XenLinkedService.py | 38 +++++--- 11 files changed, 142 insertions(+), 138 deletions(-) diff --git a/.gitignore b/.gitignore index 1e4834ed6..b45ed795f 100644 --- a/.gitignore +++ b/.gitignore @@ -167,3 +167,4 @@ /udsService/udsgui/obj/x86 .vscode +.mypy_cache diff --git a/server/src/uds/REST/handlers.py b/server/src/uds/REST/handlers.py index 42cd1dae9..916b6dfe9 100644 --- a/server/src/uds/REST/handlers.py +++ b/server/src/uds/REST/handlers.py @@ -32,7 +32,8 @@ """ # pylint: disable=too-many-public-methods -from __future__ import unicode_literals +import typing +import logging from django.contrib.sessions.backends.db import SessionStore @@ -41,8 +42,6 @@ from uds.core.auths.auth import getRootUser from uds.models import Authenticator from uds.core.managers import cryptoManager -import logging - logger = logging.getLogger(__name__) AUTH_TOKEN_HEADER = 'HTTP_X_AUTH_TOKEN' @@ -52,54 +51,48 @@ class HandlerError(Exception): """ Generic error for a REST handler """ - pass class NotFound(HandlerError): """ Item not found error """ - pass class AccessDenied(HandlerError): """ Access denied error """ - pass class RequestError(HandlerError): """ Request is invalid error """ - pass class ResponseError(HandlerError): """ Generic response error """ - pass class NotSupportedError(HandlerError): """ Some elements do not support some operations (as searching over an authenticator that does not supports it) """ - pass -class Handler(object): +class Handler: """ REST requests handler base class """ - raw = False # If true, Handler will return directly an HttpResponse Object - name = None # If name is not used, name will be the class name in lower case - path = None # Path for this method, so we can do /auth/login, /auth/logout, /auth/auths in a simple way - authenticated = True # By default, all handlers needs authentication - needs_admin = False # By default, the methods will be accessible by anyone if nothing else indicated - needs_staff = False # By default, staff + raw: typing.ClassVar[bool] = False # If true, Handler will return directly an HttpResponse Object + name: typing.ClassVar[typing.Optional[str]] = None # If name is not used, name will be the class name in lower case + path: typing.ClassVar[typing.Optional[str]] = None # Path for this method, so we can do /auth/login, /auth/logout, /auth/auths in a simple way + authenticated: typing.ClassVar[bool] = True # By default, all handlers needs authentication + needs_admin: typing.ClassVar[bool] = False # By default, the methods will be accessible by anyone if nothing else indicated + needs_staff: typing.ClassVar[bool] = False # By default, staff # method names: 'get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace' def __init__(self, request, path, operation, params, *args, **kwargs): @@ -248,25 +241,25 @@ class Handler(object): self._session.accessed = True self._session.save() except Exception: - logger.exception('Got an exception setting session value {} to {}'.format(key, value)) + logger.exception('Got an exception setting session value %s to %s', key, value) def is_admin(self): """ True if user of this REST request is administrator """ - return self.getValue('is_admin') and True or False + return bool(self.getValue('is_admin')) def is_staff_member(self): """ True if user of this REST request is member of staff """ - return self.getValue('staff_member') and True or False + return bool(self.getValue('staff_member')) def getUser(self): """ If user is staff member, returns his Associated user on auth """ - logger.debug('REST : {}'.format(self._session)) + logger.debug('REST : %s', self._session) authId = self.getValue('auth') username = self.getValue('username') # Maybe it's root user?? diff --git a/server/src/uds/REST/methods/providers.py b/server/src/uds/REST/methods/providers.py index f8ad81c15..b2d719634 100644 --- a/server/src/uds/REST/methods/providers.py +++ b/server/src/uds/REST/methods/providers.py @@ -30,22 +30,22 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com """ -from __future__ import unicode_literals +import typing +import logging from django.utils.translation import ugettext, ugettext_lazy as _ + from uds.models import Provider, Service, UserService from uds.core import services from uds.core.util.State import State from uds.core.util import permissions -from uds.core.util.model import processUuid - -from .services import Services as DetailServices -from .services_usage import ServicesUsage from uds.REST import NotFound, RequestError from uds.REST.model import ModelHandler -import logging +from .services import Services as DetailServices +from .services_usage import ServicesUsage + logger = logging.getLogger(__name__) @@ -78,8 +78,8 @@ class Providers(ModelHandler): # Field from where to get "class" and prefix for that class, so this will generate "row-state-A, row-state-X, .... table_row_style = {'field': 'maintenance_mode', 'prefix': 'row-maintenance-'} - def item_as_dict(self, provider): - type_ = provider.getType() + def item_as_dict(self, item) -> typing.Dict[str, typing.Any]: + type_ = item.getType() # Icon can have a lot of data (1-2 Kbytes), but it's not expected to have a lot of services providers, and even so, this will work fine offers = [{ @@ -89,16 +89,16 @@ class Providers(ModelHandler): 'icon': t.icon().replace('\n', '')} for t in type_.getServicesTypes()] return { - 'id': provider.uuid, - 'name': provider.name, - 'tags': [tag.vtag for tag in provider.tags.all()], - 'services_count': provider.services.count(), - 'user_services_count': UserService.objects.filter(deployed_service__service__provider=provider).exclude(state__in=(State.REMOVED, State.ERROR)).count(), - 'maintenance_mode': provider.maintenance_mode, + 'id': item.uuid, + 'name': item.name, + 'tags': [tag.vtag for tag in item.tags.all()], + 'services_count': item.services.count(), + 'user_services_count': UserService.objects.filter(deployed_service__service__provider=item).exclude(state__in=(State.REMOVED, State.ERROR)).count(), + 'maintenance_mode': item.maintenance_mode, 'offers': offers, 'type': type_.type(), - 'comments': provider.comments, - 'permission': permissions.getEffectivePermission(self._user, provider) + 'comments': item.comments, + 'permission': permissions.getEffectivePermission(self._user, item) } def checkDelete(self, item): @@ -152,12 +152,12 @@ class Providers(ModelHandler): def test(self, type_): from uds.core.Environment import Environment - logger.debug('Type: {}'.format(type_)) + logger.debug('Type: %s', type_) spType = services.factory().lookup(type_) self.ensureAccess(spType, permissions.PERMISSION_MANAGEMENT, root=True) - logger.debug('spType: {}'.format(spType)) + logger.debug('spType: %s', spType) dct = self._params.copy() dct['_request'] = self._request diff --git a/server/src/uds/REST/methods/reports.py b/server/src/uds/REST/methods/reports.py index 43e279892..08c344d6c 100644 --- a/server/src/uds/REST/methods/reports.py +++ b/server/src/uds/REST/methods/reports.py @@ -30,15 +30,15 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com """ -from __future__ import unicode_literals +import logging -from django.utils.translation import ugettext, ugettext_lazy as _ +import six + +from django.utils.translation import ugettext_lazy as _ from uds.REST import model from uds import reports -import six -import logging logger = logging.getLogger(__name__) @@ -64,7 +64,7 @@ class Reports(model.BaseModelHandler): def _findReport(self, uuid, values=None): found = None - logger.debug('Looking for report {}'.format(uuid)) + logger.debug('Looking for report %s', uuid) for i in reports.availableReports: if i.getUuid() == uuid: found = i(values) @@ -76,7 +76,7 @@ class Reports(model.BaseModelHandler): return found def get(self): - logger.debug('method GET for {0}, {1}'.format(self.__class__.__name__, self._args)) + logger.debug('method GET for %s, %s', self.__class__.__name__, self._args) nArgs = len(self._args) if nArgs == 0: @@ -98,7 +98,7 @@ class Reports(model.BaseModelHandler): """ Processes a PUT request """ - logger.debug('method PUT for {0}, {1}, {2}'.format(self.__class__.__name__, self._args, self._params)) + logger.debug('method PUT for %s, %s, %s', self.__class__.__name__, self._args, self._params) if len(self._args) != 1: return self.invalidRequestException() @@ -106,7 +106,7 @@ class Reports(model.BaseModelHandler): report = self._findReport(self._args[0], self._params) try: - logger.debug('Report: {}'.format(report)) + logger.debug('Report: %s', report) result = report.generateEncoded() data = { diff --git a/server/src/uds/REST/methods/services_pools.py b/server/src/uds/REST/methods/services_pools.py index 4543f4437..e953ab3c8 100644 --- a/server/src/uds/REST/methods/services_pools.py +++ b/server/src/uds/REST/methods/services_pools.py @@ -30,6 +30,10 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com """ +import logging + +import six + from django.utils.translation import ugettext, ugettext_lazy as _ from uds.models import DeployedService, OSManager, Service, Image, ServicesPoolGroup, Account from uds.models.CalendarAction import ( @@ -56,8 +60,6 @@ from .op_calendars import AccessCalendars, ActionsCalendars from .services import Services from uds.core.managers import userServiceManager -import six -import logging logger = logging.getLogger(__name__) diff --git a/server/src/uds/REST/methods/services_usage.py b/server/src/uds/REST/methods/services_usage.py index 9db802fc0..a610f31ba 100644 --- a/server/src/uds/REST/methods/services_usage.py +++ b/server/src/uds/REST/methods/services_usage.py @@ -33,17 +33,15 @@ # pylint: disable=too-many-public-methods -from __future__ import unicode_literals +import logging from django.utils.translation import ugettext as _ from uds.models import UserService from uds.core.util.State import State from uds.core.util.model import processUuid -from uds.core.util import log from uds.REST.model import DetailHandler -import logging logger = logging.getLogger(__name__) diff --git a/server/src/uds/REST/methods/system.py b/server/src/uds/REST/methods/system.py index a6e5ee9e6..aa34b8716 100644 --- a/server/src/uds/REST/methods/system.py +++ b/server/src/uds/REST/methods/system.py @@ -30,7 +30,10 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com """ -from __future__ import unicode_literals +import pickle +from datetime import timedelta + +import logging from uds.models import User, Group, Service, UserService, ServicePool, MetaPool, getSqlDatetime @@ -39,10 +42,6 @@ from uds.core.util.Cache import Cache from uds.core.util.State import State from uds.core.util import encoders from uds.REST import Handler, RequestError, ResponseError -import pickle -from datetime import timedelta - -import logging logger = logging.getLogger(__name__) diff --git a/server/src/uds/REST/methods/tickets.py b/server/src/uds/REST/methods/tickets.py index d58b64841..7742e5a8a 100644 --- a/server/src/uds/REST/methods/tickets.py +++ b/server/src/uds/REST/methods/tickets.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2014 Virtual Cable S.L. +# Copyright (c) 2014-2019 Virtual Cable S.L. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -30,7 +30,10 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com """ -from __future__ import unicode_literals +import datetime + +import logging + from uds.REST import Handler from uds.REST import RequestError @@ -42,11 +45,6 @@ from uds.core.managers import cryptoManager from uds.core.util.model import processUuid from uds.core.util import tools -import datetime -import six - -import logging - logger = logging.getLogger(__name__) VALID_PARAMS = ('authId', 'authTag', 'authSmallName', 'auth', 'username', 'realname', 'password', 'groups', 'servicePool', 'transport', 'force', 'userIp') @@ -142,7 +140,7 @@ class Tickets(Handler): username = self._params['username'] password = self._params.get('password', '') # Some machines needs password, depending on configuration groups = self._params['groups'] - if isinstance(groups, (six.text_type, six.binary_type)): + if isinstance(groups, (str, bytes)): groups = (groups,) grps = [] for g in groups: @@ -204,7 +202,7 @@ class Tickets(Handler): except Transport.DoesNotExist: return Tickets.result(error='Transport does not exists') except Exception as e: - return Tickets.result(error=six.text_type(e)) + return Tickets.result(error=str(e)) data = { 'username': username, diff --git a/server/src/uds/REST/model.py b/server/src/uds/REST/model.py index 4443df522..0c05b8e49 100644 --- a/server/src/uds/REST/model.py +++ b/server/src/uds/REST/model.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2014 Virtual Cable S.L. +# Copyright (c) 2014-2019 Virtual Cable S.L. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -31,12 +31,19 @@ """ # pylint: disable=too-many-public-methods -from __future__ import unicode_literals +import fnmatch +import re +import types +import typing + +import logging + +import six + +from django.utils.translation import ugettext as _ +from django.db import IntegrityError, models from uds.REST.handlers import NotFound, RequestError, ResponseError, AccessDenied, NotSupportedError -from django.utils.translation import ugettext as _ -from django.db import IntegrityError - from uds.core.ui.UserInterface import gui as uiGui from uds.REST.handlers import Handler, HandlerError from uds.core.util import log @@ -44,13 +51,6 @@ from uds.core.util import permissions from uds.core.util.model import processUuid from uds.models import Tag -import six -from six.moves import filter # @UnresolvedImport -import fnmatch -import re -import types - -import logging logger = logging.getLogger(__name__) @@ -71,7 +71,6 @@ class SaveException(HandlerError): """ Exception thrown if couldn't save """ - pass class BaseModelHandler(Handler): @@ -299,7 +298,7 @@ class BaseModelHandler(Handler): # Details do not have types at all # so, right now, we only process details petitions for Handling & tables info # noinspection PyMissingConstructor -class DetailHandler(BaseModelHandler): # pylint: disable=abstract-class-not-used +class DetailHandler(BaseModelHandler): """ Detail handler (for relations such as provider-->services, authenticators-->users,groups, deployed services-->cache,assigned, groups, transports Urls recognized for GET are: @@ -320,7 +319,7 @@ class DetailHandler(BaseModelHandler): # pylint: disable=abstract-class-not-use Also accepts GET methods for "custom" methods """ - custom_methods = [] + custom_methods: typing.Iterable[typing.Tuple[str, bool]] = [] def __init__(self, parentHandler, path, params, *args, **kwargs): # pylint: disable=super-init-not-called """ @@ -357,7 +356,7 @@ class DetailHandler(BaseModelHandler): # pylint: disable=abstract-class-not-use Processes GET method for a detail Handler """ # Process args - logger.debug("Detail args for GET: {0}".format(self._args)) + logger.debug('Detail args for GET: %s', self._args) nArgs = len(self._args) parent = self._kwargs['parent'] @@ -405,7 +404,7 @@ class DetailHandler(BaseModelHandler): # pylint: disable=abstract-class-not-use Evaluates if it is a new element or a "modify" operation (based on if it has parameter), and invokes "saveItem" with parent & item (that can be None for a new Item) """ - logger.debug("Detail args for PUT: {0}, {1}".format(self._args, self._params)) + logger.debug('Detail args for PUT: %s, %s', self._args, self._params) parent = self._kwargs['parent'] @@ -514,7 +513,7 @@ class DetailHandler(BaseModelHandler): # pylint: disable=abstract-class-not-use """ return {} - def getGui(self, parent, forType): # pylint: disable=no-self-use + def getGui(self, parent, forType) -> typing.Iterable[typing.Any]: # pylint: disable=no-self-use """ Gets the gui that is needed in order to "edit/add" new items on this detail If not overriden, means that the detail has no edit/new Gui @@ -522,7 +521,8 @@ class DetailHandler(BaseModelHandler): # pylint: disable=abstract-class-not-use :param forType: Type of object needing gui :return: a "gui" (list of gui fields) """ - raise RequestError('Gui not provided for this type of object') + # raise RequestError('Gui not provided for this type of object') + return [] def getTypes(self, parent, forType): # pylint: disable=no-self-use """ @@ -560,39 +560,39 @@ class ModelHandler(BaseModelHandler): The only detail that has types within is "Service", child of "Provider" """ # Authentication related - authenticated = True - needs_staff = True + authenticated: typing.ClassVar[bool] = True + needs_staff: typing.ClassVar[bool] = True # Which model does this manage - model = None + model: typing.Optional[models.Model] = None # By default, filter is empty - fltr = None + fltr: typing.Optional[str] = None # This is an array of tuples of two items, where first is method and second inticates if method needs parent id # For example ('services', True) -- > .../id_parent/services # ('services', False) --> ..../services - custom_methods = [] # If this model respond to "custom" methods, we will declare them here + custom_methods: typing.Iterable[typing.Tuple[str, bool]] = [] # If this model respond to "custom" methods, we will declare them here # If this model has details, which ones - detail = None # Dictionary containing detail routing + detail: typing.Optional[typing.Dict[str, typing.Type[DetailHandler]]] = None # Dictionary containing detail routing # Put needed fields - save_fields = [] + save_fields: typing.Iterable[str] = [] # Put removable fields before updating - remove_fields = [] + remove_fields: typing.Iterable[str] = [] # Table info needed fields and title - table_fields = [] - table_row_style = {} - table_title = '' - table_subtitle = '' + table_fields: typing.Iterable[typing.Any] = [] + table_row_style: typing.Dict = {} + table_title: str = '' + table_subtitle: str = '' # This methods must be override, depending on what is provided # Data related - def item_as_dict(self, item): + def item_as_dict(self, item) -> typing.Dict[str, typing.Any]: """ Must be overriden by descendants. Expects the return of an item as a dictionary """ - return None + return {} def item_as_dict_overview(self, item): """ @@ -623,7 +623,7 @@ class ModelHandler(BaseModelHandler): if found is None: raise NotFound('type not found') - logger.debug('Found type {0}'.format(found)) + logger.debug('Found type %s', found) return found # log related @@ -633,8 +633,9 @@ class ModelHandler(BaseModelHandler): return log.getLogs(item) # gui related - def getGui(self, type_): - self.invalidRequestException() + def getGui(self, type_) -> typing.Iterable[typing.Any]: + return [] + # self.invalidRequestException() # Delete related, checks if the item can be deleted # If it can't be so, raises an exception @@ -662,7 +663,7 @@ class ModelHandler(BaseModelHandler): if 'filter' in self._params: self.fltr = self._params['filter'] del self._params['filter'] # Remove parameter - logger.debug('Found a filter expression ({})'.format(self.fltr)) + logger.debug('Found a filter expression (%s)', self.fltr) def doFilter(self, data): # Right now, filtering only supports a single filter, in a future @@ -674,7 +675,7 @@ class ModelHandler(BaseModelHandler): if not isinstance(data, (list, tuple, types.GeneratorType)): return data - logger.debug('data: {}, fltr: {}'.format(data, self.fltr)) + logger.debug('data: %s, fltr: %s', data, self.fltr) try: fld, pattern = self.fltr.split('=') s, e = '', '' @@ -697,11 +698,11 @@ class ModelHandler(BaseModelHandler): res = list(filter(fltr_function, data)) - logger.debug('After filtering: {}'.format(res)) + logger.debug('After filtering: %s', res) return res except: logger.exception('Exception:') - logger.info('Filtering expression {} is invalid!'.format(self.fltr)) + logger.info('Filtering expression %s is invalid!', self.fltr) raise RequestError('Filtering expression {} is invalid'.format(self.fltr)) return data @@ -709,7 +710,7 @@ class ModelHandler(BaseModelHandler): # Helper to process detail # Details can be managed (writen) by any user that has MANAGEMENT permission over parent def processDetail(self): - logger.debug('Processing detail {} for with params {}'.format(self._path, self._params)) + logger.debug('Processing detail %s for with params %s', self._path, self._params) try: item = self.model.objects.filter(uuid=self._args[0])[0] # If we do not have access to parent to, at least, read... @@ -720,10 +721,10 @@ class ModelHandler(BaseModelHandler): requiredPermission = permissions.PERMISSION_READ if permissions.checkPermissions(self._user, item, requiredPermission) is False: - logger.debug('Permission for user {} does not comply with {}'.format(self._user, requiredPermission)) + logger.debug('Permission for user %s does not comply with %s', self._user, requiredPermission) self.accessDenied() - detailCls = self.detail[self._args[1]] + detailCls = self.detail[self._args[1]] # pylint: disable=unsubscriptable-object args = list(self._args[2:]) path = self._path + '/'.join(args[:2]) detail = detailCls(self, path, self._params, *args, parent=item, user=self._user) diff --git a/server/src/uds/core/services/BaseService.py b/server/src/uds/core/services/BaseService.py index 41435a4f8..072e09812 100644 --- a/server/src/uds/core/services/BaseService.py +++ b/server/src/uds/core/services/BaseService.py @@ -30,15 +30,15 @@ """ .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com """ -from __future__ import unicode_literals + +import typing from django.utils.translation import ugettext_noop as _ from uds.core import Module from uds.core.transports import protocols from . import types - -__updated__ = '2018-06-07' - +from .BasePublication import Publication +from .BaseDeployed import UserDeployment class Service(Module): """ @@ -79,38 +79,38 @@ class Service(Module): """ # : Constant for indicating that max elements this service can deploy is unlimited. - UNLIMITED = -1 + UNLIMITED: int = -1 # : Name of type, used at administration interface to identify this # : service (i.e. Xen server, oVirt Server, ...) # : This string will be translated when provided to admin interface # : using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop) # : if you want so it can be translated. - typeName = _('Base Service') + typeName: str = _('Base Service') # : Name of type used by Managers to identify this type of service # : We could have used here the Class name, but we decided that the # : module implementator will be the one that will provide a name that # : will relation the class (type) and that name. - typeType = 'BaseService' + typeType: str = 'BaseService' # : Description shown at administration level for this service. # : This string will be translated when provided to admin interface # : using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop) # : if you want so it can be translated. - typeDescription = _('Base Service') + typeDescription: str = _('Base Service') # : Icon file, used to represent this service at administration interface # : This file should be at same folder as this class is, except if you provide # : your own :py:meth:uds.core.BaseModule.BaseModule.icon method. - iconFile = 'service.png' + iconFile: str = 'service.png' # Functional related data # : Normally set to UNLIMITED. This attribute indicates if the service has some "limitation" # : for providing deployed services to users. This attribute can be set here or # : modified at instance level, core will access always to it using an instance object. - maxDeployed = UNLIMITED # : If the service provides more than 1 "provided service" (-1 = no limit, 0 = ???? (do not use it!!!), N = max number to deploy + maxDeployed: int = UNLIMITED # : If the service provides more than 1 "provided service" (-1 = no limit, 0 = ???? (do not use it!!!), N = max number to deploy # : If this item "has constains", on deployed service edition, defined keys will overwrite defined ones cacheConstrains = None @@ -134,7 +134,7 @@ class Service(Module): cacheTooltip_L2 = _('None') # : Tooltip shown to user when this item is pointed at admin interface # : If the service needs a o.s. manager (see os managers section) - needsManager = False + needsManager: bool = False # : If the service can be autoassigned or needs to be assigned by administrator # : Not all services are for assigning it. Thing, i.e., a Service that manages @@ -143,7 +143,7 @@ class Service(Module): # : to assign the service automatically. If this is true, the core will not # : assign the service automatically, so if the user do not have a consumable # : assigned, the user will never get one (of this kind, of course) - mustAssignManually = False + mustAssignManually: typing.ClassVar[bool] = False # : Types of publications (preparated data for deploys) # : If you provide this, UDS will assume that the service needs a preparation. @@ -152,23 +152,23 @@ class Service(Module): # : provide a publication type # : This refers to class that provides the logic for publication, you can see # : :py:class:uds.core.services.Publication - publicationType = None + publicationType: typing.ClassVar[typing.Optional[typing.Type[Publication]]] = None # : Types of deploys (services in cache and/or assigned to users) # : This is ALWAYS a MUST. You mast indicate the class responsible # : for managing the user deployments (user consumable services generated # : from this one). If this attribute is not set, the service will never work # : (core will not know how to handle the user deployments) - deployedType = None + deployedType: typing.ClassVar[typing.Optional[typing.Type[UserDeployment]]] = None # : Restricted transports # : If this list contains anything else but emtpy, the only allowed protocol for transports # : will be the ones listed here (on implementation, ofc) - allowedProtocols = protocols.GENERIC + allowedProtocols: typing.Iterable = protocols.GENERIC # : If this services "spawns" a new copy on every execution (that is, does not "reuse" the previous opened session) # : Default behavior is False (and most common), but some services may need to respawn a new "copy" on every launch - spawnsNew = False + spawnsNew: bool = False # : If the service allows "reset", here we will announce it # : Defaults to False @@ -176,7 +176,7 @@ class Service(Module): # : 'kind' of services that this service provides: # : For example, VDI, VAPP, ... - servicesTypeProvided = types.ALL + servicesTypeProvided: typing.Iterable = types.ALL def __init__(self, environment, parent, values=None, uuid=None): """ diff --git a/server/src/uds/services/Xen/XenLinkedService.py b/server/src/uds/services/Xen/XenLinkedService.py index 551dd4273..9c6bd8404 100644 --- a/server/src/uds/services/Xen/XenLinkedService.py +++ b/server/src/uds/services/Xen/XenLinkedService.py @@ -74,13 +74,15 @@ class XenLinkedService(Service): usesCache = True # : Tooltip shown to user when this item is pointed at admin interface, none # : because we don't use it - cacheTooltip = _('Number of desired machines to keep running waiting for a user') + cacheTooltip = _( + 'Number of desired machines to keep running waiting for a user') # : If we need to generate a "Level 2" cache for this service (i.e., L1 # : could be running machines and L2 suspended machines) usesCache_L2 = True # : Tooltip shown to user when this item is pointed at admin interface, None # : also because we don't use it - cacheTooltip_L2 = _('Number of desired machines to keep suspended waiting for use') + cacheTooltip_L2 = _( + 'Number of desired machines to keep suspended waiting for use') # : If the service needs a s.o. manager (managers are related to agents # : provided by services itselfs, i.e. virtual machines with actors) @@ -168,7 +170,8 @@ class XenLinkedService(Service): label=_('Name Length'), defvalue=5, order=115, - tooltip=_('Length of numeric part for the names of this machines (beetwen 3 and 6'), + tooltip=_( + 'Length of numeric part for the names of this machines (beetwen 3 and 6'), tab=_('Machine'), required=True ) @@ -183,11 +186,14 @@ class XenLinkedService(Service): if values is not None: length = int(self.lenName.value) if len(self.baseName.value) + length > 15: - raise Service.ValidationException(_('The length of basename plus length must not be greater than 15')) + raise Service.ValidationException( + _('The length of basename plus length must not be greater than 15')) if self.baseName.value.isdigit(): - raise Service.ValidationException(_('The machine name can\'t be only numbers')) + raise Service.ValidationException( + _('The machine name can\'t be only numbers')) if int(self.memory.value) < 256: - raise Service.ValidationException(_('The minimum allowed memory is 256 Mb')) + raise Service.ValidationException( + _('The minimum allowed memory is 256 Mb')) def initGui(self): """ @@ -206,8 +212,10 @@ class XenLinkedService(Service): machines_list.append(gui.choiceItem(m['id'], m['name'])) storages_list = [] for storage in storages: - space, free = storage['size'] / 1024, (storage['size'] - storage['used']) / 1024 - storages_list.append(gui.choiceItem(storage['id'], "%s (%4.2f Gb/%4.2f Gb)" % (storage['name'], space, free))) + space, free = storage['size'] / \ + 1024, (storage['size'] - storage['used']) / 1024 + storages_list.append(gui.choiceItem( + storage['id'], "%s (%4.2f Gb/%4.2f Gb)" % (storage['name'], space, free))) network_list = [] for net in networks: network_list.append(gui.choiceItem(net['id'], net['name'])) @@ -221,12 +229,14 @@ class XenLinkedService(Service): def datastoreHasSpace(self): # Get storages for that datacenter - logger.debug('Checking datastore space for {0}'.format(self.datastore.value)) + logger.debug('Checking datastore space for {0}'.format( + self.datastore.value)) info = self.parent().getStorageInfo(self.datastore.value) logger.debug('Datastore Info: {0}'.format(info)) availableGB = (info['size'] - info['used']) / 1024 if availableGB < self.minSpaceGB.num(): - raise Exception('Not enough free space available: (Needs at least {0} GB and there is only {1} GB '.format(self.minSpaceGB.num(), availableGB)) + raise Exception('Not enough free space available: (Needs at least {0} GB and there is only {1} GB '.format( + self.minSpaceGB.num(), availableGB)) def sanitizeVmName(self, name): """ @@ -248,7 +258,8 @@ class XenLinkedService(Service): Raises an exception if operation fails. """ - logger.debug('Starting deploy of template from machine {0} on datastore {1}'.format(self.machine.value, self.datastore.value)) + logger.debug('Starting deploy of template from machine {0} on datastore {1}'.format( + self.machine.value, self.datastore.value)) # Checks datastore size self.datastoreHasSpace() @@ -256,6 +267,7 @@ class XenLinkedService(Service): def convertToTemplate(self, machineId): """ + converts machine to template """ self.parent().convertToTemplate(machineId, self.shadow.value) @@ -274,7 +286,8 @@ class XenLinkedService(Service): Returns: Id of the machine being created form template """ - logger.debug('Deploying from template {0} machine {1}'.format(templateId, name)) + logger.debug( + 'Deploying from template {0} machine {1}'.format(templateId, name)) self.datastoreHasSpace() return self.parent().startDeployFromTemplate(name, comments, templateId) @@ -406,4 +419,3 @@ class XenLinkedService(Service): Returns the selected display type (for created machines, for administration """ return self.display.value -