1
0
mirror of https://github.com/dkmstr/openuds.git synced 2024-12-22 13:34:04 +03:00

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
.vscode
.mypy_cache

View File

@ -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??

View File

@ -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

View File

@ -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 = {

View File

@ -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__)

View File

@ -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__)

View File

@ -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__)

View File

@ -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,

View File

@ -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)

View File

@ -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):
"""

View File

@ -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