1
0
mirror of https://github.com/dkmstr/openuds.git synced 2024-12-31 17:17:53 +03:00

oVirt provider now publishes and removes poblications. Already unusable until finished oVirtLinkedDeployment at least

This commit is contained in:
Adolfo Gómez 2012-11-16 09:05:55 +00:00
parent 7717793604
commit aebacb166b
4 changed files with 181 additions and 155 deletions

View File

@ -129,6 +129,14 @@ class OVirtLinkedService(Service):
if self.baseName.value.isdigit():
raise Service.ValidationException(_('The machine name can\'t be only numbers'))
def initGui(self):
'''
Loads required values inside
'''
self.ov.value = self.parent().serialize()
self.ev.value = self.parent().env().key()
machines = self.parent().getMachines()
vals = []
for m in machines:
@ -140,9 +148,44 @@ class OVirtLinkedService(Service):
for c in clusters:
vals.append( gui.choiceItem(c['id'], c['name'] ) )
self.cluster.setValues(vals)
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.
'''
return self.parent().makeTemplate(name, comments, self.machine.value, self.cluster.value, self.datastore.value)
def getTemplateState(self, templateId):
'''
Invokes getTemplateState from parent provider
def initGui(self):
self.ov.value = self.parent().serialize()
self.ev.value = self.parent().env().key()
Args:
templateId: templateId to remove
Returns nothing
Raises an exception if operation fails.
'''
return self.parent().getTemplateState(templateId)
def removeTemplate(self, templateId):
'''
invokes removeTemplate from parent provider
'''
return self.parent().removeTemplate(templateId)

View File

@ -232,6 +232,43 @@ class Provider(ServiceProvider):
'''
return self.__getApi().getStorageInfo(storageId, force)
def makeTemplate(self, name, comments, vmId, clusterId, storageId):
'''
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!!!)
vmId: 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
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, vmId, clusterId, storageId)
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 removeTemplate(self, templateId):
'''
Removes a template from ovirt server
Returns nothing, and raises an Exception if it fails
'''
return self.__getApi().removeTemplate(templateId)
@staticmethod
def test(env, data):

View File

@ -41,43 +41,10 @@ logger = logging.getLogger(__name__)
class OVirtPublication(Publication):
'''
This class shows how a publication is developed.
In order to a publication to work correctly, we must provide at least the
following methods:
* Of course, the __init__
* :py:meth:`.publish`
* :py:meth:`.checkState`
* :py:meth:`.finish`
Also, of course, methods from :py:class:`uds.core.Serializable.Serializable`
Publication do not have an configuration interface, all data contained
inside an instance of a Publication must be serialized if you want them between
method calls.
It's not waranteed that the class will not be serialized/deserialized
between methods calls, so, first of all, implement the marshal and umnarshal
mehods needed by all serializable classes.
Also a thing to note is that operations requested to Publications must be
*as fast as posible*. The operations executes in a separated thread,
and so it cant take a bit more time to execute, but it's recommended that
the operations executes as fast as posible, and, if it will take a long time,
split operation so we can keep track of state.
This means that, if we have "slow" operations, we must
We first of all declares an estimation of how long a publication will take.
This value is instance based, so if we override it in our class, the suggested
time could change.
The class attribute that indicates this suggested time is "suggestedTime", and
it's expressed in seconds, (i.e. "suggestedTime = 10")
This class provides the publication of a oVirtLinkedService
'''
suggestedTime = 5 #: Suggested recheck time if publication is unfinished in seconds
suggestedTime = 20 #: Suggested recheck time if publication is unfinished in seconds
def initialize(self):
'''
@ -89,15 +56,17 @@ class OVirtPublication(Publication):
# We do not check anything at marshal method, so we ensure that
# default values are correctly handled by marshal.
self._name = 'test'
self._reason = '' # No error, no reason for it
self._number = 1
self._name = ''
self._reason = ''
self._destroyAfter = 'f'
self._templateId = ''
self._state = 'r'
def marshal(self):
'''
returns data from an instance of Sample Publication serialized
'''
return '\t'.join( [self._name, self._reason, str(self._number)] )
return '\t'.join( ['v1', self._name, self._reason, self._destroyAfter, self._templateId, self._state] )
def unmarshal(self, data):
'''
@ -105,115 +74,53 @@ class OVirtPublication(Publication):
'''
logger.debug('Data: {0}'.format(data))
vals = data.split('\t')
logger.debug('Values: {0}'.format(vals))
self._name = vals[0]
self._reason = vals[1]
self._number = int(vals[2])
if vals[0] == 'v1':
self._name, self._reason, self._destroyAfter, self._templateId, self._state = vals[1:]
def publish(self):
'''
This method is invoked whenever the administrator requests a new publication.
The method is not invoked directly (i mean, that the administration request
do no makes a call to this method), but a DelayedTask is saved witch will
initiate all publication stuff (and, of course, call this method).
You MUST implement it, so the publication do really something.
All publications can be synchronous or asynchronous.
The main difference between both is that first do whatever needed, (the
action must be fast enough to do not block core), returning State.FINISHED.
The second (asynchronous) are publications that could block the core, so
it have to be done in more than one step.
An example publication could be a copy of a virtual machine, where:
* First we invoke the copy operation to virtualization provider
* Second, we kept needed values inside instance so we can serialize
them whenever requested
* Returns an State.RUNNING, indicating the core that the publication
has started but has to finish sometime later. (We do no check
again the state and keep waiting here, because we will block the
core untill this operation is finished).
In our example wi will simple assign a name, and set number to 5. We
will use this number later, to make a "delay" at check if the publication
has finished. (see method checkState)
We also will make this publication an "stepped one", that is, it will not
finish at publish call but a later checkState call
Take care with instantiating threads from here. Whenever a publish returns
"State.RUNNING", the core will recheck it later, but not using this instance
and maybe that even do not use this server.
If you want to use threadings or somethin likt it, use DelayedTasks and
do not block it. You also musht provide the mechanism to allow those
DelayedTask to communicate with the publication.
One sample could be, for example, to copy a bunch of files, but we know
that this copy can take a long time and don't want it to take make it
all here, but in a separate task. Now, do you remember that "environment"
that is unique for every instance?, well, we can create a delayed task,
and pass that environment (owned by this intance) as a mechanism for
informing when the task is finished. (We insert at delayed tasks queue
an instance, not a class itself, so we can instantiate a class and
store it at delayed task queue.
Also note that, in that case, this class can also acomplish that by simply
using the suggestedTime attribute and the checkState method in most cases.
Realizes the publication of the service
'''
self._number = 5
self._reason = ''
self._name = self.service().sanitizeVmName('UDS Publication' + ' ' + 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
self._destroyAfter = 'f'
self._state = 'locked'
try:
self._templateId = self.service().makeTemplate(self._name, comments)
except Exception as e:
self._reason = str(e)
return State.ERROR
return State.RUNNING
def checkState(self):
'''
Our publish method will initiate publication, but will not finish it.
So in our sample, wi will only check if _number reaches 0, and if so
return that we have finished, else we will return that we are working
on it.
One publish returns State.RUNNING, this task will get called untill
checkState returns State.FINISHED.
Also, wi will make the publication fail one of every 10 calls to this
method.
Note: Destroying an publication also makes use of this method, so you
must keep the info of that you are checking (publishing or destroying...)
In our case, destroy is 1-step action so this will no get called while
destroying...
Checks state of publication creation
'''
import random
self._number -= 1
# Serialization will take care of storing self._number
# One of every 10 calls
if random.randint(0, 9) == 9:
self._reason = _('Random integer was 9!!! :-)')
return State.ERROR
if self._number <= 0:
if self._state == 'ok':
return State.FINISHED
else:
return State.RUNNING
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):
'''
Invoked when Publication manager noticed that the publication has finished.
This give us the oportunity of cleaning up things (as stored vars, etc..),
or initialize variables that will be needed in a later phase (by deployed
services)
Returned value, if any, is ignored
In our case, finish does nothing
'''
import string
import random
# Make simply a random string
self._name = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10))
pass
def reasonOfError(self):
'''
@ -236,26 +143,22 @@ class OVirtPublication(Publication):
The retunred value is the same as when publishing, State.RUNNING,
State.FINISHED or State.ERROR.
'''
self._name = ''
self._reason = '' # In fact, this is not needed, but cleaning up things... :-)
# We do not do anything else to destroy this instance of publication
return State.FINISHED
if self._state == 'locked':
self._destroyAfter = 't'
return State.RUNNING
try:
self.service().removeTemplate(self._templateId)
except Exception as e:
self._reason = str(e)
return State.ERROR
return State.FINISHED
def cancel(self):
'''
Invoked for canceling the current operation.
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.
Also, take into account that cancel is the initiation of, maybe, a
multiple-step action, so it returns, as publish and destroy does.
In our case, cancel simply invokes "destroy", that cleans up
things and returns that the action has finished in 1 step.
Do same thing as destroy
'''
return self.destroy()

View File

@ -316,7 +316,7 @@ class Client(object):
lock.release()
def publish(self, name, vmId, clusterId, storageId):
def makeTemplate(self, name, comments, vmId, clusterId, storageId):
'''
Publish the machine (makes a template from it so we can create COWs) and returns the template id of
the creating machine
@ -330,6 +330,7 @@ class Client(object):
Returns
Raises an exception if operation could not be acomplished, or returns the id of the template being created.
'''
print "n: {0}, c: {1}, vm: {2}, cl: {3}, st: {3}".format(name, comments, vmId, clusterId, storageId)
try:
lock.acquire(True)
@ -337,25 +338,67 @@ class Client(object):
api = self.__getApi()
storage = api.storagedomains.get(id=storageId)
storage_domain = params.StorageDomain(storage)
cluster = api.clusters.get(id=clusterId)
vm = api.vms.get(id=vmId)
if vm.get_status().get_state() != 'down':
raise Exception('Machine must be in down state to publish it')
template = params.Template(name=name,storage_domain=storage_domain, vm=vm, cluster=cluster, description=comments)
api.templates.add(template)
api.templates.add(params.Template(storage_domain=storage, origin = 'UDS', name=name, vm=vm, cluster=cluster))
return api.templates.get(name=name).get_id()
finally:
lock.release()
def getPublishState(self, templateId):
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)
'''
try:
lock.acquire(True)
api = self.__getApi()
return api.templates.get(id=templateId).get_status().get_state()
template = api.templates.get(id=templateId)
if template is None:
return 'removed'
return template.get_status().get_state()
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()