forked from shaba/openuds
fixes for python3 & type hinting
This commit is contained in:
parent
2baac2f532
commit
613f4efd31
1
.gitignore
vendored
1
.gitignore
vendored
@ -167,3 +167,4 @@
|
||||
/udsService/udsgui/obj/x86
|
||||
|
||||
.vscode
|
||||
.mypy_cache
|
||||
|
@ -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??
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user