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:
parent
7717793604
commit
aebacb166b
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user