From 0bb26cfae18d02130013e223d3463289313ac61f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez?= Date: Thu, 15 Nov 2012 08:56:59 +0000 Subject: [PATCH] * Added more methods needed at oVirtClient * Finished (need testing) oVirtLinkedService, so now we can add new oVirtLinkedServices, but can't publish it :-) * Added helpers methods to allow new oVirtLinkedServices --- server/src/uds/services/OVirt/Helpers.py | 44 +++++++ .../uds/services/OVirt/OVirtLinkedService.py | 87 ++++++------- .../src/uds/services/OVirt/OVirtProvider.py | 115 +++++++++++++++++- .../uds/services/OVirt/client/oVirtClient.py | 63 +++++++++- 4 files changed, 257 insertions(+), 52 deletions(-) create mode 100644 server/src/uds/services/OVirt/Helpers.py diff --git a/server/src/uds/services/OVirt/Helpers.py b/server/src/uds/services/OVirt/Helpers.py new file mode 100644 index 00000000..81d5191a --- /dev/null +++ b/server/src/uds/services/OVirt/Helpers.py @@ -0,0 +1,44 @@ +''' +Created on Nov 15, 2012 + +@author: dkmaster +''' + +from django.utils.translation import ugettext as _ +import logging + +logger = logging.getLogger(__name__) + +class oVirtHelpers(object): + + @staticmethod + def getResources(parameters): + ''' + This helper is designed as a callback for machine selector, so we can provide valid clusters and datastores domains based on it + ''' + from OVirtProvider import Provider + from uds.core.Environment import Environment + logger.debug('Parameters received by getResources Helper: {0}'.format(parameters)) + env = Environment(parameters['ev']) + provider = Provider(env) + provider.unserialize(parameters['ov']) + + # Obtains datacenter from cluster + ci = provider.getClusterInfo(parameters['cluster']) + + res = [] + # Get storages for that datacenter + for storage in provider.getDatacenterInfo(ci['datacenter_id'])['storage']: + if storage['active'] is True and 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)" % (storage['name'], space, free ) }) + data = [{ + 'name' : 'datastore', 'values' : res + }] + + logger.debug('return data: {0}'.format(data)) + return data + + + diff --git a/server/src/uds/services/OVirt/OVirtLinkedService.py b/server/src/uds/services/OVirt/OVirtLinkedService.py index b5a9cb83..b2e9858b 100644 --- a/server/src/uds/services/OVirt/OVirtLinkedService.py +++ b/server/src/uds/services/OVirt/OVirtLinkedService.py @@ -42,6 +42,8 @@ import logging 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 @@ -93,21 +95,25 @@ class OVirtLinkedService(Service): #: Types of deploys (services in cache and/or assigned to users) deployedType = OVirtLinkedDeployment - # Now the form part, this service will have only two "dummy" fields - # If we don't indicate an order, the output order of fields will be - # "random" + # Now the form part + machine = gui.ChoiceField(label=translatable("Base Machine"), order = 1, tooltip = _('Service base machine'), required = True) + cluster = gui.ChoiceField(label=translatable("Cluster"), order = 2, + fills = { + 'callbackName' : 'ovFillResourcesFromCluster', + 'function' : oVirtHelpers.getResources, + 'parameters' : ['cluster', 'ov', 'ev'] + }, + tooltip = translatable("Cluster to contain services"), required = True + ) - machine = gui.ChoiceField(label = _("Base Machine"), order = 6, tooltip = _('Base machine for this service'), required = True ) + datastore = gui.ChoiceField(label = translatable("Datastore Domain"), rdonly = False, order = 3, + tooltip = translatable('Datastore domain where to publish and put incrementals'), required = True) + baseName = gui.TextField(label = translatable('Machine Names'), rdonly = False, order = 4, tooltip = ('Base name for clones from this machine'), required = True) + lenName = gui.NumericField(length = 1, label = translatable('Name Length'), defvalue = 3, order = 5, + tooltip = translatable('Length of numeric part for the names of this machines (betwen 3 and 6'), required = True) - - - baseName = gui.TextField(order = 3, - label = translatable('Services names'), - tooltip = translatable('Base name for this user services'), - # In this case, the choice can have none value selected by default - required = True, - defvalue = '' # Default value is the ID of the choicefield - ) + ov = gui.HiddenField() + ev = gui.HiddenField() # We need to keep the env so we can instantiate the Provider def initialize(self, values): ''' @@ -116,40 +122,27 @@ class OVirtLinkedService(Service): Note that we check them throught FROM variables, that already has been initialized by __init__ method of base class, before invoking this. ''' - - # We don't need to check anything, bat because this is a sample, we do - # As in provider, we receive values only at new Service creation, - # so we only need to validate params if values is not None if values is not None: - if self.colour.value == 'nonsense': - raise Service.ValidationException('The selected colour is invalid!!!') - - - # Services itself are non testeable right now, so we don't even have - # to provide one!!! - + length = int(self.lenName.value) + if len(self.baseName.value) + length > 15: + raise Service.ValidationException(_('The length of basename plus length must not be greater than 15')) + if self.baseName.value.isdigit(): + raise Service.ValidationException(_('The machine name can\'t be only numbers')) - # Congratulations!!!, the needed part of your first simple service is done! - # Now you can go to administration panel, and check it - # - # From now onwards, we implement our own methods, that will be used by, - # for example, services derived from this provider - - def getColour(self): - ''' - Simply returns colour, for deployed user services. + machines = self.parent().getMachines() + vals = [] + for m in machines: + vals.append( gui.choiceItem( m['id'], m['name'] )) + self.machine.setValues(vals) + + clusters = self.parent().getClusters() + vals = [] + for c in clusters: + vals.append( gui.choiceItem(c['id'], c['name'] ) ) + self.cluster.setValues(vals) + + + def initGui(self): + self.ov.value = self.parent().serialize() + self.ev.value = self.parent().env().key() - Remember that choiceField.value returns the id part of the ChoiceItem - ''' - return self.colour.value - - def getPassw(self): - ''' - Simply returns passwd, for deloyed user services - ''' - return self.passw.value - - def getBaseName(self): - ''' - ''' - return self.baseName.value diff --git a/server/src/uds/services/OVirt/OVirtProvider.py b/server/src/uds/services/OVirt/OVirtProvider.py index 35834647..c00548fa 100644 --- a/server/src/uds/services/OVirt/OVirtProvider.py +++ b/server/src/uds/services/OVirt/OVirtProvider.py @@ -118,9 +118,120 @@ class Provider(ServiceProvider): self._api = None def testConnection(self): - api = self.__getApi() - return api.test() + ''' + 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): + ''' + Obtains the list of machines inside oVirt. + Machines starting with UDS are filtered out + + 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): + ''' + 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' + ''' + + return self.__getApi().getClusters(force) + + def getClusterInfo(self, clusterId, force = False): + ''' + Obtains the cluster info + + Args: + 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): + ''' + Obtains the datacenter info + + Args: + 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' + 'storage_type' -> ('isisi', 'nfs', ....) + 'storage_format' -> ('v1', v2') + 'description' + 'storage' -> array of dictionaries, with: + 'id' -> Storage id + 'name' -> Storage name + 'type' -> Storage type ('data', 'iso') + '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): + ''' + Obtains the datacenter info + + Args: + 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 + 'type' -> Storage type ('data', 'iso') + '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) + @staticmethod def test(env, data): diff --git a/server/src/uds/services/OVirt/client/oVirtClient.py b/server/src/uds/services/OVirt/client/oVirtClient.py index a37a626d..93b5b2f0 100644 --- a/server/src/uds/services/OVirt/client/oVirtClient.py +++ b/server/src/uds/services/OVirt/client/oVirtClient.py @@ -92,7 +92,6 @@ class Client(object): get data from cache and, if valid, return this. Returns - An array of dictionaries, containing: 'name' 'id' @@ -124,6 +123,60 @@ class Client(object): finally: lock.release() + 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 } + + # Updates cache info for every single cluster + 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): ''' Obtains the cluster info @@ -138,7 +191,6 @@ class Client(object): A dictionary with following values 'name' 'id' - 'description' 'datacenter_id' ''' clKey = self.__getKey('o-cluster'+clusterId) @@ -154,7 +206,12 @@ class Client(object): c = api.clusters.get(id=clusterId) - res = { 'name' : c.get_name(), 'id' : c.get_id(), 'datacenter_id' : c.get_data_center().get_id() } + 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 } self._cache.put(clKey, res, Client.CACHE_TIME_HIGH) return res finally: