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:
parent
81b1646458
commit
50b7a55be5
@ -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
|
||||
|
@ -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):
|
||||
'''
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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 = ''):
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
43
server/src/uds/core/services/ClusteredService.py
Normal file
43
server/src/uds/core/services/ClusteredService.py
Normal 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'
|
||||
|
94
server/src/uds/core/services/ClusteredServiceProvider.py
Normal file
94
server/src/uds/core/services/ClusteredServiceProvider.py
Normal 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"
|
||||
|
@ -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):
|
||||
|
@ -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():
|
||||
|
91
server/src/uds/core/workers/ClusteredProviderManagement.py
Normal file
91
server/src/uds/core/workers/ClusteredProviderManagement.py
Normal 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)
|
@ -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__()
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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):
|
||||
'''
|
||||
|
@ -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__()
|
||||
|
Loading…
Reference in New Issue
Block a user