Added safety "min space" to oVirt an HyperV

Fixes related to pep
This commit is contained in:
Adolfo Gómez 2014-03-28 11:00:51 +00:00
parent 1140f1b4e6
commit 86318a66c3
6 changed files with 678 additions and 663 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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