fixes for python3 & type hinting

This commit is contained in:
Adolfo Gómez García 2019-06-05 08:14:45 +02:00
parent 2baac2f532
commit 613f4efd31
11 changed files with 142 additions and 138 deletions

1
.gitignore vendored
View File

@ -167,3 +167,4 @@
/udsService/udsgui/obj/x86 /udsService/udsgui/obj/x86
.vscode .vscode
.mypy_cache

View File

@ -32,7 +32,8 @@
""" """
# pylint: disable=too-many-public-methods # pylint: disable=too-many-public-methods
from __future__ import unicode_literals import typing
import logging
from django.contrib.sessions.backends.db import SessionStore 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.models import Authenticator
from uds.core.managers import cryptoManager from uds.core.managers import cryptoManager
import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
AUTH_TOKEN_HEADER = 'HTTP_X_AUTH_TOKEN' AUTH_TOKEN_HEADER = 'HTTP_X_AUTH_TOKEN'
@ -52,54 +51,48 @@ class HandlerError(Exception):
""" """
Generic error for a REST handler Generic error for a REST handler
""" """
pass
class NotFound(HandlerError): class NotFound(HandlerError):
""" """
Item not found error Item not found error
""" """
pass
class AccessDenied(HandlerError): class AccessDenied(HandlerError):
""" """
Access denied error Access denied error
""" """
pass
class RequestError(HandlerError): class RequestError(HandlerError):
""" """
Request is invalid error Request is invalid error
""" """
pass
class ResponseError(HandlerError): class ResponseError(HandlerError):
""" """
Generic response error Generic response error
""" """
pass
class NotSupportedError(HandlerError): class NotSupportedError(HandlerError):
""" """
Some elements do not support some operations (as searching over an authenticator that does not supports it) 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 REST requests handler base class
""" """
raw = False # If true, Handler will return directly an HttpResponse Object raw: typing.ClassVar[bool] = 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 name: typing.ClassVar[typing.Optional[str]] = 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 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 = True # By default, all handlers needs authentication authenticated: typing.ClassVar[bool] = True # By default, all handlers needs authentication
needs_admin = False # By default, the methods will be accessible by anyone if nothing else indicated needs_admin: typing.ClassVar[bool] = False # By default, the methods will be accessible by anyone if nothing else indicated
needs_staff = False # By default, staff needs_staff: typing.ClassVar[bool] = False # By default, staff
# method names: 'get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace' # method names: 'get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'
def __init__(self, request, path, operation, params, *args, **kwargs): def __init__(self, request, path, operation, params, *args, **kwargs):
@ -248,25 +241,25 @@ class Handler(object):
self._session.accessed = True self._session.accessed = True
self._session.save() self._session.save()
except Exception: 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): def is_admin(self):
""" """
True if user of this REST request is administrator 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): def is_staff_member(self):
""" """
True if user of this REST request is member of staff 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): def getUser(self):
""" """
If user is staff member, returns his Associated user on auth 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') authId = self.getValue('auth')
username = self.getValue('username') username = self.getValue('username')
# Maybe it's root user?? # Maybe it's root user??

View File

@ -30,22 +30,22 @@
""" """
@author: Adolfo Gómez, dkmaster at dkmon dot com @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 django.utils.translation import ugettext, ugettext_lazy as _
from uds.models import Provider, Service, UserService from uds.models import Provider, Service, UserService
from uds.core import services from uds.core import services
from uds.core.util.State import State from uds.core.util.State import State
from uds.core.util import permissions 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 import NotFound, RequestError
from uds.REST.model import ModelHandler from uds.REST.model import ModelHandler
import logging from .services import Services as DetailServices
from .services_usage import ServicesUsage
logger = logging.getLogger(__name__) 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, .... # 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-'} table_row_style = {'field': 'maintenance_mode', 'prefix': 'row-maintenance-'}
def item_as_dict(self, provider): def item_as_dict(self, item) -> typing.Dict[str, typing.Any]:
type_ = provider.getType() 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 # 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 = [{ offers = [{
@ -89,16 +89,16 @@ class Providers(ModelHandler):
'icon': t.icon().replace('\n', '')} for t in type_.getServicesTypes()] 'icon': t.icon().replace('\n', '')} for t in type_.getServicesTypes()]
return { return {
'id': provider.uuid, 'id': item.uuid,
'name': provider.name, 'name': item.name,
'tags': [tag.vtag for tag in provider.tags.all()], 'tags': [tag.vtag for tag in item.tags.all()],
'services_count': provider.services.count(), 'services_count': item.services.count(),
'user_services_count': UserService.objects.filter(deployed_service__service__provider=provider).exclude(state__in=(State.REMOVED, State.ERROR)).count(), 'user_services_count': UserService.objects.filter(deployed_service__service__provider=item).exclude(state__in=(State.REMOVED, State.ERROR)).count(),
'maintenance_mode': provider.maintenance_mode, 'maintenance_mode': item.maintenance_mode,
'offers': offers, 'offers': offers,
'type': type_.type(), 'type': type_.type(),
'comments': provider.comments, 'comments': item.comments,
'permission': permissions.getEffectivePermission(self._user, provider) 'permission': permissions.getEffectivePermission(self._user, item)
} }
def checkDelete(self, item): def checkDelete(self, item):
@ -152,12 +152,12 @@ class Providers(ModelHandler):
def test(self, type_): def test(self, type_):
from uds.core.Environment import Environment from uds.core.Environment import Environment
logger.debug('Type: {}'.format(type_)) logger.debug('Type: %s', type_)
spType = services.factory().lookup(type_) spType = services.factory().lookup(type_)
self.ensureAccess(spType, permissions.PERMISSION_MANAGEMENT, root=True) self.ensureAccess(spType, permissions.PERMISSION_MANAGEMENT, root=True)
logger.debug('spType: {}'.format(spType)) logger.debug('spType: %s', spType)
dct = self._params.copy() dct = self._params.copy()
dct['_request'] = self._request dct['_request'] = self._request

View File

@ -30,15 +30,15 @@
""" """
@author: Adolfo Gómez, dkmaster at dkmon dot com @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.REST import model
from uds import reports from uds import reports
import six
import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -64,7 +64,7 @@ class Reports(model.BaseModelHandler):
def _findReport(self, uuid, values=None): def _findReport(self, uuid, values=None):
found = None found = None
logger.debug('Looking for report {}'.format(uuid)) logger.debug('Looking for report %s', uuid)
for i in reports.availableReports: for i in reports.availableReports:
if i.getUuid() == uuid: if i.getUuid() == uuid:
found = i(values) found = i(values)
@ -76,7 +76,7 @@ class Reports(model.BaseModelHandler):
return found return found
def get(self): 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) nArgs = len(self._args)
if nArgs == 0: if nArgs == 0:
@ -98,7 +98,7 @@ class Reports(model.BaseModelHandler):
""" """
Processes a PUT request 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: if len(self._args) != 1:
return self.invalidRequestException() return self.invalidRequestException()
@ -106,7 +106,7 @@ class Reports(model.BaseModelHandler):
report = self._findReport(self._args[0], self._params) report = self._findReport(self._args[0], self._params)
try: try:
logger.debug('Report: {}'.format(report)) logger.debug('Report: %s', report)
result = report.generateEncoded() result = report.generateEncoded()
data = { data = {

View File

@ -30,6 +30,10 @@
""" """
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
""" """
import logging
import six
from django.utils.translation import ugettext, ugettext_lazy as _ from django.utils.translation import ugettext, ugettext_lazy as _
from uds.models import DeployedService, OSManager, Service, Image, ServicesPoolGroup, Account from uds.models import DeployedService, OSManager, Service, Image, ServicesPoolGroup, Account
from uds.models.CalendarAction import ( from uds.models.CalendarAction import (
@ -56,8 +60,6 @@ from .op_calendars import AccessCalendars, ActionsCalendars
from .services import Services from .services import Services
from uds.core.managers import userServiceManager from uds.core.managers import userServiceManager
import six
import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -33,17 +33,15 @@
# pylint: disable=too-many-public-methods # pylint: disable=too-many-public-methods
from __future__ import unicode_literals import logging
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from uds.models import UserService from uds.models import UserService
from uds.core.util.State import State from uds.core.util.State import State
from uds.core.util.model import processUuid from uds.core.util.model import processUuid
from uds.core.util import log
from uds.REST.model import DetailHandler from uds.REST.model import DetailHandler
import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -30,7 +30,10 @@
""" """
@author: Adolfo Gómez, dkmaster at dkmon dot com @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 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.State import State
from uds.core.util import encoders from uds.core.util import encoders
from uds.REST import Handler, RequestError, ResponseError from uds.REST import Handler, RequestError, ResponseError
import pickle
from datetime import timedelta
import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2014 Virtual Cable S.L. # Copyright (c) 2014-2019 Virtual Cable S.L.
# 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,
@ -30,7 +30,10 @@
""" """
@author: Adolfo Gómez, dkmaster at dkmon dot com @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 Handler
from uds.REST import RequestError 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.model import processUuid
from uds.core.util import tools from uds.core.util import tools
import datetime
import six
import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
VALID_PARAMS = ('authId', 'authTag', 'authSmallName', 'auth', 'username', 'realname', 'password', 'groups', 'servicePool', 'transport', 'force', 'userIp') 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'] username = self._params['username']
password = self._params.get('password', '') # Some machines needs password, depending on configuration password = self._params.get('password', '') # Some machines needs password, depending on configuration
groups = self._params['groups'] groups = self._params['groups']
if isinstance(groups, (six.text_type, six.binary_type)): if isinstance(groups, (str, bytes)):
groups = (groups,) groups = (groups,)
grps = [] grps = []
for g in groups: for g in groups:
@ -204,7 +202,7 @@ class Tickets(Handler):
except Transport.DoesNotExist: except Transport.DoesNotExist:
return Tickets.result(error='Transport does not exists') return Tickets.result(error='Transport does not exists')
except Exception as e: except Exception as e:
return Tickets.result(error=six.text_type(e)) return Tickets.result(error=str(e))
data = { data = {
'username': username, 'username': username,

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2014 Virtual Cable S.L. # Copyright (c) 2014-2019 Virtual Cable S.L.
# 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,
@ -31,12 +31,19 @@
""" """
# pylint: disable=too-many-public-methods # 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 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.core.ui.UserInterface import gui as uiGui
from uds.REST.handlers import Handler, HandlerError from uds.REST.handlers import Handler, HandlerError
from uds.core.util import log 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.core.util.model import processUuid
from uds.models import Tag 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__) logger = logging.getLogger(__name__)
@ -71,7 +71,6 @@ class SaveException(HandlerError):
""" """
Exception thrown if couldn't save Exception thrown if couldn't save
""" """
pass
class BaseModelHandler(Handler): class BaseModelHandler(Handler):
@ -299,7 +298,7 @@ class BaseModelHandler(Handler):
# Details do not have types at all # Details do not have types at all
# so, right now, we only process details petitions for Handling & tables info # so, right now, we only process details petitions for Handling & tables info
# noinspection PyMissingConstructor # 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 Detail handler (for relations such as provider-->services, authenticators-->users,groups, deployed services-->cache,assigned, groups, transports
Urls recognized for GET are: 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 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 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 Processes GET method for a detail Handler
""" """
# Process args # 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) nArgs = len(self._args)
parent = self._kwargs['parent'] 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), 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) 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'] parent = self._kwargs['parent']
@ -514,7 +513,7 @@ class DetailHandler(BaseModelHandler): # pylint: disable=abstract-class-not-use
""" """
return {} 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 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 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 :param forType: Type of object needing gui
:return: a "gui" (list of gui fields) :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 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" The only detail that has types within is "Service", child of "Provider"
""" """
# Authentication related # Authentication related
authenticated = True authenticated: typing.ClassVar[bool] = True
needs_staff = True needs_staff: typing.ClassVar[bool] = True
# Which model does this manage # Which model does this manage
model = None model: typing.Optional[models.Model] = None
# By default, filter is empty # 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 # 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 # For example ('services', True) -- > .../id_parent/services
# ('services', False) --> ..../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 # 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 # Put needed fields
save_fields = [] save_fields: typing.Iterable[str] = []
# Put removable fields before updating # Put removable fields before updating
remove_fields = [] remove_fields: typing.Iterable[str] = []
# Table info needed fields and title # Table info needed fields and title
table_fields = [] table_fields: typing.Iterable[typing.Any] = []
table_row_style = {} table_row_style: typing.Dict = {}
table_title = '' table_title: str = ''
table_subtitle = '' table_subtitle: str = ''
# This methods must be override, depending on what is provided # This methods must be override, depending on what is provided
# Data related # Data related
def item_as_dict(self, item): def item_as_dict(self, item) -> typing.Dict[str, typing.Any]:
""" """
Must be overriden by descendants. Must be overriden by descendants.
Expects the return of an item as a dictionary Expects the return of an item as a dictionary
""" """
return None return {}
def item_as_dict_overview(self, item): def item_as_dict_overview(self, item):
""" """
@ -623,7 +623,7 @@ class ModelHandler(BaseModelHandler):
if found is None: if found is None:
raise NotFound('type not found') raise NotFound('type not found')
logger.debug('Found type {0}'.format(found)) logger.debug('Found type %s', found)
return found return found
# log related # log related
@ -633,8 +633,9 @@ class ModelHandler(BaseModelHandler):
return log.getLogs(item) return log.getLogs(item)
# gui related # gui related
def getGui(self, type_): def getGui(self, type_) -> typing.Iterable[typing.Any]:
self.invalidRequestException() return []
# self.invalidRequestException()
# Delete related, checks if the item can be deleted # Delete related, checks if the item can be deleted
# If it can't be so, raises an exception # If it can't be so, raises an exception
@ -662,7 +663,7 @@ class ModelHandler(BaseModelHandler):
if 'filter' in self._params: if 'filter' in self._params:
self.fltr = self._params['filter'] self.fltr = self._params['filter']
del self._params['filter'] # Remove parameter 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): def doFilter(self, data):
# Right now, filtering only supports a single filter, in a future # 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)): if not isinstance(data, (list, tuple, types.GeneratorType)):
return data return data
logger.debug('data: {}, fltr: {}'.format(data, self.fltr)) logger.debug('data: %s, fltr: %s', data, self.fltr)
try: try:
fld, pattern = self.fltr.split('=') fld, pattern = self.fltr.split('=')
s, e = '', '' s, e = '', ''
@ -697,11 +698,11 @@ class ModelHandler(BaseModelHandler):
res = list(filter(fltr_function, data)) res = list(filter(fltr_function, data))
logger.debug('After filtering: {}'.format(res)) logger.debug('After filtering: %s', res)
return res return res
except: except:
logger.exception('Exception:') 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)) raise RequestError('Filtering expression {} is invalid'.format(self.fltr))
return data return data
@ -709,7 +710,7 @@ class ModelHandler(BaseModelHandler):
# Helper to process detail # Helper to process detail
# Details can be managed (writen) by any user that has MANAGEMENT permission over parent # Details can be managed (writen) by any user that has MANAGEMENT permission over parent
def processDetail(self): def processDetail(self):
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: try:
item = self.model.objects.filter(uuid=self._args[0])[0] item = self.model.objects.filter(uuid=self._args[0])[0]
# If we do not have access to parent to, at least, read... # If we do not have access to parent to, at least, read...
@ -720,10 +721,10 @@ class ModelHandler(BaseModelHandler):
requiredPermission = permissions.PERMISSION_READ requiredPermission = permissions.PERMISSION_READ
if permissions.checkPermissions(self._user, item, requiredPermission) is False: 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() self.accessDenied()
detailCls = self.detail[self._args[1]] detailCls = self.detail[self._args[1]] # pylint: disable=unsubscriptable-object
args = list(self._args[2:]) args = list(self._args[2:])
path = self._path + '/'.join(args[:2]) path = self._path + '/'.join(args[:2])
detail = detailCls(self, path, self._params, *args, parent=item, user=self._user) detail = detailCls(self, path, self._params, *args, parent=item, user=self._user)

View File

@ -30,15 +30,15 @@
""" """
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. 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 django.utils.translation import ugettext_noop as _
from uds.core import Module from uds.core import Module
from uds.core.transports import protocols from uds.core.transports import protocols
from . import types from . import types
from .BasePublication import Publication
__updated__ = '2018-06-07' from .BaseDeployed import UserDeployment
class Service(Module): class Service(Module):
""" """
@ -79,38 +79,38 @@ class Service(Module):
""" """
# : Constant for indicating that max elements this service can deploy is unlimited. # : 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 # : Name of type, used at administration interface to identify this
# : service (i.e. Xen server, oVirt Server, ...) # : service (i.e. Xen server, oVirt Server, ...)
# : This string will be translated when provided to admin interface # : This string will be translated when provided to admin interface
# : using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop) # : using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop)
# : if you want so it can be translated. # : 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 # : 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 # : 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 # : module implementator will be the one that will provide a name that
# : will relation the class (type) and that name. # : will relation the class (type) and that name.
typeType = 'BaseService' typeType: str = 'BaseService'
# : Description shown at administration level for this service. # : Description shown at administration level for this service.
# : This string will be translated when provided to admin interface # : This string will be translated when provided to admin interface
# : using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop) # : using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop)
# : if you want so it can be translated. # : 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 # : 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 # : 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. # : your own :py:meth:uds.core.BaseModule.BaseModule.icon method.
iconFile = 'service.png' iconFile: str = 'service.png'
# Functional related data # Functional related data
# : Normally set to UNLIMITED. This attribute indicates if the service has some "limitation" # : 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 # : 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. # : 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 # : If this item "has constains", on deployed service edition, defined keys will overwrite defined ones
cacheConstrains = None cacheConstrains = None
@ -134,7 +134,7 @@ class Service(Module):
cacheTooltip_L2 = _('None') # : Tooltip shown to user when this item is pointed at admin interface 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) # : 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 # : 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 # : 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 # : 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 # : 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) # : 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) # : Types of publications (preparated data for deploys)
# : If you provide this, UDS will assume that the service needs a preparation. # : If you provide this, UDS will assume that the service needs a preparation.
@ -152,23 +152,23 @@ class Service(Module):
# : provide a publication type # : provide a publication type
# : This refers to class that provides the logic for publication, you can see # : This refers to class that provides the logic for publication, you can see
# : :py:class:uds.core.services.Publication # : :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) # : Types of deploys (services in cache and/or assigned to users)
# : This is ALWAYS a MUST. You mast indicate the class responsible # : This is ALWAYS a MUST. You mast indicate the class responsible
# : for managing the user deployments (user consumable services generated # : for managing the user deployments (user consumable services generated
# : from this one). If this attribute is not set, the service will never work # : from this one). If this attribute is not set, the service will never work
# : (core will not know how to handle the user deployments) # : (core will not know how to handle the user deployments)
deployedType = None deployedType: typing.ClassVar[typing.Optional[typing.Type[UserDeployment]]] = None
# : Restricted transports # : Restricted transports
# : If this list contains anything else but emtpy, the only allowed protocol for transports # : If this list contains anything else but emtpy, the only allowed protocol for transports
# : will be the ones listed here (on implementation, ofc) # : 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) # : 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 # : 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 # : If the service allows "reset", here we will announce it
# : Defaults to False # : Defaults to False
@ -176,7 +176,7 @@ class Service(Module):
# : 'kind' of services that this service provides: # : 'kind' of services that this service provides:
# : For example, VDI, VAPP, ... # : For example, VDI, VAPP, ...
servicesTypeProvided = types.ALL servicesTypeProvided: typing.Iterable = types.ALL
def __init__(self, environment, parent, values=None, uuid=None): def __init__(self, environment, parent, values=None, uuid=None):
""" """

View File

@ -74,13 +74,15 @@ class XenLinkedService(Service):
usesCache = True usesCache = True
# : Tooltip shown to user when this item is pointed at admin interface, none # : Tooltip shown to user when this item is pointed at admin interface, none
# : because we don't use it # : 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 # : If we need to generate a "Level 2" cache for this service (i.e., L1
# : could be running machines and L2 suspended machines) # : could be running machines and L2 suspended machines)
usesCache_L2 = True usesCache_L2 = True
# : Tooltip shown to user when this item is pointed at admin interface, None # : Tooltip shown to user when this item is pointed at admin interface, None
# : also because we don't use it # : 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 # : If the service needs a s.o. manager (managers are related to agents
# : provided by services itselfs, i.e. virtual machines with actors) # : provided by services itselfs, i.e. virtual machines with actors)
@ -168,7 +170,8 @@ class XenLinkedService(Service):
label=_('Name Length'), label=_('Name Length'),
defvalue=5, defvalue=5,
order=115, 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'), tab=_('Machine'),
required=True required=True
) )
@ -183,11 +186,14 @@ class XenLinkedService(Service):
if values is not None: if values is not None:
length = int(self.lenName.value) length = int(self.lenName.value)
if len(self.baseName.value) + length > 15: 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(): 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: 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): def initGui(self):
""" """
@ -206,8 +212,10 @@ class XenLinkedService(Service):
machines_list.append(gui.choiceItem(m['id'], m['name'])) machines_list.append(gui.choiceItem(m['id'], m['name']))
storages_list = [] storages_list = []
for storage in storages: for storage in storages:
space, free = storage['size'] / 1024, (storage['size'] - storage['used']) / 1024 space, free = storage['size'] / \
storages_list.append(gui.choiceItem(storage['id'], "%s (%4.2f Gb/%4.2f Gb)" % (storage['name'], space, free))) 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 = [] network_list = []
for net in networks: for net in networks:
network_list.append(gui.choiceItem(net['id'], net['name'])) network_list.append(gui.choiceItem(net['id'], net['name']))
@ -221,12 +229,14 @@ class XenLinkedService(Service):
def datastoreHasSpace(self): def datastoreHasSpace(self):
# Get storages for that datacenter # 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) info = self.parent().getStorageInfo(self.datastore.value)
logger.debug('Datastore Info: {0}'.format(info)) logger.debug('Datastore Info: {0}'.format(info))
availableGB = (info['size'] - info['used']) / 1024 availableGB = (info['size'] - info['used']) / 1024
if availableGB < self.minSpaceGB.num(): 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): def sanitizeVmName(self, name):
""" """
@ -248,7 +258,8 @@ class XenLinkedService(Service):
Raises an exception if operation fails. 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 # Checks datastore size
self.datastoreHasSpace() self.datastoreHasSpace()
@ -256,6 +267,7 @@ class XenLinkedService(Service):
def convertToTemplate(self, machineId): def convertToTemplate(self, machineId):
""" """
converts machine to template
""" """
self.parent().convertToTemplate(machineId, self.shadow.value) self.parent().convertToTemplate(machineId, self.shadow.value)
@ -274,7 +286,8 @@ class XenLinkedService(Service):
Returns: Returns:
Id of the machine being created form template 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() self.datastoreHasSpace()
return self.parent().startDeployFromTemplate(name, comments, templateId) return self.parent().startDeployFromTemplate(name, comments, templateId)
@ -406,4 +419,3 @@ class XenLinkedService(Service):
Returns the selected display type (for created machines, for administration Returns the selected display type (for created machines, for administration
""" """
return self.display.value return self.display.value