forked from shaba/openuds
Added safety "min space" to oVirt an HyperV
Fixes related to pep
This commit is contained in:
parent
1140f1b4e6
commit
86318a66c3
@ -10,8 +10,9 @@ import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class oVirtHelpers(object):
|
||||
|
||||
|
||||
@staticmethod
|
||||
def getResources(parameters):
|
||||
'''
|
||||
@ -23,23 +24,23 @@ class oVirtHelpers(object):
|
||||
env = Environment(parameters['ev'])
|
||||
provider = Provider(env)
|
||||
provider.unserialize(parameters['ov'])
|
||||
|
||||
|
||||
# Obtains datacenter from cluster
|
||||
ci = provider.getClusterInfo(parameters['cluster'])
|
||||
|
||||
res = []
|
||||
res = []
|
||||
# Get storages for that datacenter
|
||||
for storage in provider.getDatacenterInfo(ci['datacenter_id'])['storage']:
|
||||
if storage['type'] == 'data':
|
||||
space, free = storage['available']/1024/1024/1024, (storage['available']-storage['used'])/1024/1024/1024
|
||||
|
||||
res.append( {'id': storage['id'], 'text': "%s (%4.2f Gb/%4.2f Gb) %s" % (storage['name'], space, free, storage['active'] and '(ok)' or '(disabled)' ) })
|
||||
data = [{
|
||||
'name' : 'datastore', 'values' : res
|
||||
}]
|
||||
|
||||
space, free = storage['available'] / 1024 / 1024 / 1024, (storage['available'] - storage['used']) / 1024 / 1024 / 1024
|
||||
|
||||
res.append({'id': storage['id'], 'text': "%s (%4.2f Gb/%4.2f Gb) %s" % (storage['name'], space, free, storage['active'] and '(ok)' or '(disabled)')})
|
||||
data = [
|
||||
{
|
||||
'name': 'datastore',
|
||||
'values': res
|
||||
}
|
||||
]
|
||||
|
||||
logger.debug('return data: {0}'.format(data))
|
||||
return data
|
||||
|
||||
|
||||
|
||||
|
@ -4,27 +4,27 @@
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# * 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
|
||||
# * 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
|
||||
# * 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
|
||||
# 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
|
||||
# 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.
|
||||
|
||||
'''
|
||||
@ -44,21 +44,22 @@ opCreate, opStart, opStop, opSuspend, opRemove, opWait, opError, opFinish, opRet
|
||||
|
||||
NO_MORE_NAMES = 'NO-NAME-ERROR'
|
||||
|
||||
|
||||
class OVirtLinkedDeployment(UserDeployment):
|
||||
'''
|
||||
This class generates the user consumable elements of the service tree.
|
||||
|
||||
|
||||
After creating at administration interface an Deployed Service, UDS will
|
||||
create consumable services for users using UserDeployment class as
|
||||
provider of this elements.
|
||||
|
||||
|
||||
The logic for managing ovirt deployments (user machines in this case) is here.
|
||||
|
||||
|
||||
'''
|
||||
|
||||
#: Recheck every six seconds by default (for task methods)
|
||||
|
||||
# : Recheck every six seconds by default (for task methods)
|
||||
suggestedTime = 6
|
||||
|
||||
|
||||
def initialize(self):
|
||||
self._name = ''
|
||||
self._ip = ''
|
||||
@ -67,13 +68,13 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
self._reason = ''
|
||||
self._queue = []
|
||||
|
||||
# Serializable needed methods
|
||||
# Serializable needed methods
|
||||
def marshal(self):
|
||||
'''
|
||||
Does nothing right here, we will use envoronment storage in this sample
|
||||
'''
|
||||
return '\1'.join( ['v1', self._name, self._ip, self._mac, self._vmid, self._reason, cPickle.dumps(self._queue)] )
|
||||
|
||||
return '\1'.join(['v1', self._name, self._ip, self._mac, self._vmid, self._reason, cPickle.dumps(self._queue)])
|
||||
|
||||
def unmarshal(self, str_):
|
||||
'''
|
||||
Does nothing here also, all data are keeped at environment storage
|
||||
@ -85,38 +86,37 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
|
||||
def getName(self):
|
||||
'''
|
||||
We override this to return a name to display. Default inplementation
|
||||
We override this to return a name to display. Default inplementation
|
||||
(in base class), returns getUniqueIde() value
|
||||
This name will help user to identify elements, and is only used
|
||||
at administration interface.
|
||||
|
||||
|
||||
We will use here the environment name provided generator to generate
|
||||
a name for this element.
|
||||
|
||||
The namaGenerator need two params, the base name and a length for a
|
||||
|
||||
The namaGenerator need two params, the base name and a length for a
|
||||
numeric incremental part for generating unique names. This are unique for
|
||||
all UDS names generations, that is, UDS will not generate this name again
|
||||
until this name is freed, or object is removed, what makes its environment
|
||||
to also get removed, that makes all uniques ids (names and macs right now)
|
||||
to also get released.
|
||||
|
||||
|
||||
Every time get method of a generator gets called, the generator creates
|
||||
a new unique name, so we keep the first generated name cached and don't
|
||||
generate more names. (Generator are simple utility classes)
|
||||
'''
|
||||
if self._name == '':
|
||||
if self._name == '':
|
||||
try:
|
||||
self._name = self.nameGenerator().get( self.service().getBaseName(), self.service().getLenName() )
|
||||
self._name = self.nameGenerator().get(self.service().getBaseName(), self.service().getLenName())
|
||||
except KeyError:
|
||||
return NO_MORE_NAMES
|
||||
return self._name
|
||||
return self._name
|
||||
|
||||
|
||||
def setIp(self, ip):
|
||||
'''
|
||||
In our case, there is no OS manager associated with this, so this method
|
||||
will never get called, but we put here as sample.
|
||||
|
||||
|
||||
Whenever an os manager actor notifies the broker the state of the service
|
||||
(mainly machines), the implementation of that os manager can (an probably will)
|
||||
need to notify the IP of the deployed service. Remember that UDS treats with
|
||||
@ -126,38 +126,38 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
'''
|
||||
logger.debug('Setting IP to %s' % ip)
|
||||
self._ip = ip
|
||||
|
||||
|
||||
def getUniqueId(self):
|
||||
'''
|
||||
Return and unique identifier for this service.
|
||||
In our case, we will generate a mac name, that can be also as sample
|
||||
of 'mac' generator use, and probably will get used something like this
|
||||
at some services.
|
||||
|
||||
|
||||
The get method of a mac generator takes one param, that is the mac range
|
||||
to use to get an unused mac.
|
||||
'''
|
||||
if self._mac == '':
|
||||
self._mac = self.macGenerator().get( self.service().getMacRange() )
|
||||
self._mac = self.macGenerator().get(self.service().getMacRange())
|
||||
return self._mac
|
||||
|
||||
|
||||
def getIp(self):
|
||||
'''
|
||||
We need to implement this method, so we can return the IP for transports
|
||||
use. If no IP is known for this service, this must return None
|
||||
|
||||
|
||||
If our sample do not returns an IP, IP transport will never work with
|
||||
this service. Remember in real cases to return a valid IP address if
|
||||
the service is accesible and you alredy know that (for example, because
|
||||
the IP has been assigend via setIp by an os manager) or because
|
||||
you get it for some other method.
|
||||
|
||||
you get it for some other method.
|
||||
|
||||
Storage returns None if key is not stored.
|
||||
|
||||
|
||||
:note: Keeping the IP address is responsibility of the User Deployment.
|
||||
Every time the core needs to provide the service to the user, or
|
||||
show the IP to the administrator, this method will get called
|
||||
|
||||
|
||||
'''
|
||||
return self._ip
|
||||
|
||||
@ -168,16 +168,16 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
'''
|
||||
if self.cache().get('ready') == '1':
|
||||
return State.FINISHED
|
||||
|
||||
|
||||
state = self.service().getMachineState(self._vmid)
|
||||
|
||||
|
||||
if state == 'unknown':
|
||||
return self.__error('Machine is not available anymore')
|
||||
|
||||
|
||||
if state not in ('up', 'powering_up', 'restoring_state'):
|
||||
self._queue = [ opStart, opFinish ]
|
||||
self._queue = [opStart, opFinish]
|
||||
return self.__executeQueue()
|
||||
|
||||
|
||||
self.cache().put('ready', '1')
|
||||
return State.FINISHED
|
||||
|
||||
@ -186,7 +186,7 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
logger.debug('Checking if cache 2 for {0}'.format(self._name))
|
||||
if self.__getCurrentOp() == opWait:
|
||||
logger.debug('Machine is ready. Moving to level 2')
|
||||
self.__popCurrentOp() # Remove current state
|
||||
self.__popCurrentOp() # Remove current state
|
||||
return self.__executeQueue()
|
||||
# Do not need to go to level 2 (opWait is in fact "waiting for moving machine to cache level 2)
|
||||
return State.FINISHED
|
||||
@ -198,7 +198,7 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
logger.debug('Deploying for user')
|
||||
self.__initQueueForDeploy(False)
|
||||
return self.__executeQueue()
|
||||
|
||||
|
||||
def deployForCache(self, cacheLevel):
|
||||
'''
|
||||
Deploys an service instance for cache
|
||||
@ -206,21 +206,21 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
self.__initQueueForDeploy(cacheLevel == self.L2_CACHE)
|
||||
return self.__executeQueue()
|
||||
|
||||
def __initQueueForDeploy(self, forLevel2 = False):
|
||||
|
||||
def __initQueueForDeploy(self, forLevel2=False):
|
||||
|
||||
if forLevel2 is False:
|
||||
self._queue = [opCreate, opChangeMac, opStart, opFinish]
|
||||
else:
|
||||
self._queue = [opCreate, opChangeMac, opStart, opWait, opSuspend, opFinish]
|
||||
|
||||
|
||||
def __checkMachineState(self, chkState):
|
||||
logger.debug('Checking that state of machine {0} is {1}'.format(self._vmid, chkState))
|
||||
state = self.service().getMachineState(self._vmid)
|
||||
|
||||
|
||||
# If we want to check an state and machine does not exists (except in case that we whant to check this)
|
||||
if state == 'unknown' and chkState != 'unknown':
|
||||
return self.__error('Machine not found')
|
||||
|
||||
|
||||
ret = State.RUNNING
|
||||
if type(chkState) is list:
|
||||
for cks in chkState:
|
||||
@ -230,46 +230,46 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
else:
|
||||
if state == chkState:
|
||||
ret = State.FINISHED
|
||||
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def __getCurrentOp(self):
|
||||
if len(self._queue) == 0:
|
||||
return opFinish
|
||||
|
||||
|
||||
return self._queue[0]
|
||||
|
||||
|
||||
def __popCurrentOp(self):
|
||||
if len(self._queue) == 0:
|
||||
return opFinish
|
||||
|
||||
|
||||
res = self._queue.pop(0)
|
||||
return res
|
||||
|
||||
|
||||
def __pushFrontOp(self, op):
|
||||
self._queue.insert(0, op)
|
||||
|
||||
|
||||
def __pushBackOp(self, op):
|
||||
self._queue.append(op)
|
||||
|
||||
|
||||
def __error(self, reason):
|
||||
'''
|
||||
Internal method to set object as error state
|
||||
|
||||
|
||||
Returns:
|
||||
State.ERROR, so we can do "return self.__error(reason)"
|
||||
'''
|
||||
logger.debug('Setting error state, reason: {0}'.format(reason))
|
||||
self.doLog(log.ERROR, reason)
|
||||
|
||||
if self._vmid != '': # Powers off
|
||||
|
||||
if self._vmid != '': # Powers off
|
||||
try:
|
||||
state = self.service().getMachineState(self._vmid)
|
||||
if state in ('up', 'suspended'):
|
||||
self.service().stopMachine(self._vmid)
|
||||
except:
|
||||
logger.debug('Can\t set machine state to stopped')
|
||||
|
||||
|
||||
self._queue = [opError]
|
||||
self._reason = str(reason)
|
||||
return State.ERROR
|
||||
@ -277,83 +277,84 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
def __executeQueue(self):
|
||||
self.__debug('executeQueue')
|
||||
op = self.__getCurrentOp()
|
||||
|
||||
|
||||
if op == opError:
|
||||
return State.ERROR
|
||||
|
||||
|
||||
if op == opFinish:
|
||||
return State.FINISHED
|
||||
|
||||
fncs = { opCreate: self.__create,
|
||||
opRetry: self.__retry,
|
||||
opStart: self.__startMachine,
|
||||
opStop: self.__stopMachine,
|
||||
opSuspend: self.__suspendMachine,
|
||||
opWait: self.__wait,
|
||||
opRemove: self.__remove,
|
||||
opChangeMac: self.__changeMac
|
||||
}
|
||||
|
||||
|
||||
fncs = {
|
||||
opCreate: self.__create,
|
||||
opRetry: self.__retry,
|
||||
opStart: self.__startMachine,
|
||||
opStop: self.__stopMachine,
|
||||
opSuspend: self.__suspendMachine,
|
||||
opWait: self.__wait,
|
||||
opRemove: self.__remove,
|
||||
opChangeMac: self.__changeMac
|
||||
}
|
||||
|
||||
try:
|
||||
execFnc = fncs.get(op, None)
|
||||
|
||||
|
||||
if execFnc is None:
|
||||
return self.__error('Unknown operation found at execution queue ({0})'.format(op))
|
||||
|
||||
return self.__error('Unknown operation found at execution queue ({0})'.format(op))
|
||||
|
||||
execFnc()
|
||||
|
||||
|
||||
return State.RUNNING
|
||||
except Exception as e:
|
||||
return self.__error(e)
|
||||
|
||||
|
||||
# Queue execution methods
|
||||
def __retry(self):
|
||||
'''
|
||||
Used to retry an operation
|
||||
In fact, this will not be never invoked, unless we push it twice, because
|
||||
checkState method will "pop" first item when a check operation returns State.FINISHED
|
||||
|
||||
|
||||
At executeQueue this return value will be ignored, and it will only be used at checkState
|
||||
'''
|
||||
return State.FINISHED
|
||||
|
||||
|
||||
def __wait(self):
|
||||
'''
|
||||
Executes opWait, it simply waits something "external" to end
|
||||
'''
|
||||
return State.RUNNING
|
||||
|
||||
|
||||
def __create(self):
|
||||
'''
|
||||
Deploys a machine from template for user/cache
|
||||
'''
|
||||
templateId = self.publication().getTemplateId()
|
||||
templateId = self.publication().getTemplateId()
|
||||
name = self.getName()
|
||||
if name == NO_MORE_NAMES:
|
||||
raise Exception('No more names available for this service. (Increase digits for this service to fix)')
|
||||
|
||||
name = self.service().sanitizeVmName(name) # oVirt don't let us to create machines with more than 15 chars!!!
|
||||
|
||||
name = self.service().sanitizeVmName(name) # oVirt don't let us to create machines with more than 15 chars!!!
|
||||
comments = 'UDS Linked clone'
|
||||
|
||||
|
||||
self._vmid = self.service().deployFromTemplate(name, comments, templateId)
|
||||
if self._vmid is None:
|
||||
raise Exception('Can\'t create machine')
|
||||
|
||||
|
||||
def __remove(self):
|
||||
'''
|
||||
Removes a machine from system
|
||||
'''
|
||||
state = self.service().getMachineState(self._vmid)
|
||||
|
||||
|
||||
if state == 'unknown':
|
||||
raise Exception('Machine not found')
|
||||
|
||||
|
||||
if state != 'down':
|
||||
self.__pushFrontOp(opStop)
|
||||
self.__executeQueue()
|
||||
else:
|
||||
self.service().removeMachine(self._vmid)
|
||||
|
||||
|
||||
def __startMachine(self):
|
||||
'''
|
||||
Powers on the machine
|
||||
@ -362,15 +363,15 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
|
||||
if state == 'unknown':
|
||||
raise Exception('Machine not found')
|
||||
|
||||
if state == 'up': # Already started, return
|
||||
|
||||
if state == 'up': # Already started, return
|
||||
return
|
||||
|
||||
|
||||
if state != 'down' and state != 'suspended':
|
||||
self.__pushFrontOp(opRetry) # Will call "check Retry", that will finish inmediatly and again call this one
|
||||
self.__pushFrontOp(opRetry) # Will call "check Retry", that will finish inmediatly and again call this one
|
||||
else:
|
||||
self.service().startMachine(self._vmid)
|
||||
|
||||
|
||||
def __stopMachine(self):
|
||||
'''
|
||||
Powers off the machine
|
||||
@ -379,15 +380,15 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
|
||||
if state == 'unknown':
|
||||
raise Exception('Machine not found')
|
||||
|
||||
if state == 'down': # Already stoped, return
|
||||
|
||||
if state == 'down': # Already stoped, return
|
||||
return
|
||||
|
||||
|
||||
if state != 'up' and state != 'suspended':
|
||||
self.__pushFrontOp(opRetry) # Will call "check Retry", that will finish inmediatly and again call this one
|
||||
self.__pushFrontOp(opRetry) # Will call "check Retry", that will finish inmediatly and again call this one
|
||||
else:
|
||||
self.service().stopMachine(self._vmid)
|
||||
|
||||
|
||||
def __suspendMachine(self):
|
||||
'''
|
||||
Suspends the machine
|
||||
@ -396,98 +397,99 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
|
||||
if state == 'unknown':
|
||||
raise Exception('Machine not found')
|
||||
|
||||
if state == 'suspended': # Already suspended, return
|
||||
|
||||
if state == 'suspended': # Already suspended, return
|
||||
return
|
||||
|
||||
|
||||
if state != 'up':
|
||||
self.__pushFrontOp(opRetry) # Remember here, the return State.FINISH will make this retry be "poped" right ar return
|
||||
self.__pushFrontOp(opRetry) # Remember here, the return State.FINISH will make this retry be "poped" right ar return
|
||||
else:
|
||||
self.service().suspendMachine(self._vmid)
|
||||
|
||||
|
||||
def __changeMac(self):
|
||||
'''
|
||||
Changes the mac of the first nic
|
||||
'''
|
||||
self.service().updateMachineMac(self._vmid, self.getUniqueId())
|
||||
|
||||
|
||||
# Check methods
|
||||
def __checkCreate(self):
|
||||
'''
|
||||
Checks the state of a deploy for an user or cache
|
||||
'''
|
||||
return self.__checkMachineState('down')
|
||||
|
||||
|
||||
def __checkStart(self):
|
||||
'''
|
||||
Checks if machine has started
|
||||
'''
|
||||
return self.__checkMachineState('up')
|
||||
|
||||
|
||||
def __checkStop(self):
|
||||
'''
|
||||
Checks if machine has stoped
|
||||
'''
|
||||
return self.__checkMachineState('down')
|
||||
|
||||
|
||||
def __checkSuspend(self):
|
||||
'''
|
||||
Check if the machine has suspended
|
||||
'''
|
||||
return self.__checkMachineState('suspended')
|
||||
|
||||
|
||||
def __checkRemoved(self):
|
||||
'''
|
||||
Checks if a machine has been removed
|
||||
'''
|
||||
return self.__checkMachineState('unknown')
|
||||
|
||||
|
||||
def __checkMac(self):
|
||||
'''
|
||||
Checks if change mac operation has finished.
|
||||
|
||||
|
||||
Changing nic configuration es 1-step operation, so when we check it here, it is already done
|
||||
'''
|
||||
return State.FINISHED
|
||||
|
||||
|
||||
def checkState(self):
|
||||
'''
|
||||
Check what operation is going on, and acts acordly to it
|
||||
'''
|
||||
self.__debug('checkState')
|
||||
op = self.__getCurrentOp()
|
||||
|
||||
|
||||
if op == opError:
|
||||
return State.ERROR
|
||||
|
||||
if op == opFinish:
|
||||
|
||||
if op == opFinish:
|
||||
return State.FINISHED
|
||||
|
||||
fncs = { opCreate: self.__checkCreate,
|
||||
opRetry: self.__retry,
|
||||
opWait: self.__wait,
|
||||
opStart: self.__checkStart,
|
||||
opStop: self.__checkStop,
|
||||
opSuspend: self.__checkSuspend,
|
||||
opRemove: self.__checkRemoved,
|
||||
opChangeMac: self.__checkMac
|
||||
}
|
||||
|
||||
|
||||
fncs = {
|
||||
opCreate: self.__checkCreate,
|
||||
opRetry: self.__retry,
|
||||
opWait: self.__wait,
|
||||
opStart: self.__checkStart,
|
||||
opStop: self.__checkStop,
|
||||
opSuspend: self.__checkSuspend,
|
||||
opRemove: self.__checkRemoved,
|
||||
opChangeMac: self.__checkMac
|
||||
}
|
||||
|
||||
try:
|
||||
chkFnc = fncs.get(op, None)
|
||||
|
||||
|
||||
if chkFnc is None:
|
||||
return self.__error('Unknown operation found at check queue ({0})'.format(op))
|
||||
|
||||
|
||||
state = chkFnc()
|
||||
if state == State.FINISHED:
|
||||
self.__popCurrentOp() # Remove runing op
|
||||
self.__popCurrentOp() # Remove runing op
|
||||
return self.__executeQueue()
|
||||
|
||||
|
||||
return state
|
||||
except Exception as e:
|
||||
return self.__error(e)
|
||||
|
||||
|
||||
def finish(self):
|
||||
'''
|
||||
Invoked when the core notices that the deployment of a service has finished.
|
||||
@ -495,7 +497,7 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
'''
|
||||
self.__debug('finish')
|
||||
pass
|
||||
|
||||
|
||||
def assignToUser(self, user):
|
||||
'''
|
||||
This method is invoked whenever a cache item gets assigned to an user.
|
||||
@ -503,21 +505,21 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
are required so the service puts at a correct state for using by a service.
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
def moveToCache(self, newLevel):
|
||||
'''
|
||||
Moves machines between cache levels
|
||||
'''
|
||||
if opRemove in self._queue:
|
||||
return State.RUNNING
|
||||
|
||||
|
||||
if newLevel == self.L1_CACHE:
|
||||
self._queue = [opStart, opFinish]
|
||||
else:
|
||||
self._queue = [opStart, opSuspend, opFinish]
|
||||
|
||||
|
||||
return self.__executeQueue()
|
||||
|
||||
|
||||
def userLoggedIn(self, user):
|
||||
'''
|
||||
This method must be available so os managers can invoke it whenever
|
||||
@ -527,42 +529,42 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
'''
|
||||
# We store the value at storage, but never get used, just an example
|
||||
pass
|
||||
|
||||
|
||||
def userLoggedOut(self, user):
|
||||
'''
|
||||
This method must be available so os managers can invoke it whenever
|
||||
an user get logged out if a service.
|
||||
|
||||
|
||||
The user provided is just an string, that is provided by actor.
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
def reasonOfError(self):
|
||||
'''
|
||||
Returns the reason of the error.
|
||||
|
||||
|
||||
Remember that the class is responsible of returning this whenever asked
|
||||
for it, and it will be asked everytime it's needed to be shown to the
|
||||
user (when the administation asks for it).
|
||||
'''
|
||||
return self._reason
|
||||
|
||||
|
||||
def destroy(self):
|
||||
'''
|
||||
Invoked for destroying a deployed service
|
||||
'''
|
||||
'''
|
||||
self.__debug('destroy')
|
||||
# If executing something, wait until finished to remove it
|
||||
# We simply replace the execution queue
|
||||
op = self.__getCurrentOp()
|
||||
|
||||
|
||||
if op == opError:
|
||||
return self.__error('Machine is already in error state!')
|
||||
|
||||
|
||||
if op == opFinish or op == opWait:
|
||||
self._queue = [opStop, opRemove, opFinish]
|
||||
return self.__executeQueue()
|
||||
|
||||
|
||||
self._queue = [op, opStop, opRemove, opFinish]
|
||||
# Do not execute anything.here, just continue normally
|
||||
return State.RUNNING
|
||||
@ -571,33 +573,32 @@ class OVirtLinkedDeployment(UserDeployment):
|
||||
'''
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
|
||||
|
||||
This can be invoked directly by an administration or by the clean up
|
||||
of the deployed service (indirectly).
|
||||
When administrator requests it, the cancel is "delayed" and not
|
||||
invoked directly.
|
||||
'''
|
||||
return self.destroy()
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def __op2str(op):
|
||||
return { opCreate: 'create',
|
||||
opStart: 'start',
|
||||
opStop: 'stop',
|
||||
opSuspend: 'suspend',
|
||||
opRemove: 'remove',
|
||||
opWait: 'wait',
|
||||
opError: 'error',
|
||||
opFinish: 'finish',
|
||||
opRetry: 'retry',
|
||||
opChangeMac: 'changing mac'
|
||||
}.get(op, '????')
|
||||
|
||||
return {
|
||||
opCreate: 'create',
|
||||
opStart: 'start',
|
||||
opStop: 'stop',
|
||||
opSuspend: 'suspend',
|
||||
opRemove: 'remove',
|
||||
opWait: 'wait',
|
||||
opError: 'error',
|
||||
opFinish: 'finish',
|
||||
opRetry: 'retry',
|
||||
opChangeMac: 'changing mac'
|
||||
}.get(op, '????')
|
||||
|
||||
def __debug(self, txt):
|
||||
logger.debug('_name {0}: {1}'.format(txt, self._name))
|
||||
logger.debug('_ip {0}: {1}'.format(txt, self._ip))
|
||||
logger.debug('_mac {0}: {1}'.format(txt, self._mac))
|
||||
logger.debug('_vmid {0}: {1}'.format(txt, self._vmid))
|
||||
logger.debug('Queue at {0}: {1}'.format(txt,[OVirtLinkedDeployment.__op2str(op) for op in self._queue ]))
|
||||
|
||||
logger.debug('Queue at {0}: {1}'.format(txt, [OVirtLinkedDeployment.__op2str(op) for op in self._queue]))
|
||||
|
@ -4,27 +4,27 @@
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# * 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
|
||||
# * 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
|
||||
# * 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
|
||||
# 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
|
||||
# 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.
|
||||
|
||||
'''
|
||||
@ -44,96 +44,102 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
from Helpers import oVirtHelpers
|
||||
|
||||
|
||||
class OVirtLinkedService(Service):
|
||||
'''
|
||||
oVirt Linked clones service. This is based on creating a template from selected vm, and then use it to
|
||||
|
||||
|
||||
oVirt Linked clones service. This is based on creating a template from selected vm, and then use it to
|
||||
|
||||
|
||||
'''
|
||||
#: Name to show the administrator. This string will be translated BEFORE
|
||||
#: sending it to administration interface, so don't forget to
|
||||
#: mark it as _ (using ugettext_noop)
|
||||
typeName = _('oVirt Linked Clone (Experimental)')
|
||||
#: Type used internally to identify this provider
|
||||
# : Name to show the administrator. This string will be translated BEFORE
|
||||
# : sending it to administration interface, so don't forget to
|
||||
# : mark it as _ (using ugettext_noop)
|
||||
typeName = _('oVirt Linked Clone (Experimental)')
|
||||
# : Type used internally to identify this provider
|
||||
typeType = 'oVirtLinkedService'
|
||||
#: Description shown at administration interface for this provider
|
||||
# : Description shown at administration interface for this provider
|
||||
typeDescription = _('oVirt Services based on templates and COW (experimental)')
|
||||
#: 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 _ (using ugettext_noop)
|
||||
# : 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 _ (using ugettext_noop)
|
||||
iconFile = 'service.png'
|
||||
|
||||
|
||||
# Functional related data
|
||||
|
||||
#: If the service provides more than 1 "deployed user" (-1 = no limit,
|
||||
#: 0 = ???? (do not use it!!!), N = max number to deploy
|
||||
|
||||
# : If the service provides more than 1 "deployed user" (-1 = no limit,
|
||||
# : 0 = ???? (do not use it!!!), N = max number to deploy
|
||||
maxDeployed = -1
|
||||
#: If we need to generate "cache" for this service, so users can access the
|
||||
#: provided services faster. Is usesCache is True, you will need also
|
||||
#: set publicationType, do take care about that!
|
||||
# : If we need to generate "cache" for this service, so users can access the
|
||||
# : provided services faster. Is usesCache is True, you will need also
|
||||
# : set publicationType, do take care about that!
|
||||
usesCache = True
|
||||
#: Tooltip shown to user when this item is pointed at admin interface, none
|
||||
#: because we don't use it
|
||||
# : 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')
|
||||
#: 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')
|
||||
|
||||
#: If the service needs a s.o. manager (managers are related to agents
|
||||
#: provided by services itselfs, i.e. virtual machines with actors)
|
||||
# : 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')
|
||||
|
||||
# : If the service needs a s.o. manager (managers are related to agents
|
||||
# : provided by services itselfs, i.e. virtual machines with actors)
|
||||
needsManager = True
|
||||
#: If true, the system can't do an automatic assignation of a deployed user
|
||||
#: service from this service
|
||||
mustAssignManually = False
|
||||
# : If true, the system can't do an automatic assignation of a deployed user
|
||||
# : service from this service
|
||||
mustAssignManually = False
|
||||
|
||||
#: Types of publications (preparated data for deploys)
|
||||
#: In our case, we do no need a publication, so this is None
|
||||
# : Types of publications (preparated data for deploys)
|
||||
# : In our case, we do no need a publication, so this is None
|
||||
publicationType = OVirtPublication
|
||||
#: Types of deploys (services in cache and/or assigned to users)
|
||||
# : Types of deploys (services in cache and/or assigned to users)
|
||||
deployedType = OVirtLinkedDeployment
|
||||
|
||||
# Now the form part
|
||||
machine = gui.ChoiceField(label=_("Base Machine"), order = 1, tooltip = _('Service base machine'), required = True)
|
||||
cluster = gui.ChoiceField(label=_("Cluster"), order = 2,
|
||||
fills = {
|
||||
'callbackName' : 'ovFillResourcesFromCluster',
|
||||
'function' : oVirtHelpers.getResources,
|
||||
'parameters' : ['cluster', 'ov', 'ev']
|
||||
},
|
||||
tooltip = _("Cluster to contain services"), required = True
|
||||
)
|
||||
|
||||
datastore = gui.ChoiceField(label = _("Datastore Domain"), rdonly = False, order = 3,
|
||||
tooltip = _('Datastore domain where to publish and put incrementals'), required = True)
|
||||
|
||||
memory = gui.NumericField(label = _("Memory (Mb)"), length = 4, defvalue = 512, rdonly = False, order = 4,
|
||||
tooltip = _('Memory assigned to machines'), required = True)
|
||||
|
||||
memoryGuaranteed = gui.NumericField(label = _("Memory Guaranteed (Mb)"), length = 4, defvalue = 256, rdonly = False, order = 5,
|
||||
tooltip = _('Physical memory guaranteed to machines'), required = True)
|
||||
|
||||
baseName = gui.TextField(label = _('Machine Names'), rdonly = False, order = 6, tooltip = ('Base name for clones from this machine'), required = True)
|
||||
lenName = gui.NumericField(length = 1, label = _('Name Length'), defvalue = 5, order = 7,
|
||||
tooltip = _('Length of numeric part for the names of this machines (betwen 3 and 6'), required = True)
|
||||
|
||||
display = gui.ChoiceField(label = _('Display'), rdonly = False, order = 8,
|
||||
tooltip = _('Display type (only for administration pourposses)'),
|
||||
values = [ gui.choiceItem('spice', 'Spice'),
|
||||
gui.choiceItem('vnc', 'Vnc')
|
||||
# Now the form part
|
||||
machine = gui.ChoiceField(label=_("Base Machine"), order=1, tooltip=_('Service base machine'), required=True)
|
||||
cluster = gui.ChoiceField(label=_("Cluster"), order=2,
|
||||
fills={
|
||||
'callbackName': 'ovFillResourcesFromCluster',
|
||||
'function': oVirtHelpers.getResources,
|
||||
'parameters': ['cluster', 'ov', 'ev']
|
||||
},
|
||||
tooltip=_("Cluster to contain services"), required=True
|
||||
)
|
||||
|
||||
datastore = gui.ChoiceField(label=_("Datastore Domain"), rdonly=False, order=3,
|
||||
tooltip=_('Datastore domain where to publish and put incrementals'), required=True)
|
||||
|
||||
minSpaceGB = gui.NumericField(length=3, label=_('Reserved Space'), defvalue='32', order=4, tooltip=_('Minimal free space in GB'), required=True)
|
||||
|
||||
|
||||
memory = gui.NumericField(label=_("Memory (Mb)"), length=4, defvalue=512, rdonly=False, order=5,
|
||||
tooltip=_('Memory assigned to machines'), required=True)
|
||||
|
||||
memoryGuaranteed = gui.NumericField(label=_("Memory Guaranteed (Mb)"), length=4, defvalue=256, rdonly=False, order=6,
|
||||
tooltip=_('Physical memory guaranteed to machines'), required=True)
|
||||
|
||||
baseName = gui.TextField(label=_('Machine Names'), rdonly=False, order=6, tooltip=('Base name for clones from this machine'), required=True)
|
||||
|
||||
lenName = gui.NumericField(length=1, label=_('Name Length'), defvalue=5, order=7,
|
||||
tooltip=_('Length of numeric part for the names of this machines (betwen 3 and 6'), required=True)
|
||||
|
||||
display = gui.ChoiceField(label=_('Display'), rdonly=False, order=8,
|
||||
tooltip=_('Display type (only for administration pourposses)'),
|
||||
values=[
|
||||
gui.choiceItem('spice', 'Spice'),
|
||||
gui.choiceItem('vnc', 'Vnc')
|
||||
],
|
||||
defvalue = '1' # Default value is the ID of the choicefield
|
||||
)
|
||||
|
||||
defvalue='1' # Default value is the ID of the choicefield
|
||||
)
|
||||
|
||||
ov = gui.HiddenField()
|
||||
ev = gui.HiddenField() # We need to keep the env so we can instantiate the Provider
|
||||
|
||||
ev = gui.HiddenField() # We need to keep the env so we can instantiate the Provider
|
||||
|
||||
def initialize(self, values):
|
||||
'''
|
||||
We check here form values to see if they are valid.
|
||||
|
||||
|
||||
Note that we check them throught FROM variables, that already has been
|
||||
initialized by __init__ method of base class, before invoking this.
|
||||
'''
|
||||
@ -152,67 +158,79 @@ class OVirtLinkedService(Service):
|
||||
'''
|
||||
Loads required values inside
|
||||
'''
|
||||
|
||||
# Here we have to use "default values", cause values aren't used at form initialization
|
||||
|
||||
# Here we have to use "default values", cause values aren't used at form initialization
|
||||
# This is that value is always '', so if we want to change something, we have to do it
|
||||
# at defValue
|
||||
self.ov.defValue = self.parent().serialize()
|
||||
self.ev.defValue = self.parent().env().key()
|
||||
|
||||
|
||||
machines = self.parent().getMachines()
|
||||
vals = []
|
||||
for m in machines:
|
||||
vals.append( gui.choiceItem( m['id'], m['name'] ))
|
||||
|
||||
vals.append(gui.choiceItem(m['id'], m['name']))
|
||||
|
||||
# This is not the same case, values is not the "value" of the field, but
|
||||
# the list of values shown because this is a "ChoiceField"
|
||||
# the list of values shown because this is a "ChoiceField"
|
||||
self.machine.setValues(vals)
|
||||
|
||||
|
||||
clusters = self.parent().getClusters()
|
||||
vals = []
|
||||
for c in clusters:
|
||||
vals.append( gui.choiceItem(c['id'], c['name'] ) )
|
||||
vals.append(gui.choiceItem(c['id'], c['name']))
|
||||
self.cluster.setValues(vals)
|
||||
|
||||
|
||||
def datastoreHasSpace(self):
|
||||
# Get storages for that datacenter
|
||||
info = self.parent().getStorageInfo(self.datastore.value)
|
||||
availableGB = info['available'] / (1024 * 1024 * 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))
|
||||
|
||||
def sanitizeVmName(self, name):
|
||||
'''
|
||||
Ovirt only allows machine names with [a-zA-Z0-9_-]
|
||||
'''
|
||||
import re
|
||||
return re.sub("[^a-zA-Z0-9_-]", "_", name)
|
||||
|
||||
|
||||
def makeTemplate(self, name, comments):
|
||||
'''
|
||||
Invokes makeTemplate from parent provider, completing params
|
||||
|
||||
|
||||
Args:
|
||||
name: Name to assign to template (must be previously "sanitized"
|
||||
comments: Comments (UTF-8) to add to template
|
||||
|
||||
|
||||
Returns:
|
||||
template Id of the template created
|
||||
|
||||
|
||||
Raises an exception if operation fails.
|
||||
'''
|
||||
|
||||
# Checks datastore size
|
||||
# Get storages for that datacenter
|
||||
|
||||
self.datastoreHasSpace()
|
||||
return self.parent().makeTemplate(name, comments, self.machine.value, self.cluster.value, self.datastore.value, self.display.value)
|
||||
|
||||
|
||||
def getTemplateState(self, templateId):
|
||||
'''
|
||||
Invokes getTemplateState from parent provider
|
||||
|
||||
|
||||
Args:
|
||||
templateId: templateId to remove
|
||||
|
||||
|
||||
Returns nothing
|
||||
|
||||
|
||||
Raises an exception if operation fails.
|
||||
'''
|
||||
return self.parent().getTemplateState(templateId)
|
||||
|
||||
|
||||
def deployFromTemplate(self, name, comments, templateId):
|
||||
'''
|
||||
Deploys a virtual machine on selected cluster from selected template
|
||||
|
||||
|
||||
Args:
|
||||
name: Name (sanitized) of the machine
|
||||
comments: Comments for machine
|
||||
@ -220,12 +238,13 @@ class OVirtLinkedService(Service):
|
||||
displayType: 'vnc' or 'spice'. Display to use ad oVirt admin interface
|
||||
memoryMB: Memory requested for machine, in MB
|
||||
guaranteedMB: Minimum memory guaranteed for this machine
|
||||
|
||||
|
||||
Returns:
|
||||
Id of the machine being created form template
|
||||
Id of the machine being created form template
|
||||
'''
|
||||
logger.debug('Deploying from template {0} machine {1}'.format(templateId, name))
|
||||
return self.parent().deployFromTemplate(name, comments, templateId, self.cluster.value,
|
||||
self.datastoreHasSpace()
|
||||
return self.parent().deployFromTemplate(name, comments, templateId, self.cluster.value,
|
||||
self.display.value, int(self.memory.value), int(self.memoryGuaranteed.value))
|
||||
|
||||
def removeTemplate(self, templateId):
|
||||
@ -233,34 +252,34 @@ class OVirtLinkedService(Service):
|
||||
invokes removeTemplate from parent provider
|
||||
'''
|
||||
return self.parent().removeTemplate(templateId)
|
||||
|
||||
|
||||
def getMachineState(self, machineId):
|
||||
'''
|
||||
Invokes getMachineState from parent provider
|
||||
(returns if machine is "active" or "inactive"
|
||||
|
||||
|
||||
Args:
|
||||
machineId: If of the machine to get state
|
||||
|
||||
|
||||
Returns:
|
||||
one of this values:
|
||||
unassigned, down, up, powering_up, powered_down,
|
||||
paused, migrating_from, migrating_to, unknown, not_responding,
|
||||
wait_for_launch, reboot_in_progress, saving_state, restoring_state,
|
||||
unassigned, down, up, powering_up, powered_down,
|
||||
paused, migrating_from, migrating_to, unknown, not_responding,
|
||||
wait_for_launch, reboot_in_progress, saving_state, restoring_state,
|
||||
suspended, image_illegal, image_locked or powering_down
|
||||
Also can return'unknown' if Machine is not known
|
||||
'''
|
||||
return self.parent().getMachineState(machineId)
|
||||
|
||||
|
||||
def startMachine(self, machineId):
|
||||
'''
|
||||
Tries to start a machine. No check is done, it is simply requested to oVirt.
|
||||
|
||||
|
||||
This start also "resume" suspended/paused machines
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine
|
||||
|
||||
|
||||
Returns:
|
||||
'''
|
||||
return self.parent().startMachine(machineId)
|
||||
@ -268,36 +287,36 @@ class OVirtLinkedService(Service):
|
||||
def stopMachine(self, machineId):
|
||||
'''
|
||||
Tries to start a machine. No check is done, it is simply requested to oVirt
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine
|
||||
|
||||
|
||||
Returns:
|
||||
'''
|
||||
return self.parent().stopMachine(machineId)
|
||||
|
||||
|
||||
def suspendMachine(self, machineId):
|
||||
'''
|
||||
Tries to start a machine. No check is done, it is simply requested to oVirt
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine
|
||||
|
||||
|
||||
Returns:
|
||||
'''
|
||||
return self.parent().suspendMachine(machineId)
|
||||
|
||||
|
||||
def removeMachine(self, machineId):
|
||||
'''
|
||||
Tries to delete a machine. No check is done, it is simply requested to oVirt
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine
|
||||
|
||||
|
||||
Returns:
|
||||
'''
|
||||
return self.parent().removeMachine(machineId)
|
||||
|
||||
|
||||
def updateMachineMac(self, machineId, macAddres):
|
||||
'''
|
||||
Changes the mac address of first nic of the machine to the one specified
|
||||
@ -309,22 +328,22 @@ class OVirtLinkedService(Service):
|
||||
Returns de selected mac range
|
||||
'''
|
||||
return self.parent().getMacRange()
|
||||
|
||||
|
||||
def getBaseName(self):
|
||||
'''
|
||||
Returns the base name
|
||||
'''
|
||||
return self.baseName.value
|
||||
|
||||
|
||||
def getLenName(self):
|
||||
'''
|
||||
Returns the length of numbers part
|
||||
'''
|
||||
return int(self.lenName.value)
|
||||
|
||||
|
||||
def getDisplay(self):
|
||||
'''
|
||||
Returns the selected display type (for created machines, for administration
|
||||
Returns the selected display type (for created machines, for administration
|
||||
'''
|
||||
return self.display.value
|
||||
|
||||
|
||||
|
@ -4,27 +4,27 @@
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# * 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
|
||||
# * 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
|
||||
# * 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
|
||||
# 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
|
||||
# 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.
|
||||
|
||||
'''
|
||||
@ -48,38 +48,39 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
CACHE_TIME_FOR_SERVER = 1800
|
||||
|
||||
|
||||
class Provider(ServiceProvider):
|
||||
'''
|
||||
This class represents the sample services provider
|
||||
|
||||
|
||||
In this class we provide:
|
||||
* The Provider functionality
|
||||
* The basic configuration parameters for the provider
|
||||
* The form fields needed by administrators to configure this provider
|
||||
|
||||
|
||||
:note: At class level, the translation must be simply marked as so
|
||||
using ugettext_noop. This is so cause we will translate the string when
|
||||
sent to the administration client.
|
||||
|
||||
|
||||
For this class to get visible at administration client as a provider type,
|
||||
we MUST register it at package __init__.
|
||||
|
||||
|
||||
'''
|
||||
#: What kind of services we offer, this are classes inherited from Service
|
||||
# : What kind of services we offer, this are classes inherited from Service
|
||||
offers = [OVirtLinkedService]
|
||||
#: Name to show the administrator. This string will be translated BEFORE
|
||||
#: sending it to administration interface, so don't forget to
|
||||
#: mark it as _ (using ugettext_noop)
|
||||
typeName = _('oVirt Platform Provider')
|
||||
#: Type used internally to identify this provider
|
||||
# : Name to show the administrator. This string will be translated BEFORE
|
||||
# : sending it to administration interface, so don't forget to
|
||||
# : mark it as _ (using ugettext_noop)
|
||||
typeName = _('oVirt Platform Provider')
|
||||
# : Type used internally to identify this provider
|
||||
typeType = 'oVirtPlatform'
|
||||
#: Description shown at administration interface for this provider
|
||||
# : Description shown at administration interface for this provider
|
||||
typeDescription = _('oVirt platform 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 _ (using ugettext_noop)
|
||||
# : 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 _ (using ugettext_noop)
|
||||
iconFile = 'provider.png'
|
||||
|
||||
|
||||
# now comes the form fields
|
||||
# There is always two fields that are requested to the admin, that are:
|
||||
# Service Name, that is a name that the admin uses to name this provider
|
||||
@ -89,15 +90,13 @@ class Provider(ServiceProvider):
|
||||
# but used for sample purposes
|
||||
# If we don't indicate an order, the output order of fields will be
|
||||
# "random"
|
||||
host = gui.TextField(length=64, label = _('Host'), order = 1, tooltip = _('oVirt Server IP or Hostname'), required = True)
|
||||
username = gui.TextField(length=32, label = _('Username'), order = 3, tooltip = _('User with valid privileges on oVirt, (use "user@domain" form)'), required = True, defvalue='admin@internal')
|
||||
password = gui.PasswordField(lenth=32, label = _('Password'), order = 4, tooltip = _('Password of the user of oVirt'), required = True)
|
||||
timeout = gui.NumericField(length=3, label = _('Timeout'), defvalue = '10', order = 5, tooltip = _('Timeout in seconds of connection to VC'), required = True)
|
||||
macsRange = gui.TextField(length=36, label = _('Macs range'), defvalue = '52:54:00:00:00:00-52:54:00:FF:FF:FF', order = 6, rdonly = True,
|
||||
tooltip = _('Range of valids macs for created machines'), required = True)
|
||||
host = gui.TextField(length=64, label=_('Host'), order=1, tooltip=_('oVirt Server IP or Hostname'), required=True)
|
||||
username = gui.TextField(length=32, label=_('Username'), order=3, tooltip=_('User with valid privileges on oVirt, (use "user@domain" form)'), required=True, defvalue='admin@internal')
|
||||
password = gui.PasswordField(lenth=32, label=_('Password'), order=4, tooltip=_('Password of the user of oVirt'), required=True)
|
||||
timeout = gui.NumericField(length=3, label=_('Timeout'), defvalue='10', order=5, tooltip=_('Timeout in seconds of connection to VC'), required=True)
|
||||
macsRange = gui.TextField(length=36, label=_('Macs range'), defvalue='52:54:00:00:00:00-52:54:00:FF:FF:FF', order=6, rdonly=True,
|
||||
tooltip=_('Range of valids macs for created machines'), required=True)
|
||||
|
||||
|
||||
|
||||
# oVirt engine, right now, only permits a connection to one server and only one per instance
|
||||
# If we want to connect to more than one server, we need keep locked access to api, change api server, etc..
|
||||
# We have implemented an "exclusive access" client that will only connect to one server at a time (using locks)
|
||||
@ -107,30 +106,30 @@ class Provider(ServiceProvider):
|
||||
Returns the connection API object for oVirt (using ovirtsdk)
|
||||
'''
|
||||
if self._api is None:
|
||||
self._api = oVirtClient.Client(self.host.value, self.username.value, self.password.value, self.timeout.value, self.cache())
|
||||
self._api = oVirtClient.Client(self.host.value, self.username.value, self.password.value, self.timeout.value, self.cache())
|
||||
return self._api
|
||||
|
||||
# There is more fields type, but not here the best place to cover it
|
||||
def initialize(self, values = None):
|
||||
def initialize(self, values=None):
|
||||
'''
|
||||
We will use the "autosave" feature for form fields
|
||||
'''
|
||||
|
||||
# Just reset _api connection variable
|
||||
# Just reset _api connection variable
|
||||
self._api = None
|
||||
|
||||
def testConnection(self):
|
||||
'''
|
||||
Test that conection to oVirt server is fine
|
||||
|
||||
|
||||
Returns
|
||||
|
||||
|
||||
True if all went fine, false if id didn't
|
||||
'''
|
||||
|
||||
|
||||
return self.__getApi().test()
|
||||
|
||||
def getMachines(self, force = False):
|
||||
|
||||
def getMachines(self, force=False):
|
||||
'''
|
||||
Obtains the list of machines inside oVirt.
|
||||
Machines starting with UDS are filtered out
|
||||
@ -138,24 +137,24 @@ class Provider(ServiceProvider):
|
||||
Args:
|
||||
force: If true, force to update the cache, if false, tries to first
|
||||
get data from cache and, if valid, return this.
|
||||
|
||||
|
||||
Returns
|
||||
An array of dictionaries, containing:
|
||||
'name'
|
||||
'id'
|
||||
'cluster_id'
|
||||
'''
|
||||
|
||||
|
||||
return self.__getApi().getVms(force)
|
||||
|
||||
def getClusters(self, force = False):
|
||||
|
||||
def getClusters(self, force=False):
|
||||
'''
|
||||
Obtains the list of clusters inside oVirt.
|
||||
|
||||
|
||||
Args:
|
||||
force: If true, force to update the cache, if false, tries to first
|
||||
get data from cache and, if valid, return this.
|
||||
|
||||
|
||||
Returns
|
||||
Filters out clusters not attached to any datacenter
|
||||
An array of dictionaries, containing:
|
||||
@ -163,10 +162,10 @@ class Provider(ServiceProvider):
|
||||
'id'
|
||||
'datacenter_id'
|
||||
'''
|
||||
|
||||
|
||||
return self.__getApi().getClusters(force)
|
||||
|
||||
def getClusterInfo(self, clusterId, force = False):
|
||||
|
||||
def getClusterInfo(self, clusterId, force=False):
|
||||
'''
|
||||
Obtains the cluster info
|
||||
|
||||
@ -174,17 +173,17 @@ class Provider(ServiceProvider):
|
||||
datacenterId: Id of the cluster to get information about it
|
||||
force: If true, force to update the cache, if false, tries to first
|
||||
get data from cache and, if valid, return this.
|
||||
|
||||
|
||||
Returns
|
||||
|
||||
|
||||
A dictionary with following values
|
||||
'name'
|
||||
'id'
|
||||
'datacenter_id'
|
||||
'''
|
||||
return self.__getApi().getClusterInfo(clusterId, force)
|
||||
|
||||
def getDatacenterInfo(self, datacenterId, force = False):
|
||||
|
||||
def getDatacenterInfo(self, datacenterId, force=False):
|
||||
'''
|
||||
Obtains the datacenter info
|
||||
|
||||
@ -192,9 +191,9 @@ class Provider(ServiceProvider):
|
||||
datacenterId: Id of the datacenter to get information about it
|
||||
force: If true, force to update the cache, if false, tries to first
|
||||
get data from cache and, if valid, return this.
|
||||
|
||||
|
||||
Returns
|
||||
|
||||
|
||||
A dictionary with following values
|
||||
'name'
|
||||
'id'
|
||||
@ -208,21 +207,21 @@ class Provider(ServiceProvider):
|
||||
'available' -> Space available, in bytes
|
||||
'used' -> Space used, in bytes
|
||||
'active' -> True or False
|
||||
|
||||
|
||||
'''
|
||||
return self.__getApi().getDatacenterInfo(datacenterId, force)
|
||||
|
||||
def getStorageInfo(self, storageId, force = False):
|
||||
|
||||
def getStorageInfo(self, storageId, force=False):
|
||||
'''
|
||||
Obtains the datacenter info
|
||||
Obtains the storage info
|
||||
|
||||
Args:
|
||||
datacenterId: Id of the datacenter to get information about it
|
||||
storageId: Id of the storage to get information about it
|
||||
force: If true, force to update the cache, if false, tries to first
|
||||
get data from cache and, if valid, return this.
|
||||
|
||||
|
||||
Returns
|
||||
|
||||
|
||||
A dictionary with following values
|
||||
'id' -> Storage id
|
||||
'name' -> Storage name
|
||||
@ -230,7 +229,7 @@ class Provider(ServiceProvider):
|
||||
'available' -> Space available, in bytes
|
||||
'used' -> Space used, in bytes
|
||||
# 'active' -> True or False --> This is not provided by api?? (api.storagedomains.get)
|
||||
|
||||
|
||||
'''
|
||||
return self.__getApi().getStorageInfo(storageId, force)
|
||||
|
||||
@ -238,64 +237,64 @@ class Provider(ServiceProvider):
|
||||
'''
|
||||
Publish the machine (makes a template from it so we can create COWs) and returns the template id of
|
||||
the creating machine
|
||||
|
||||
|
||||
Args:
|
||||
name: Name of the machine (care, only ascii characters and no spaces!!!)
|
||||
machineId: id of the machine to be published
|
||||
clusterId: id of the cluster that will hold the machine
|
||||
storageId: id of the storage tuat will contain the publication AND linked clones
|
||||
displayType: type of display (for oVirt admin interface only)
|
||||
|
||||
|
||||
Returns
|
||||
Raises an exception if operation could not be acomplished, or returns the id of the template being created.
|
||||
'''
|
||||
return self.__getApi().makeTemplate(name, comments, machineId, clusterId, storageId, displayType)
|
||||
|
||||
|
||||
def getTemplateState(self, templateId):
|
||||
'''
|
||||
Returns current template state.
|
||||
|
||||
|
||||
Returned values could be:
|
||||
ok
|
||||
locked
|
||||
removed
|
||||
|
||||
|
||||
(don't know if ovirt returns something more right now, will test what happens when template can't be published)
|
||||
'''
|
||||
return self.__getApi().getTemplateState(templateId)
|
||||
|
||||
|
||||
def getMachineState(self, machineId):
|
||||
'''
|
||||
Returns the state of the machine
|
||||
This method do not uses cache at all (it always tries to get machine state from oVirt server)
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine to get state
|
||||
|
||||
Returns:
|
||||
one of this values:
|
||||
unassigned, down, up, powering_up, powered_down,
|
||||
paused, migrating_from, migrating_to, unknown, not_responding,
|
||||
wait_for_launch, reboot_in_progress, saving_state, restoring_state,
|
||||
unassigned, down, up, powering_up, powered_down,
|
||||
paused, migrating_from, migrating_to, unknown, not_responding,
|
||||
wait_for_launch, reboot_in_progress, saving_state, restoring_state,
|
||||
suspended, image_illegal, image_locked or powering_down
|
||||
Also can return'unknown' if Machine is not known
|
||||
'''
|
||||
return self.__getApi().getMachineState(machineId)
|
||||
|
||||
|
||||
return State.INACTIVE
|
||||
|
||||
def removeTemplate(self, templateId):
|
||||
'''
|
||||
Removes a template from ovirt server
|
||||
|
||||
|
||||
Returns nothing, and raises an Exception if it fails
|
||||
'''
|
||||
return self.__getApi().removeTemplate(templateId)
|
||||
|
||||
|
||||
def deployFromTemplate(self, name, comments, templateId, clusterId, displayType, memoryMB, guaranteedMB):
|
||||
'''
|
||||
Deploys a virtual machine on selected cluster from selected template
|
||||
|
||||
|
||||
Args:
|
||||
name: Name (sanitized) of the machine
|
||||
comments: Comments for machine
|
||||
@ -304,21 +303,21 @@ class Provider(ServiceProvider):
|
||||
displayType: 'vnc' or 'spice'. Display to use ad oVirt admin interface
|
||||
memoryMB: Memory requested for machine, in MB
|
||||
guaranteedMB: Minimum memory guaranteed for this machine
|
||||
|
||||
|
||||
Returns:
|
||||
Id of the machine being created form template
|
||||
Id of the machine being created form template
|
||||
'''
|
||||
return self.__getApi().deployFromTemplate(name, comments, templateId, clusterId, displayType, memoryMB, guaranteedMB)
|
||||
|
||||
|
||||
def startMachine(self, machineId):
|
||||
'''
|
||||
Tries to start a machine. No check is done, it is simply requested to oVirt.
|
||||
|
||||
|
||||
This start also "resume" suspended/paused machines
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine
|
||||
|
||||
|
||||
Returns:
|
||||
'''
|
||||
return self.__getApi().startMachine(machineId)
|
||||
@ -326,80 +325,80 @@ class Provider(ServiceProvider):
|
||||
def stopMachine(self, machineId):
|
||||
'''
|
||||
Tries to start a machine. No check is done, it is simply requested to oVirt
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine
|
||||
|
||||
|
||||
Returns:
|
||||
'''
|
||||
return self.__getApi().stopMachine(machineId)
|
||||
|
||||
|
||||
def suspendMachine(self, machineId):
|
||||
'''
|
||||
Tries to start a machine. No check is done, it is simply requested to oVirt
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine
|
||||
|
||||
|
||||
Returns:
|
||||
'''
|
||||
return self.__getApi().suspendMachine(machineId)
|
||||
|
||||
|
||||
def removeMachine(self, machineId):
|
||||
'''
|
||||
Tries to delete a machine. No check is done, it is simply requested to oVirt
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine
|
||||
|
||||
|
||||
Returns:
|
||||
'''
|
||||
return self.__getApi().removeMachine(machineId)
|
||||
|
||||
|
||||
def updateMachineMac(self, machineId, macAddres):
|
||||
'''
|
||||
Changes the mac address of first nic of the machine to the one specified
|
||||
'''
|
||||
return self.__getApi().updateMachineMac(machineId, macAddres)
|
||||
|
||||
|
||||
def getMacRange(self):
|
||||
return self.macsRange.value
|
||||
|
||||
|
||||
@staticmethod
|
||||
def test(env, data):
|
||||
'''
|
||||
Test ovirt Connectivity
|
||||
|
||||
|
||||
Args:
|
||||
env: environment passed for testing (temporal environment passed)
|
||||
|
||||
data: data passed for testing (data obtained from the form
|
||||
|
||||
data: data passed for testing (data obtained from the form
|
||||
definition)
|
||||
|
||||
Returns:
|
||||
Array of two elements, first is True of False, depending on test
|
||||
|
||||
Returns:
|
||||
Array of two elements, first is True of False, depending on test
|
||||
(True is all right, false is error),
|
||||
second is an String with error, preferably internacionalizated..
|
||||
|
||||
|
||||
'''
|
||||
#try:
|
||||
# try:
|
||||
# # We instantiate the provider, but this may fail...
|
||||
# instance = Provider(env, data)
|
||||
# logger.debug('Methuselah has {0} years and is {1} :-)'
|
||||
# .format(instance.methAge.value, instance.methAlive.value))
|
||||
#except ServiceProvider.ValidationException as e:
|
||||
# # If we say that meth is alive, instantiation will
|
||||
# except ServiceProvider.ValidationException as e:
|
||||
# # If we say that meth is alive, instantiation will
|
||||
# return [False, str(e)]
|
||||
#except Exception as e:
|
||||
# except Exception as e:
|
||||
# logger.exception("Exception caugth!!!")
|
||||
# return [False, str(e)]
|
||||
#return [True, _('Nothing tested, but all went fine..')]
|
||||
# return [True, _('Nothing tested, but all went fine..')]
|
||||
ov = Provider(env, data)
|
||||
if ov.testConnection() is True:
|
||||
return [True, _('Connection test successful')]
|
||||
return [False, _("Connection failed. Check connection params")]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -4,27 +4,27 @@
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# * 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
|
||||
# * 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
|
||||
# * 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
|
||||
# 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
|
||||
# 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.
|
||||
|
||||
'''
|
||||
@ -39,21 +39,22 @@ import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OVirtPublication(Publication):
|
||||
'''
|
||||
This class provides the publication of a oVirtLinkedService
|
||||
'''
|
||||
|
||||
suggestedTime = 20 #: Suggested recheck time if publication is unfinished in seconds
|
||||
|
||||
|
||||
suggestedTime = 20 # : Suggested recheck time if publication is unfinished in seconds
|
||||
|
||||
def initialize(self):
|
||||
'''
|
||||
This method will be invoked by default __init__ of base class, so it gives
|
||||
us the oportunity to initialize whataver we need here.
|
||||
|
||||
us the oportunity to initialize whataver we need here.
|
||||
|
||||
In our case, we setup a few attributes..
|
||||
'''
|
||||
|
||||
|
||||
# We do not check anything at marshal method, so we ensure that
|
||||
# default values are correctly handled by marshal.
|
||||
self._name = ''
|
||||
@ -61,13 +62,13 @@ class OVirtPublication(Publication):
|
||||
self._destroyAfter = 'f'
|
||||
self._templateId = ''
|
||||
self._state = 'r'
|
||||
|
||||
|
||||
def marshal(self):
|
||||
'''
|
||||
returns data from an instance of Sample Publication serialized
|
||||
'''
|
||||
return '\t'.join( ['v1', self._name, self._reason, self._destroyAfter, self._templateId, self._state] )
|
||||
|
||||
return '\t'.join(['v1', self._name, self._reason, self._destroyAfter, self._templateId, self._state])
|
||||
|
||||
def unmarshal(self, data):
|
||||
'''
|
||||
deserializes the data and loads it inside instance.
|
||||
@ -76,86 +77,85 @@ class OVirtPublication(Publication):
|
||||
vals = data.split('\t')
|
||||
if vals[0] == 'v1':
|
||||
self._name, self._reason, self._destroyAfter, self._templateId, self._state = vals[1:]
|
||||
|
||||
|
||||
def publish(self):
|
||||
'''
|
||||
Realizes the publication of the service
|
||||
'''
|
||||
self._name = self.service().sanitizeVmName('UDSP ' + self.dsName() + "-" + str(self.revision()))
|
||||
comments = _('UDS pub for {0} at {1}').format( self.dsName(), str(datetime.now()).split('.')[0] )
|
||||
self._reason = '' # No error, no reason for it
|
||||
comments = _('UDS pub for {0} at {1}').format(self.dsName(), str(datetime.now()).split('.')[0])
|
||||
self._reason = '' # No error, no reason for it
|
||||
self._destroyAfter = 'f'
|
||||
self._state = 'locked'
|
||||
|
||||
|
||||
try:
|
||||
self._templateId = self.service().makeTemplate(self._name, comments)
|
||||
except Exception as e:
|
||||
self._state = 'error'
|
||||
self._reason = str(e)
|
||||
return State.ERROR
|
||||
|
||||
|
||||
return State.RUNNING
|
||||
|
||||
|
||||
def checkState(self):
|
||||
'''
|
||||
Checks state of publication creation
|
||||
'''
|
||||
if self._state == 'ok':
|
||||
return State.FINISHED
|
||||
|
||||
|
||||
if self._state == 'error':
|
||||
return State.ERROR
|
||||
|
||||
|
||||
self._state = self.service().getTemplateState(self._templateId)
|
||||
|
||||
|
||||
# If publication os done (template is ready), and cancel was requested, do it just after template becomes ready
|
||||
if self._state == 'ok':
|
||||
if self._destroyAfter == 't':
|
||||
return self.destroy()
|
||||
return State.FINISHED
|
||||
|
||||
|
||||
return State.RUNNING
|
||||
|
||||
|
||||
|
||||
def finish(self):
|
||||
'''
|
||||
In our case, finish does nothing
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
def reasonOfError(self):
|
||||
'''
|
||||
If a publication produces an error, here we must notify the reason why
|
||||
it happened. This will be called just after publish or checkState
|
||||
If a publication produces an error, here we must notify the reason why
|
||||
it happened. This will be called just after publish or checkState
|
||||
if they return State.ERROR
|
||||
|
||||
Returns an string, in our case, set at checkState
|
||||
|
||||
Returns an string, in our case, set at checkState
|
||||
'''
|
||||
return self._reason
|
||||
|
||||
def destroy(self):
|
||||
'''
|
||||
This is called once a publication is no more needed.
|
||||
|
||||
|
||||
This method do whatever needed to clean up things, such as
|
||||
removing created "external" data (environment gets cleaned by core),
|
||||
etc..
|
||||
|
||||
etc..
|
||||
|
||||
The retunred value is the same as when publishing, State.RUNNING,
|
||||
State.FINISHED or State.ERROR.
|
||||
'''
|
||||
'''
|
||||
# We do not do anything else to destroy this instance of publication
|
||||
if self._state == 'locked':
|
||||
self._destroyAfter = 't'
|
||||
return State.RUNNING
|
||||
|
||||
|
||||
try:
|
||||
self.service().removeTemplate(self._templateId)
|
||||
except Exception as e:
|
||||
self._state = 'error'
|
||||
self._reason = str(e)
|
||||
return State.ERROR
|
||||
|
||||
|
||||
return State.FINISHED
|
||||
|
||||
def cancel(self):
|
||||
|
@ -17,43 +17,44 @@ lock = threading.Lock()
|
||||
cached_api = None
|
||||
cached_api_key = None
|
||||
|
||||
|
||||
class Client(object):
|
||||
'''
|
||||
Module to manage oVirt connections using ovirtsdk.
|
||||
|
||||
|
||||
Due to the fact that we can't create two proxy connections at same time, we serialize all access to ovirt platform.
|
||||
Only one request and one live connection can exists at a time.
|
||||
|
||||
|
||||
This can waste a lot of time, so use of cache here is more than important to achieve aceptable performance.
|
||||
|
||||
|
||||
'''
|
||||
|
||||
CACHE_TIME_LOW = 60*5 # Cache time for requests are 5 minutes by default
|
||||
CACHE_TIME_HIGH = 60*30 # Cache time for requests that are less probable to change (as cluster perteinance of a machine)
|
||||
CACHE_TIME_LOW = 60 * 5 # Cache time for requests are 5 minutes by default
|
||||
CACHE_TIME_HIGH = 60 * 30 # Cache time for requests that are less probable to change (as cluster perteinance of a machine)
|
||||
|
||||
def __getKey(self, prefix = ''):
|
||||
def __getKey(self, prefix=''):
|
||||
'''
|
||||
Creates a key for the cache, using the prefix indicated as part of it
|
||||
|
||||
|
||||
Returns:
|
||||
The cache key, taking into consideration the prefix
|
||||
'''
|
||||
return prefix + self._host + self._username + self._password + str(self._timeout)
|
||||
return prefix + self._host + self._username + self._password + str(self._timeout)
|
||||
|
||||
def __getApi(self):
|
||||
'''
|
||||
Gets the api connection.
|
||||
|
||||
|
||||
Again, due to the fact that ovirtsdk don't allow (at this moment, but it's on the "TODO" list) concurrent access to
|
||||
more than one server, we keep only one opened connection.
|
||||
|
||||
|
||||
Must be acceses "locked", we can alter cached_api and cached_api_key
|
||||
'''
|
||||
global cached_api, cached_api_key
|
||||
global cached_api, cached_api_key
|
||||
aKey = self.__getKey('o-host')
|
||||
#if cached_api_key == aKey:
|
||||
# if cached_api_key == aKey:
|
||||
# return cached_api
|
||||
|
||||
|
||||
if cached_api is not None:
|
||||
try:
|
||||
cached_api.disconnect()
|
||||
@ -62,14 +63,13 @@ class Client(object):
|
||||
pass
|
||||
try:
|
||||
cached_api_key = aKey
|
||||
cached_api = API(url='https://'+self._host+'/api', username=self._username, password=self._password, timeout=self._timeout, insecure=True, debug=True)
|
||||
cached_api = API(url='https://' + self._host + '/api', username=self._username, password=self._password, timeout=self._timeout, insecure=True, debug=True)
|
||||
return cached_api
|
||||
except:
|
||||
logger.exception('Exception connection ovirt at {0}'.format(self._host))
|
||||
cached_api_key = None
|
||||
raise Exception("Can't connet to server at {0}".format(self._host))
|
||||
return None
|
||||
|
||||
|
||||
def __init__(self, host, username, password, timeout, cache):
|
||||
self._host = host
|
||||
@ -78,7 +78,6 @@ class Client(object):
|
||||
self._timeout = int(timeout)
|
||||
self._cache = cache
|
||||
|
||||
|
||||
def test(self):
|
||||
try:
|
||||
lock.acquire(True)
|
||||
@ -88,102 +87,101 @@ class Client(object):
|
||||
return False
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
def getVms(self, force = False):
|
||||
|
||||
def getVms(self, force=False):
|
||||
'''
|
||||
Obtains the list of machines inside ovirt that do aren't part of uds
|
||||
|
||||
|
||||
Args:
|
||||
force: If true, force to update the cache, if false, tries to first
|
||||
get data from cache and, if valid, return this.
|
||||
|
||||
|
||||
Returns
|
||||
An array of dictionaries, containing:
|
||||
'name'
|
||||
'id'
|
||||
'cluster_id'
|
||||
|
||||
|
||||
'''
|
||||
vmsKey = self.__getKey('o-vms')
|
||||
val = self._cache.get(vmsKey)
|
||||
|
||||
|
||||
if val is not None and force is False:
|
||||
return val
|
||||
|
||||
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
vms = api.vms.list(query='name!=UDS*')
|
||||
|
||||
|
||||
res = []
|
||||
|
||||
|
||||
for vm in vms:
|
||||
res.append({ 'name' : vm.get_name(), 'id' : vm.get_id(), 'cluster_id' : vm.get_cluster().get_id() })
|
||||
|
||||
res.append({'name': vm.get_name(), 'id': vm.get_id(), 'cluster_id': vm.get_cluster().get_id()})
|
||||
|
||||
self._cache.put(vmsKey, res, Client.CACHE_TIME_LOW)
|
||||
|
||||
|
||||
return res
|
||||
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
def getClusters(self, force = False):
|
||||
|
||||
def getClusters(self, force=False):
|
||||
'''
|
||||
Obtains the list of clusters inside ovirt
|
||||
|
||||
|
||||
Args:
|
||||
force: If true, force to update the cache, if false, tries to first
|
||||
get data from cache and, if valid, return this.
|
||||
|
||||
|
||||
Returns
|
||||
Filters out clusters not attached to any datacenter
|
||||
An array of dictionaries, containing:
|
||||
'name'
|
||||
'id'
|
||||
'datacenter_id'
|
||||
|
||||
|
||||
'''
|
||||
clsKey = self.__getKey('o-clusters')
|
||||
val = self._cache.get(clsKey)
|
||||
|
||||
|
||||
if val is not None and force is False:
|
||||
return val
|
||||
|
||||
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
clusters = api.clusters.list()
|
||||
|
||||
|
||||
res = []
|
||||
|
||||
|
||||
for cluster in clusters:
|
||||
dc = cluster.get_data_center()
|
||||
|
||||
|
||||
if dc is not None:
|
||||
dc = dc.get_id()
|
||||
|
||||
val = { 'name' : cluster.get_name(), 'id' : cluster.get_id(), 'datacenter_id' : dc }
|
||||
|
||||
val = {'name': cluster.get_name(), 'id': cluster.get_id(), 'datacenter_id': dc}
|
||||
|
||||
# Updates cache info for every single cluster
|
||||
clKey = self.__getKey('o-cluster'+cluster.get_id())
|
||||
clKey = self.__getKey('o-cluster' + cluster.get_id())
|
||||
self._cache.put(clKey, val)
|
||||
|
||||
|
||||
if dc is not None:
|
||||
res.append(val)
|
||||
|
||||
|
||||
self._cache.put(clsKey, res, Client.CACHE_TIME_HIGH)
|
||||
|
||||
|
||||
return res
|
||||
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
|
||||
def getClusterInfo(self, clusterId, force = False):
|
||||
|
||||
def getClusterInfo(self, clusterId, force=False):
|
||||
'''
|
||||
Obtains the cluster info
|
||||
|
||||
@ -191,39 +189,39 @@ class Client(object):
|
||||
datacenterId: Id of the cluster to get information about it
|
||||
force: If true, force to update the cache, if false, tries to first
|
||||
get data from cache and, if valid, return this.
|
||||
|
||||
|
||||
Returns
|
||||
|
||||
|
||||
A dictionary with following values
|
||||
'name'
|
||||
'id'
|
||||
'datacenter_id'
|
||||
'''
|
||||
clKey = self.__getKey('o-cluster'+clusterId)
|
||||
clKey = self.__getKey('o-cluster' + clusterId)
|
||||
val = self._cache.get(clKey)
|
||||
|
||||
|
||||
if val is not None and force is False:
|
||||
return val
|
||||
|
||||
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
c = api.clusters.get(id=clusterId)
|
||||
|
||||
|
||||
dc = c.get_data_center()
|
||||
|
||||
|
||||
if dc is not None:
|
||||
dc = dc.get_id()
|
||||
|
||||
res = { 'name' : c.get_name(), 'id' : c.get_id(), 'datacenter_id' : dc }
|
||||
|
||||
res = {'name': c.get_name(), 'id': c.get_id(), 'datacenter_id': dc}
|
||||
self._cache.put(clKey, res, Client.CACHE_TIME_HIGH)
|
||||
return res
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
def getDatacenterInfo(self, datacenterId, force = False):
|
||||
|
||||
def getDatacenterInfo(self, datacenterId, force=False):
|
||||
'''
|
||||
Obtains the datacenter info
|
||||
|
||||
@ -231,9 +229,9 @@ class Client(object):
|
||||
datacenterId: Id of the datacenter to get information about it
|
||||
force: If true, force to update the cache, if false, tries to first
|
||||
get data from cache and, if valid, return this.
|
||||
|
||||
|
||||
Returns
|
||||
|
||||
|
||||
A dictionary with following values
|
||||
'name'
|
||||
'id'
|
||||
@ -247,19 +245,19 @@ class Client(object):
|
||||
'available' -> Space available, in bytes
|
||||
'used' -> Space used, in bytes
|
||||
'active' -> True or False
|
||||
|
||||
|
||||
'''
|
||||
dcKey = self.__getKey('o-dc'+datacenterId)
|
||||
dcKey = self.__getKey('o-dc' + datacenterId)
|
||||
val = self._cache.get(dcKey)
|
||||
|
||||
|
||||
if val is not None and force is False:
|
||||
return val
|
||||
|
||||
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
d = api.datacenters.get(id=datacenterId)
|
||||
storage = []
|
||||
for dd in d.storagedomains.list():
|
||||
@ -267,22 +265,21 @@ class Client(object):
|
||||
active = dd.get_status().get_state()
|
||||
except:
|
||||
active = 'inactive'
|
||||
|
||||
storage.append( { 'id' : dd.get_id(), 'name' : dd.get_name(), 'type' : dd.get_type(),
|
||||
'available' : dd.get_available(), 'used' : dd.get_used(),
|
||||
'active' : active == 'active' } )
|
||||
|
||||
|
||||
res = { 'name' : d.get_name(), 'id' : d.get_id(), 'storage_type' : d.get_storage_type(),
|
||||
'storage_format' : d.get_storage_format(), 'description' : d.get_description(),
|
||||
'storage' : storage }
|
||||
|
||||
|
||||
storage.append({'id': dd.get_id(), 'name': dd.get_name(), 'type': dd.get_type(),
|
||||
'available': dd.get_available(), 'used': dd.get_used(),
|
||||
'active': active == 'active'})
|
||||
|
||||
res = {'name': d.get_name(), 'id': d.get_id(), 'storage_type': d.get_storage_type(),
|
||||
'storage_format': d.get_storage_format(), 'description': d.get_description(),
|
||||
'storage': storage}
|
||||
|
||||
self._cache.put(dcKey, res, Client.CACHE_TIME_HIGH)
|
||||
return res
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
def getStorageInfo(self, storageId, force = False):
|
||||
|
||||
def getStorageInfo(self, storageId, force=False):
|
||||
'''
|
||||
Obtains the datacenter info
|
||||
|
||||
@ -290,9 +287,9 @@ class Client(object):
|
||||
datacenterId: Id of the datacenter to get information about it
|
||||
force: If true, force to update the cache, if false, tries to first
|
||||
get data from cache and, if valid, return this.
|
||||
|
||||
|
||||
Returns
|
||||
|
||||
|
||||
A dictionary with following values
|
||||
'id' -> Storage id
|
||||
'name' -> Storage name
|
||||
@ -300,56 +297,56 @@ class Client(object):
|
||||
'available' -> Space available, in bytes
|
||||
'used' -> Space used, in bytes
|
||||
# 'active' -> True or False --> This is not provided by api?? (api.storagedomains.get)
|
||||
|
||||
|
||||
'''
|
||||
sdKey = self.__getKey('o-sd'+storageId)
|
||||
sdKey = self.__getKey('o-sd' + storageId)
|
||||
val = self._cache.get(sdKey)
|
||||
|
||||
|
||||
if val is not None and force is False:
|
||||
return val
|
||||
|
||||
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
dd = api.storagedomains.get(id=storageId)
|
||||
|
||||
|
||||
|
||||
res = { 'id' : dd.get_id(), 'name' : dd.get_name(), 'type' : dd.get_type(),
|
||||
'available' : dd.get_available(), 'used' : dd.get_used()
|
||||
}
|
||||
|
||||
|
||||
res = {
|
||||
'id': dd.get_id(),
|
||||
'name': dd.get_name(),
|
||||
'type': dd.get_type(),
|
||||
'available': dd.get_available(), 'used': dd.get_used()
|
||||
}
|
||||
|
||||
self._cache.put(sdKey, res, Client.CACHE_TIME_LOW)
|
||||
return res
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
|
||||
def makeTemplate(self, name, comments, machineId, clusterId, storageId, displayType):
|
||||
'''
|
||||
Publish the machine (makes a template from it so we can create COWs) and returns the template id of
|
||||
the creating machine
|
||||
|
||||
|
||||
Args:
|
||||
name: Name of the machine (care, only ascii characters and no spaces!!!)
|
||||
machineId: id of the machine to be published
|
||||
clusterId: id of the cluster that will hold the machine
|
||||
storageId: id of the storage tuat will contain the publication AND linked clones
|
||||
displayType: type of display (for oVirt admin interface only)
|
||||
|
||||
|
||||
Returns
|
||||
Raises an exception if operation could not be acomplished, or returns the id of the template being created.
|
||||
'''
|
||||
logger.debug("n: {0}, c: {1}, vm: {2}, cl: {3}, st: {3}, dt: {4}".format(name, comments, machineId, clusterId, storageId, displayType))
|
||||
|
||||
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
|
||||
|
||||
cluster = api.clusters.get(id=clusterId)
|
||||
vm = api.vms.get(id=machineId)
|
||||
|
||||
@ -361,61 +358,60 @@ class Client(object):
|
||||
|
||||
if vm.get_status().get_state() != 'down':
|
||||
raise Exception('Machine must be in down state to publish it')
|
||||
|
||||
|
||||
# Create disks description to be created in specified storage domain, one for each disk
|
||||
sd = params.StorageDomains(storage_domain=[params.StorageDomain(id=storageId)])
|
||||
|
||||
|
||||
dsks = []
|
||||
for dsk in vm.disks.list():
|
||||
dsks.append(params.Disk(id=dsk.get_id(), storage_domains=sd))
|
||||
|
||||
|
||||
disks = params.Disks(disk=dsks)
|
||||
|
||||
|
||||
# Create display description
|
||||
display = params.Display(type_=displayType)
|
||||
|
||||
template = params.Template(name=name, vm=params.VM(id=vm.get_id(), disks=disks),
|
||||
|
||||
template = params.Template(name=name, vm=params.VM(id=vm.get_id(), disks=disks),
|
||||
cluster=params.Cluster(id=cluster.get_id()), description=comments)
|
||||
#display=display)
|
||||
|
||||
# display=display)
|
||||
|
||||
return api.templates.add(template).get_id()
|
||||
|
||||
#return api.templates.get(name=name).get_id()
|
||||
|
||||
# return api.templates.get(name=name).get_id()
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
|
||||
|
||||
def getTemplateState(self, templateId):
|
||||
'''
|
||||
Returns current template state.
|
||||
This method do not uses cache at all (it always tries to get template state from oVirt server)
|
||||
|
||||
|
||||
Returned values could be:
|
||||
ok
|
||||
locked
|
||||
removed
|
||||
|
||||
|
||||
(don't know if ovirt returns something more right now, will test what happens when template can't be published)
|
||||
'''
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
template = api.templates.get(id=templateId)
|
||||
|
||||
|
||||
if template is None:
|
||||
return 'removed'
|
||||
|
||||
|
||||
return template.get_status().get_state()
|
||||
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
|
||||
def deployFromTemplate(self, name, comments, templateId, clusterId, displayType, memoryMB, guaranteedMB):
|
||||
'''
|
||||
Deploys a virtual machine on selected cluster from selected template
|
||||
|
||||
|
||||
Args:
|
||||
name: Name (sanitized) of the machine
|
||||
comments: Comments for machine
|
||||
@ -424,9 +420,9 @@ class Client(object):
|
||||
displayType: 'vnc' or 'spice'. Display to use ad oVirt admin interface
|
||||
memoryMB: Memory requested for machine, in MB
|
||||
guaranteedMB: Minimum memory guaranteed for this machine
|
||||
|
||||
|
||||
Returns:
|
||||
Id of the machine being created form template
|
||||
Id of the machine being created form template
|
||||
'''
|
||||
logger.debug('Deploying machine with name "{0}" from template {1} at cluster {2} with display {3}, memory {4} and guaranteed {5}'.format(
|
||||
name, templateId, clusterId, displayType, memoryMB, guaranteedMB))
|
||||
@ -434,168 +430,168 @@ class Client(object):
|
||||
lock.acquire(True)
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
logger.debug('Deploying machine {0}'.format(name))
|
||||
|
||||
|
||||
cluster = params.Cluster(id=clusterId)
|
||||
template = params.Template(id=templateId)
|
||||
display = params.Display(type_=displayType)
|
||||
|
||||
memoryPolicy = params.MemoryPolicy(guaranteed=guaranteedMB*1024*1024)
|
||||
par = params.VM(name=name, cluster=cluster, template=template, description=comments,
|
||||
type_='desktop', memory=memoryMB*1024*1024, memory_policy=memoryPolicy) # display=display,
|
||||
|
||||
|
||||
memoryPolicy = params.MemoryPolicy(guaranteed=guaranteedMB * 1024 * 1024)
|
||||
par = params.VM(name=name, cluster=cluster, template=template, description=comments,
|
||||
type_='desktop', memory=memoryMB * 1024 * 1024, memory_policy=memoryPolicy) # display=display,
|
||||
|
||||
return api.vms.add(par).get_id()
|
||||
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
|
||||
def removeTemplate(self, templateId):
|
||||
'''
|
||||
Removes a template from ovirt server
|
||||
|
||||
|
||||
Returns nothing, and raises an Exception if it fails
|
||||
'''
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
template = api.templates.get(id=templateId)
|
||||
if template is None:
|
||||
raise Exception('Template does not exists')
|
||||
|
||||
|
||||
template.delete()
|
||||
# This returns nothing, if it fails it raises an exception
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
|
||||
def getMachineState(self, machineId):
|
||||
'''
|
||||
Returns current state of a machine (running, suspended, ...).
|
||||
This method do not uses cache at all (it always tries to get machine state from oVirt server)
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine to get status
|
||||
|
||||
|
||||
Returns:
|
||||
one of this values:
|
||||
unassigned, down, up, powering_up, powered_down,
|
||||
paused, migrating_from, migrating_to, unknown, not_responding,
|
||||
wait_for_launch, reboot_in_progress, saving_state, restoring_state,
|
||||
unassigned, down, up, powering_up, powered_down,
|
||||
paused, migrating_from, migrating_to, unknown, not_responding,
|
||||
wait_for_launch, reboot_in_progress, saving_state, restoring_state,
|
||||
suspended, image_illegal, image_locked or powering_down
|
||||
Also can return'unknown' if Machine is not known
|
||||
'''
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
vm = api.vms.get(id=machineId)
|
||||
|
||||
|
||||
if vm is None or vm.get_status() is None:
|
||||
return 'unknown'
|
||||
|
||||
|
||||
return vm.get_status().get_state()
|
||||
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
|
||||
def startMachine(self, machineId):
|
||||
'''
|
||||
Tries to start a machine. No check is done, it is simply requested to oVirt.
|
||||
|
||||
|
||||
This start also "resume" suspended/paused machines
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine
|
||||
|
||||
|
||||
Returns:
|
||||
'''
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
vm = api.vms.get(id=machineId)
|
||||
|
||||
if vm is None:
|
||||
raise Exception('Machine not found')
|
||||
|
||||
|
||||
vm.start()
|
||||
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
def stopMachine(self, machineId):
|
||||
'''
|
||||
Tries to start a machine. No check is done, it is simply requested to oVirt
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine
|
||||
|
||||
|
||||
Returns:
|
||||
'''
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
vm = api.vms.get(id=machineId)
|
||||
|
||||
if vm is None:
|
||||
raise Exception('Machine not found')
|
||||
|
||||
|
||||
vm.stop()
|
||||
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
|
||||
def suspendMachine(self, machineId):
|
||||
'''
|
||||
Tries to start a machine. No check is done, it is simply requested to oVirt
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine
|
||||
|
||||
|
||||
Returns:
|
||||
'''
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
vm = api.vms.get(id=machineId)
|
||||
|
||||
if vm is None:
|
||||
raise Exception('Machine not found')
|
||||
|
||||
|
||||
vm.suspend()
|
||||
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
|
||||
def removeMachine(self, machineId):
|
||||
'''
|
||||
Tries to delete a machine. No check is done, it is simply requested to oVirt
|
||||
|
||||
|
||||
Args:
|
||||
machineId: Id of the machine
|
||||
|
||||
|
||||
Returns:
|
||||
'''
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
vm = api.vms.get(id=machineId)
|
||||
|
||||
if vm is None:
|
||||
raise Exception('Machine not found')
|
||||
|
||||
|
||||
vm.delete()
|
||||
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
@ -605,23 +601,22 @@ class Client(object):
|
||||
'''
|
||||
try:
|
||||
lock.acquire(True)
|
||||
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
vm = api.vms.get(id=machineId)
|
||||
|
||||
if vm is None:
|
||||
raise Exception('Machine not found')
|
||||
|
||||
nic = vm.nics.list()[0] # If has no nic, will raise an exception (IndexError)
|
||||
|
||||
|
||||
nic = vm.nics.list()[0] # If has no nic, will raise an exception (IndexError)
|
||||
|
||||
nic.get_mac().set_address(macAddres)
|
||||
|
||||
nic.update() # Updates the nic
|
||||
|
||||
|
||||
nic.update() # Updates the nic
|
||||
|
||||
except IndexError:
|
||||
raise Exception('Machine do not have network interfaces!!')
|
||||
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
Loading…
Reference in New Issue
Block a user