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

Started adding Automatic Cluster services support, so we can simplify using services as ProxMox, HyperV, ESXi (without VC), etc... as Providers.

This clusters are special type of providers, where that providers contains several nodes, and do not provide automatic placement of machines, etc...
This commit is contained in:
Adolfo Gómez 2013-09-06 12:51:01 +00:00
parent 81b1646458
commit 50b7a55be5
18 changed files with 343 additions and 71 deletions

View File

@ -67,6 +67,8 @@ encoding//src/uds/core/services/BaseDeployed.py=utf-8
encoding//src/uds/core/services/BasePublication.py=utf-8
encoding//src/uds/core/services/BaseService.py=utf-8
encoding//src/uds/core/services/BaseServiceProvider.py=utf-8
encoding//src/uds/core/services/ClusteredService.py=utf-8
encoding//src/uds/core/services/ClusteredServiceProvider.py=utf-8
encoding//src/uds/core/services/Exceptions.py=utf-8
encoding//src/uds/core/services/ServiceProviderFactory.py=utf-8
encoding//src/uds/core/services/__init__.py=utf-8
@ -95,6 +97,7 @@ encoding//src/uds/core/util/stats/charts.py=utf-8
encoding//src/uds/core/util/stats/counters.py=utf-8
encoding//src/uds/core/workers/AssignedAndUnused.py=utf-8
encoding//src/uds/core/workers/CacheCleaner.py=utf-8
encoding//src/uds/core/workers/ClusteredProviderManagement.py=utf-8
encoding//src/uds/core/workers/DeployedServiceCleaner.py=utf-8
encoding//src/uds/core/workers/HangedCleaner.py=utf-8
encoding//src/uds/core/workers/PublicationCleaner.py=utf-8
@ -135,6 +138,7 @@ encoding//src/uds/osmanagers/WindowsOsManager/WinRandomPassOsManager.py=utf-8
encoding//src/uds/osmanagers/WindowsOsManager/WindowsOsManager.py=utf-8
encoding//src/uds/osmanagers/WindowsOsManager/__init__.py=utf-8
encoding//src/uds/osmanagers/__init__.py=utf-8
encoding//src/uds/services/HyperV_enterprise/HyperVClusterProvider.py=utf-8
encoding//src/uds/services/HyperV_enterprise/HyperVLinkedDeployment.py=utf-8
encoding//src/uds/services/HyperV_enterprise/HyperVLinkedService.py=utf-8
encoding//src/uds/services/HyperV_enterprise/HyperVProvider.py=utf-8

View File

@ -30,7 +30,7 @@
'''
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
'''
from django.utils.translation import ugettext_noop as translatable
from django.utils.translation import ugettext_noop as _
from uds.core.ui.UserInterface import gui
from uds.core import auths
@ -66,9 +66,9 @@ class SampleAuth(auths.Authenticator):
#: Name of type, used at administration interface to identify this
#: authenticator (i.e. LDAP, SAML, ...)
#: This string will be translated when provided to admin interface
#: using ugettext, so you can mark it as "translatable" 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.
typeName = translatable('Sample Authenticator')
typeName = _('Sample Authenticator')
#: 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
@ -78,9 +78,9 @@ class SampleAuth(auths.Authenticator):
#: Description shown at administration level for this authenticator.
#: This string will be translated when provided to admin interface
#: using ugettext, so you can mark it as "translatable" 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.
typeDescription = translatable('Sample dummy authenticator')
typeDescription = _('Sample dummy authenticator')
#: Icon file, used to represent this authenticator at administration interface
@ -99,16 +99,16 @@ class SampleAuth(auths.Authenticator):
#: needsPassword = False
#: Label for username field, shown at administration interface user form.
userNameLabel = translatable('Fake User')
userNameLabel = _('Fake User')
# Label for group field, shown at administration interface user form.
groupNameLabel = translatable('Fake Group')
groupNameLabel = _('Fake Group')
#: Definition of this type of authenticator form
#: We will define a simple form where we will use a simple
#: list editor to allow entering a few group names
groups = gui.EditableList(label=translatable('Groups'), values = ['Gods', 'Daemons', 'Mortals'])
groups = gui.EditableList(label=_('Groups'), values = ['Gods', 'Daemons', 'Mortals'])
def initialize(self, values):
'''
@ -121,7 +121,7 @@ class SampleAuth(auths.Authenticator):
# unserialization, and at this point all will be default values
# so self.groups.value will be []
if values is not None and len(self.groups.value) < 2:
raise auths.Authenticator.ValidationException(translatable('We need more that two items!'))
raise auths.Authenticator.ValidationException(_('We need more that two items!'))
def searchUsers(self, pattern):
'''

View File

@ -32,7 +32,7 @@ Base module for all authenticators
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
'''
from uds.core import Module
from django.utils.translation import ugettext_noop as translatable
from django.utils.translation import ugettext_noop as _
from GroupsManager import GroupsManager
from Exceptions import InvalidUserException
import logging
@ -83,19 +83,19 @@ class Authenticator(Module):
so we have defined isExternalSource as True by default, that will be most
cases.
:note: All attributes that are "translatable" here means that they will be
:note: All attributes that are "_" here means that they will be
translated when provided to administration interface, so remember
to mark them in your own authenticators as "translatable" using
ugettext_noop. We have aliased it here to "translatable" so it's
to mark them in your own authenticators as "_" using
ugettext_noop. We have aliased it here to "_" so it's
easier to understand.
'''
#: Name of type, used at administration interface to identify this
#: authenticator (i.e. LDAP, SAML, ...)
#: This string will be translated when provided to admin interface
#: using ugettext, so you can mark it as "translatable" 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.
typeName = translatable('Base Authenticator')
typeName = _('Base Authenticator')
#: 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
@ -105,9 +105,9 @@ class Authenticator(Module):
#: Description shown at administration level for this authenticator.
#: This string will be translated when provided to admin interface
#: using ugettext, so you can mark it as "translatable" 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.
typeDescription = translatable('Base Authenticator')
typeDescription = _('Base Authenticator')
#: Icon file, used to represent this authenticator at administration interface
@ -125,15 +125,15 @@ class Authenticator(Module):
needsPassword = False
#: Label for username field, shown at administration interface user form.
userNameLabel = translatable('User name')
userNameLabel = _('User name')
#: Label for group field, shown at administration interface user form.
groupNameLabel = translatable('Group name')
groupNameLabel = _('Group name')
#: Label for password field, , shown at administration interface user form.
#: Not needed for external authenticators (where credentials are stored with
#: an already existing user.
passwordLabel = translatable('Password')
passwordLabel = _('Password')
#: If this authenticators casues a temporal block of an user on repeated login failures
blockUserOnLoginFailures = True
@ -533,7 +533,7 @@ class Authenticator(Module):
says that user can't be created manually
'''
raise InvalidUserException(translatable('Users can\'t be created inside this authenticator'))
raise InvalidUserException(_('Users can\'t be created inside this authenticator'))
def modifyUser(self, usrData):

View File

@ -55,3 +55,14 @@ class DelayedTask(Environmentable):
You must provide your own "run" method to do whatever you need
'''
logging.debug("Base run of job called for class")
def register(self, suggestedTime, tag='', check=True):
'''
Utility method that allows to register a Delayedtask
'''
from DelayedTaskRunner import DelayedTaskRunner
if check is True and DelayedTaskRunner.runner().checkExists(tag):
return
DelayedTaskRunner.runner().insert(self, suggestedTime, tag)

View File

@ -30,6 +30,7 @@
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
from django.db import transaction
from django.db.models import Q
@ -102,7 +103,12 @@ class DelayedTaskRunner(object):
now = datetime.now()
exec_time = now + timedelta(seconds = delay)
cls = instance.__class__
dbDelayedTask.objects.create(type = str(cls.__module__ + '.' + cls.__name__), instance = dumps(instance).encode(self.CODEC),
instanceDump = dumps(instance).encode(self.CODEC)
typeName = str(cls.__module__ + '.' + cls.__name__)
logger.debug('Inserting delayed task {0} with {1} bytes'.format(typeName, len(instanceDump)))
dbDelayedTask.objects.create(type = typeName, instance = instanceDump,
insert_date = now, execution_delay = delay, execution_time = exec_time, tag = tag)
def insert(self, instance, delay, tag = ''):

View File

@ -71,7 +71,8 @@ class TaskManager(object):
TaskManager.keepRunning = False
@staticmethod
def registerJob(jobName, jobType):
def registerJob(jobType):
jobName = jobType.friendly_name
jobs.factory().insert(jobName, jobType)

View File

@ -30,6 +30,7 @@
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
import logging
@ -50,8 +51,9 @@ class OSManagersFactory(object):
def providers(self):
return self._jobs
def insert(self, type):
self._jobs[type.type()] = type
def insert(self, type_):
logger.debug('Adding OS Manager {0} as {1}'.format(type_.type(), type_))
self._jobs[type_.type()] = type_
def lookup(self, typeName):
try:

View File

@ -30,7 +30,9 @@
'''
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
'''
from django.utils.translation import ugettext_noop as translatable
from __future__ import unicode_literals
from django.utils.translation import ugettext_noop as _
from uds.core import Module
class Service(Module):
@ -77,9 +79,9 @@ class Service(Module):
#: 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 "translatable" 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.
typeName = translatable('Base Service')
typeName = _('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
@ -89,9 +91,9 @@ class Service(Module):
#: 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 "translatable" 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.
typeDescription = translatable('Base Service')
typeDescription = _('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
@ -108,20 +110,20 @@ class Service(Module):
#: If this class uses cache or not. If uses cache is true, means that the
#: service can "prepare" some user deployments to allow quicker user access
#: to services if he already do not have one.
#: If you set this to True, please, provide a translatable :py:attr:.cacheToolTip
#: If you set this to True, please, provide a _ :py:attr:.cacheToolTip
usesCache = False
#: Tooltip to be used if services uses cache at administration interface, indicated by :py:attr:.usesCache
cacheTooltip = translatable('None') #: Tooltip shown to user when this item is pointed at admin interface
cacheTooltip = _('None') #: Tooltip shown to user when this item is pointed at admin interface
#: If user deployments can be cached (see :py:attr:.usesCache), may he also can provide a secondary cache,
#: that is no more that user deployments that are "almost ready" to be used, but preperably consumes less
#: resources than L1 cache. This can give a boost to cache L1 recovering in case of peaks
#: in demand. If you set this to True, please, provide also a translatable :py:attr:.cacheTooltip_L2
#: in demand. If you set this to True, please, provide also a _ :py:attr:.cacheTooltip_L2
usesCache_L2 = False #: If we need to generate a "Level 2" cache for this service (i.e., L1 could be running machines and L2 suspended machines)
#: Tooltip to be used if services uses L2 cache at administration interface, indicated by :py:attr:.usesCache_L2
cacheTooltip_L2 = translatable('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)
needsManager = False

View File

@ -30,6 +30,8 @@
'''
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
from uds.core import Module
import logging

View File

@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
from django.utils.translation import ugettext_noop as _
from BaseService import Service
class ClusteredService(Service):
typeName = _('Base Clustered Service')
typeType = 'BaseClusteredService'
typeDescription = _('Base Clustered Service')
iconFile = 'service.png'

View File

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
from BaseServiceProvider import ServiceProvider
from uds.core import managers
import logging
logger = logging.getLogger(__name__)
class ClusteredServiceProvider(ServiceProvider):
'''
This class represents a Clustered Service Provider, that is, a Service provider that forms a Cluster and needs
"organization".
It adds the needed methods to keep cluster "in good shape"
'''
typeName = 'Base Clustered Provider'
typeType = 'BaseClusteredServiceProvider'
typeDescription = 'Base Clustered Service Provider'
iconFile = 'provider.png'
allowInUseMigration = False # If True, means that we can migrate a service while it is being used
def clusterStats(self):
stats = self.storage().getPickle('ClusterStats')
if stats is None:
stats = {}
return stats
# This method must be overriden
def getClusterNodes(self):
'''
This method must be overriden.
returns the nodes of this clusters as an array dictionaries, with the id of nodes and the is the node is "active".
Active means that it is ready to process services, inactive means that services are not available
This ids must be recognized later by nodes methods of ClusteredServiceProvider
Example:
[ { 'id': 'node1', 'active': True }, { 'id': 'node2', 'active': False }]
'''
return []
def getClusterNodeLoad(self, nodeId):
'''
This method must be overriden
Returns the load of a node of the cluster, as a dictionary, with 3 informations used right now:
{ 'cpuLoad':, 'freeMemory'}
If any value is not known or can't be obtained, this can be not included in resulting dictionary, or
it's value can be None
The units for elements are:
* cpuLoad: Load of cpu of Node (use) in %. If server has more than one CPU, average can be used (Integer)
* freeMemory: Unused memory (or usable memory) of node, expressed in Kb (Integer)
'''
return {'cpuLoad': None, 'freeMemory': None} # We could have used return {}, but i prefer this "sample template"

View File

@ -74,6 +74,10 @@ class ServiceProviderFactory(object):
# We will check that if service provided by "provider" needs
# cache, but service do not provides publicationType,
# that service will not be registered and it will be informed
if self._providers.get(type_.type(), None) is not None:
logger.debug('{0} already registered as Service Provider'.format(type_))
return
offers = []
for s in type_.offers:
if s.usesCache_L2 is True:
@ -87,6 +91,7 @@ class ServiceProviderFactory(object):
# Only offers valid services
type_.offers = offers
logger.debug('Adding provider {0} as {1}'.format(type_.type(), type_))
self._providers[type_.type()] = type_
def lookup(self, typeName):

View File

@ -37,6 +37,10 @@ from BaseServiceProvider import ServiceProvider
from BaseService import Service
from BasePublication import Publication
from BaseDeployed import UserDeployment
from ClusteredServiceProvider import ClusteredServiceProvider
from ClusteredService import ClusteredService
import Exceptions
def factory():

View File

@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2013 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
from uds.core.jobs.Job import Job
from uds.core.jobs import DelayedTask
from uds.models import Provider
import logging
logger = logging.getLogger(__name__)
GETCLUSTERSTATS_TAG = 'ClstrStats'
# Utility to get all providers that are derived from
def getClusteredProvidersFromDB():
#services.ClusteredServiceProvider.
from uds.core import services
p = services.ClusteredServiceProvider
for prov in Provider.objects.all():
for cls in p.__subclasses__():
if prov.isOfType(cls.typeType):
yield prov
class ClusterUpdateStatsTask(DelayedTask):
def __init__(self, providerId):
super(ClusterUpdateStatsTask,self).__init__()
self._id = providerId
def run(self):
try:
provider = Provider.objects.get(pk=self._id)
logger.debug('Updating stats for {0}'.format(provider.name))
cluster = provider.getInstance()
nodes = cluster.getClusterNodes()
stats = {}
for node in nodes:
s = cluster.getClusterNodeLoad(node['id'])
stats[node['id']] = { 'cpuLoad': s.get('cpuLoad', None), 'freeMemory': s.get('freeMemory', None) }
cluster.storage().putPickle('ClusterStats', stats)
except:
logger.exception('Exception')
# Removed provider, no problem at all, no update is done
pass
# Job for managing ClusteredServiceProvider
class ClusterUpdateStats(Job):
frecuency = 60 # Once every 60 seconds
friendly_name = 'Clustered Providers Statistics Update'
def __init__(self, environment):
super(ClusterUpdateStats,self).__init__(environment)
def run(self):
logger.debug('Clustered Service manager started')
for p in getClusteredProvidersFromDB():
logger.debug('Getting stats for clustered provider {0}'.format(p.name))
ct = ClusterUpdateStatsTask(p.id)
ct.register(0, '{0}_{1}'.format(GETCLUSTERSTATS_TAG, p.id), True)

View File

@ -52,6 +52,6 @@ def __init__():
for cls in p.__subclasses__():
# Limit to autoregister just workers jobs inside this module
if cls.__module__[0:16] == 'uds.core.workers':
TaskManager.registerJob(cls.friendly_name, cls)
TaskManager.registerJob(cls)
__init__()

View File

@ -33,7 +33,7 @@ Created on Jun 22, 2012
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
'''
from django.utils.translation import ugettext_noop as translatable, ugettext as _
from django.utils.translation import ugettext_noop as _, ugettext as _
from uds.core.services import ServiceProvider
from SampleService import ServiceOne, ServiceTwo
from uds.core.ui import gui
@ -64,15 +64,15 @@ class Provider(ServiceProvider):
offers = [ServiceOne, ServiceTwo]
#: Name to show the administrator. This string will be translated BEFORE
#: sending it to administration interface, so don't forget to
#: mark it as translatable (using ugettext_noop)
typeName = translatable('Sample Provider')
#: mark it as _ (using ugettext_noop)
typeName = _('Sample Provider')
#: Type used internally to identify this provider
typeType = 'SampleProvider'
#: Description shown at administration interface for this provider
typeDescription = translatable('Sample (and dummy) service provider')
typeDescription = _('Sample (and dummy) service provider')
#: Icon file used as icon for this provider. This string will be translated
#: BEFORE sending it to administration interface, so don't forget to
#: mark it as translatable (using ugettext_noop)
#: mark it as _ (using ugettext_noop)
iconFile = 'provider.png'
# now comes the form fields
@ -86,18 +86,18 @@ class Provider(ServiceProvider):
# "random"
#: Remote host. Here core will translate label and tooltip, remember to
#: mark them as translatable using ugettext_noop.
#: mark them as _ using ugettext_noop.
remoteHost = gui.TextField(oder=1,
length = 64,
label = translatable('Remote host'),
tooltip = translatable('This fields contains a remote host'),
label = _('Remote host'),
tooltip = _('This fields contains a remote host'),
required = True,
)
#: Name of your pet (sample, not really needed :-) )
petName = gui.TextField(order=2,
length = 32,
label = translatable('Your pet\'s name'),
tooltip = translatable('If you like, write the name of your pet'),
label = _('Your pet\'s name'),
tooltip = _('If you like, write the name of your pet'),
requred = False,
defvalue = 'Tux' #: This will not get translated
)
@ -106,16 +106,16 @@ class Provider(ServiceProvider):
#: "Tiene mas años que matusalén"(is older than Methuselah)
methAge = gui.NumericField(order = 3,
length = 4, # That is, max allowed value is 9999
label = translatable('Age of Methuselah'),
tooltip = translatable('If you know it, please, tell me!!!'),
label = _('Age of Methuselah'),
tooltip = _('If you know it, please, tell me!!!'),
required = True, #: Numeric fields have always a value, so this not really needed
defvalue = '4500'
)
#: Is Methuselah istill alive?
methAlive = gui.CheckBoxField(order = 4,
label = translatable('Is Methuselah still alive?'),
tooltip = translatable('If you fails, this will not get saved :-)'),
label = _('Is Methuselah still alive?'),
tooltip = _('If you fails, this will not get saved :-)'),
required = True, #: Also means nothing. Check boxes has always a value
defvalue = gui.TRUE #: By default, at new item, check this
)

View File

@ -31,7 +31,7 @@
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
'''
from django.utils.translation import ugettext_noop as translatable
from django.utils.translation import ugettext_noop as _
from uds.core.services import Service
from SamplePublication import SamplePublication
from SampleUserDeploymentOne import SampleUserDeploymentOne
@ -67,15 +67,15 @@ class ServiceOne(Service):
'''
#: Name to show the administrator. This string will be translated BEFORE
#: sending it to administration interface, so don't forget to
#: mark it as translatable (using ugettext_noop)
typeName = translatable('Sample Service One')
#: mark it as _ (using ugettext_noop)
typeName = _('Sample Service One')
#: Type used internally to identify this provider
typeType = 'SampleService1'
#: Description shown at administration interface for this provider
typeDescription = translatable('Sample (and dummy) service ONE')
typeDescription = _('Sample (and dummy) service ONE')
#: Icon file used as icon for this provider. This string will be translated
#: BEFORE sending it to administration interface, so don't forget to
#: mark it as translatable (using ugettext_noop)
#: mark it as _ (using ugettext_noop)
iconFile = 'service.png'
# Functional related data
@ -89,13 +89,13 @@ class ServiceOne(Service):
usesCache = False
#: Tooltip shown to user when this item is pointed at admin interface, none
#: because we don't use it
cacheTooltip = translatable('None')
cacheTooltip = _('None')
#: 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 = False
#: Tooltip shown to user when this item is pointed at admin interface, None
#: also because we don't use it
cacheTooltip_L2 = translatable('None')
cacheTooltip_L2 = _('None')
#: If the service needs a s.o. manager (managers are related to agents
#: provided by services itselfs, i.e. virtual machines with actors)
@ -115,8 +115,8 @@ class ServiceOne(Service):
# "random"
colour = gui.ChoiceField(order = 1,
label = translatable('Colour'),
tooltip = translatable('Colour of the field'),
label = _('Colour'),
tooltip = _('Colour of the field'),
# In this case, the choice can have none value selected by default
required = True,
values = [ gui.choiceItem('red', 'Red'),
@ -128,15 +128,15 @@ class ServiceOne(Service):
)
passw = gui.PasswordField(order = 2,
label = translatable('Password'),
tooltip = translatable('Password for testing purposes'),
label = _('Password'),
tooltip = _('Password for testing purposes'),
required = True,
defvalue = '1234' #: Default password are nonsense?? :-)
)
baseName = gui.TextField(order = 3,
label = translatable('Services names'),
tooltip = translatable('Base name for this user services'),
label = _('Services names'),
tooltip = _('Base name for this user services'),
# In this case, the choice can have none value selected by default
required = True,
defvalue = '' # Default value is the ID of the choicefield
@ -193,17 +193,17 @@ class ServiceTwo(Service):
'''
Just a second service, no comments here (almost same that ServiceOne
'''
typeName = translatable('Sample Service Two')
typeName = _('Sample Service Two')
typeType = 'SampleService2'
typeDescription = translatable('Sample (and dummy) service ONE+ONE')
typeDescription = _('Sample (and dummy) service ONE+ONE')
iconFile = 'provider.png' #: We reuse provider icon here :-)
# Functional related data
maxDeployed = 5
usesCache = True
cacheTooltip = translatable('L1 cache for dummy elements')
cacheTooltip = _('L1 cache for dummy elements')
usesCache_L2 = True
cacheTooltip_L2 = translatable('L2 cache for dummy elements')
cacheTooltip_L2 = _('L2 cache for dummy elements')
needsManager = False
mustAssignManually = False
@ -217,7 +217,7 @@ class ServiceTwo(Service):
# Gui, we will use here the EditableList field
names = gui.EditableList(label=translatable('List of names'))
names = gui.EditableList(label=_('List of names'))
def __init__(self, environment, parent, values = None):
'''

View File

@ -40,6 +40,10 @@ The registration of modules is done locating subclases of :py:class:`uds.core.au
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
'''
import logging
logger = logging.getLogger(__name__)
def __init__():
'''
@ -55,9 +59,12 @@ def __init__():
for _, name, _ in pkgutil.iter_modules([pkgpath]):
__import__(name, globals(), locals(), [], -1)
p = services.ServiceProvider
# This is marked as error in IDE, but it's not (__subclasses__)
for cls in p.__subclasses__():
services.factory().insert(cls)
for p in [services.ServiceProvider, services.ClusteredServiceProvider]:
# This is marked as error in IDE, but it's not (__subclasses__)
for cls in p.__subclasses__():
# Skip ClusteredServiceProvider
if cls is services.ClusteredServiceProvider:
continue
services.factory().insert(cls)
__init__()