mirror of
https://github.com/dkmstr/openuds.git
synced 2024-12-22 13:34:04 +03:00
Starting kvm (oVirt based) part
This commit is contained in:
parent
a4c1f6af8f
commit
8a04db28e9
203
server/src/uds/migrations/0007_auto__add_field_config_long.py
Normal file
203
server/src/uds/migrations/0007_auto__add_field_config_long.py
Normal file
@ -0,0 +1,203 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding field 'Config.long'
|
||||
db.add_column('uds_configuration', 'long',
|
||||
self.gf('django.db.models.fields.BooleanField')(default=False),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'Config.long'
|
||||
db.delete_column('uds_configuration', 'long')
|
||||
|
||||
|
||||
models = {
|
||||
'uds.authenticator': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'Authenticator'},
|
||||
'comments': ('django.db.models.fields.TextField', [], {'default': "''"}),
|
||||
'data': ('django.db.models.fields.TextField', [], {'default': "''"}),
|
||||
'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
|
||||
'priority': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'})
|
||||
},
|
||||
'uds.cache': {
|
||||
'Meta': {'object_name': 'Cache', 'db_table': "'uds_utility_cache'"},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'key': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
|
||||
'owner': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
|
||||
'validity': ('django.db.models.fields.IntegerField', [], {'default': '60'}),
|
||||
'value': ('django.db.models.fields.TextField', [], {'default': "''"})
|
||||
},
|
||||
'uds.config': {
|
||||
'Meta': {'unique_together': "(('section', 'key'),)", 'object_name': 'Config', 'db_table': "'uds_configuration'"},
|
||||
'crypt': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||
'long': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'section': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'value': ('django.db.models.fields.TextField', [], {'default': "''"})
|
||||
},
|
||||
'uds.delayedtask': {
|
||||
'Meta': {'object_name': 'DelayedTask'},
|
||||
'execution_delay': ('django.db.models.fields.PositiveIntegerField', [], {}),
|
||||
'execution_time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'insert_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'instance': ('django.db.models.fields.TextField', [], {}),
|
||||
'tag': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}),
|
||||
'type': ('django.db.models.fields.CharField', [], {'max_length': '128'})
|
||||
},
|
||||
'uds.deployedservice': {
|
||||
'Meta': {'object_name': 'DeployedService', 'db_table': "'uds__deployed_service'"},
|
||||
'assignedGroups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'deployedServices'", 'symmetrical': 'False', 'db_table': "'uds__ds_grps'", 'to': "orm['uds.Group']"}),
|
||||
'cache_l1_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'cache_l2_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'comments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
|
||||
'current_pub_revision': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'initial_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'max_srvs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
|
||||
'osmanager': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.OSManager']"}),
|
||||
'service': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deployedServices'", 'null': 'True', 'to': "orm['uds.Service']"}),
|
||||
'state': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1', 'db_index': 'True'}),
|
||||
'state_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}),
|
||||
'transports': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'deployedServices'", 'symmetrical': 'False', 'db_table': "'uds__ds_trans'", 'to': "orm['uds.Transport']"})
|
||||
},
|
||||
'uds.deployedservicepublication': {
|
||||
'Meta': {'ordering': "('publish_date',)", 'object_name': 'DeployedServicePublication', 'db_table': "'uds__deployed_service_pub'"},
|
||||
'data': ('django.db.models.fields.TextField', [], {'default': "''"}),
|
||||
'deployed_service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publications'", 'to': "orm['uds.DeployedService']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'publish_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
|
||||
'revision': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
|
||||
'state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1', 'db_index': 'True'}),
|
||||
'state_date': ('django.db.models.fields.DateTimeField', [], {})
|
||||
},
|
||||
'uds.group': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('manager', 'name'),)", 'object_name': 'Group'},
|
||||
'comments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'manager': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['uds.Authenticator']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
|
||||
'state': ('django.db.models.fields.CharField', [], {'default': "'A'", 'max_length': '1', 'db_index': 'True'}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'groups'", 'symmetrical': 'False', 'to': "orm['uds.User']"})
|
||||
},
|
||||
'uds.network': {
|
||||
'Meta': {'object_name': 'Network'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
|
||||
'net_end': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
|
||||
'net_start': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
|
||||
'transports': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'networks'", 'symmetrical': 'False', 'db_table': "'uds_net_trans'", 'to': "orm['uds.Transport']"})
|
||||
},
|
||||
'uds.osmanager': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'OSManager'},
|
||||
'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
|
||||
'data': ('django.db.models.fields.TextField', [], {'default': "''"}),
|
||||
'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
|
||||
},
|
||||
'uds.provider': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'Provider'},
|
||||
'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
|
||||
'data': ('django.db.models.fields.TextField', [], {'default': "''"}),
|
||||
'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
|
||||
},
|
||||
'uds.scheduler': {
|
||||
'Meta': {'object_name': 'Scheduler'},
|
||||
'frecuency': ('django.db.models.fields.PositiveIntegerField', [], {'default': '86400'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_execution': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
|
||||
'next_execution': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)', 'db_index': 'True'}),
|
||||
'owner_server': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'db_index': 'True'}),
|
||||
'state': ('django.db.models.fields.CharField', [], {'default': "'X'", 'max_length': '1', 'db_index': 'True'})
|
||||
},
|
||||
'uds.service': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('provider', 'name'),)", 'object_name': 'Service'},
|
||||
'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
|
||||
'data': ('django.db.models.fields.TextField', [], {'default': "''"}),
|
||||
'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'provider': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'services'", 'to': "orm['uds.Provider']"})
|
||||
},
|
||||
'uds.storage': {
|
||||
'Meta': {'object_name': 'Storage'},
|
||||
'attr1': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '64', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
|
||||
'data': ('django.db.models.fields.TextField', [], {'default': "''"}),
|
||||
'key': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
|
||||
'owner': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
|
||||
},
|
||||
'uds.transport': {
|
||||
'Meta': {'ordering': "('name',)", 'object_name': 'Transport'},
|
||||
'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
|
||||
'data': ('django.db.models.fields.TextField', [], {'default': "''"}),
|
||||
'data_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
|
||||
'nets_positive': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'priority': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'})
|
||||
},
|
||||
'uds.uniqueid': {
|
||||
'Meta': {'ordering': "('-seq',)", 'unique_together': "(('basename', 'seq'),)", 'object_name': 'UniqueId'},
|
||||
'assigned': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
|
||||
'basename': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'owner': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'db_index': 'True'}),
|
||||
'seq': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'})
|
||||
},
|
||||
'uds.user': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('manager', 'name'),)", 'object_name': 'User'},
|
||||
'comments': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_access': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}),
|
||||
'manager': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'users'", 'to': "orm['uds.Authenticator']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
|
||||
'real_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'staff_member': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'state': ('django.db.models.fields.CharField', [], {'max_length': '1', 'db_index': 'True'})
|
||||
},
|
||||
'uds.userpreference': {
|
||||
'Meta': {'object_name': 'UserPreference'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'module': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'preferences'", 'to': "orm['uds.User']"}),
|
||||
'value': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
|
||||
},
|
||||
'uds.userservice': {
|
||||
'Meta': {'ordering': "('creation_date',)", 'object_name': 'UserService', 'db_table': "'uds__user_service'"},
|
||||
'cache_level': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0', 'db_index': 'True'}),
|
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
|
||||
'data': ('django.db.models.fields.TextField', [], {'default': "''"}),
|
||||
'deployed_service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'userServices'", 'to': "orm['uds.DeployedService']"}),
|
||||
'friendly_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'in_use': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'in_use_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1972, 7, 1, 0, 0)'}),
|
||||
'os_state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1'}),
|
||||
'publication': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'userServices'", 'null': 'True', 'to': "orm['uds.DeployedServicePublication']"}),
|
||||
'state': ('django.db.models.fields.CharField', [], {'default': "'P'", 'max_length': '1', 'db_index': 'True'}),
|
||||
'state_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'unique_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'db_index': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'userServices'", 'null': 'True', 'blank': 'True', 'to': "orm['uds.User']"})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['uds']
|
@ -1438,6 +1438,7 @@ class Config(models.Model):
|
||||
key = models.CharField(max_length=64)
|
||||
value = models.TextField(default = '')
|
||||
crypt = models.BooleanField(default = False)
|
||||
long = models.BooleanField(default = False)
|
||||
|
||||
class Meta:
|
||||
'''
|
||||
|
355
server/src/uds/services/OVirt/OVirtLinkedDeployment.py
Normal file
355
server/src/uds/services/OVirt/OVirtLinkedDeployment.py
Normal file
@ -0,0 +1,355 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# 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,
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
|
||||
from uds.core.services import UserDeployment
|
||||
from uds.core.util.State import State
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
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 five seconds by default (for task methods)
|
||||
suggestedTime = 5
|
||||
|
||||
# Serializable needed methods
|
||||
def marshal(self):
|
||||
'''
|
||||
Does nothing right here, we will use envoronment storage in this sample
|
||||
'''
|
||||
return ''
|
||||
|
||||
def unmarshal(self, str_):
|
||||
'''
|
||||
Does nothing here also, all data are keeped at environment storage
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
def getName(self):
|
||||
'''
|
||||
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
|
||||
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)
|
||||
'''
|
||||
name = self.storage().readData('name')
|
||||
if name is None:
|
||||
name = self.nameGenerator().get( self.service().getBaseName()
|
||||
+ '-' + self.service().getColour(), 3 )
|
||||
# Store value for persistence
|
||||
self.storage().saveData('name', name)
|
||||
|
||||
return 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
|
||||
IP services, so will probable needed in every service that you will create.
|
||||
:note: This IP is the IP of the "consumed service", so the transport can
|
||||
access it.
|
||||
'''
|
||||
self.storage().saveData('ip', str(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.
|
||||
'''
|
||||
mac = self.storage().readData('mac')
|
||||
if mac is None:
|
||||
mac = self.macGenerator().get( '00:00:00:00:00:00-00:FF:FF:FF:FF:FF' )
|
||||
self.storage().saveData('mac', mac)
|
||||
return 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.
|
||||
|
||||
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
|
||||
|
||||
'''
|
||||
ip = self.storage().readData('ip')
|
||||
if ip is None:
|
||||
ip = '192.168.0.34' # Sample IP for testing purposses only
|
||||
return ip
|
||||
|
||||
def setReady(self):
|
||||
'''
|
||||
This is a task method. As that, the expected return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
|
||||
The method is invoked whenever a machine is provided to an user, right
|
||||
before presenting it (via transport rendering) to the user.
|
||||
|
||||
This method exist for this kind of situations (i will explain it with a
|
||||
sample)
|
||||
|
||||
Imagine a Service tree (Provider, Service, ...) for virtual machines.
|
||||
This machines will get created by the UserDeployment implementation, but,
|
||||
at some time, the machine can be put at in an state (suspend, shut down)
|
||||
that will make the transport impossible to connect with it.
|
||||
|
||||
This method, in this case, will check the state of the machine, and if
|
||||
it is "ready", that is, powered on and accesible, it will return
|
||||
"State.FINISHED". If the machine is not accesible (has ben erased, for
|
||||
example), it will return "State.ERROR" and store a reason of error so UDS
|
||||
can ask for it and present this information to the Administrator.
|
||||
|
||||
If the machine powered off, or suspended, or any other state that is not
|
||||
directly usable but can be put in an usable state, it will return
|
||||
"State.RUNNING", and core will use checkState to see when the operation
|
||||
has finished.
|
||||
|
||||
I hope this sample is enough to explain the use of this method..
|
||||
'''
|
||||
|
||||
# In our case, the service is always ready
|
||||
return State.FINISHED
|
||||
|
||||
def deployForUser(self, user):
|
||||
'''
|
||||
Deploys an service instance for an user.
|
||||
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
|
||||
The user parameter is not realy neded, but provided. It indicates the
|
||||
Database User Object (see py:mod:`uds.modules`) to which this deployed
|
||||
user service will be assigned to.
|
||||
|
||||
This method will get called whenever a new deployed service for an user
|
||||
is needed. This will give this class the oportunity to create
|
||||
a service that is assigned to an user.
|
||||
|
||||
The way of using this method is as follows:
|
||||
|
||||
If the service gets created in "one step", that is, before the return
|
||||
of this method, the consumable service for the user gets created, it
|
||||
will return "State.FINISH".
|
||||
If the service needs more steps (as in this case), we will return
|
||||
"State.RUNNING", and if it has an error, it wil return "State.ERROR" and
|
||||
store an error string so administration interface can show it.
|
||||
|
||||
We do not use user for anything, as in most cases will be.
|
||||
'''
|
||||
import random
|
||||
|
||||
self.storage().saveData('count', '0')
|
||||
|
||||
# random fail
|
||||
if random.randint(0, 9) == 9:
|
||||
self.storage().saveData('error', 'Random error at deployForUser :-)')
|
||||
return State.ERROR
|
||||
|
||||
return State.RUNNING
|
||||
|
||||
|
||||
def checkState(self):
|
||||
'''
|
||||
Our deployForUser method will initiate the consumable service deployment,
|
||||
but will not finish it.
|
||||
|
||||
So in our sample, we will only check if a number reaches 5, and if so
|
||||
return that we have finished, else we will return that we are working
|
||||
on it.
|
||||
|
||||
One deployForUser returns State.RUNNING, this task will get called until
|
||||
checkState returns State.FINISHED.
|
||||
|
||||
Also, we will make the publication fail one of every 10 calls to this
|
||||
method.
|
||||
|
||||
Note: Destroying, canceling and deploying for cache also makes use of
|
||||
this method, so you must keep the info of that you are checking if you
|
||||
need it.
|
||||
In our case, destroy is 1-step action so this will no get called while
|
||||
destroying, and cancel will simply invoke destroy
|
||||
'''
|
||||
import random
|
||||
|
||||
count = int(self.storage().readData('count')) + 1
|
||||
# Count is always a valid value, because this method will never get
|
||||
# called before deployForUser, deployForCache, destroy or cancel.
|
||||
# In our sample, we only use checkState in case of deployForUser,
|
||||
# so at first call count will be 0.
|
||||
if count >= 5:
|
||||
return State.FINISHED
|
||||
|
||||
# random fail
|
||||
if random.randint(0, 9) == 9:
|
||||
self.storage().saveData('error', 'Random error at checkState :-)')
|
||||
return State.ERROR
|
||||
|
||||
self.storage().saveData('count', str(count))
|
||||
return State.RUNNING
|
||||
|
||||
def finish(self):
|
||||
'''
|
||||
Invoked when the core notices that the deployment of a service has finished.
|
||||
(No matter wether it is for cache or for an user)
|
||||
|
||||
This gives the oportunity to make something at that moment.
|
||||
:note: You can also make these operations at checkState, this is really
|
||||
not needed, but can be provided (default implementation of base class does
|
||||
nothing)
|
||||
'''
|
||||
# Note that this is not really needed, is just a sample of storage use
|
||||
self.storage().remove('count')
|
||||
|
||||
def assignToUser(self, user):
|
||||
'''
|
||||
This method is invoked whenever a cache item gets assigned to an user.
|
||||
This gives the User Deployment an oportunity to do whatever actions
|
||||
are required so the service puts at a correct state for using by a service.
|
||||
|
||||
In our sample, the service is always ready, so this does nothing.
|
||||
|
||||
This is not a task method. All level 1 cache items can be diretly
|
||||
assigned to an user with no more work needed, but, if something is needed,
|
||||
here you can do whatever you need
|
||||
'''
|
||||
pass
|
||||
|
||||
def userLoggedIn(self, user):
|
||||
'''
|
||||
This method must be available so os managers can invoke it whenever
|
||||
an user get logged into a service.
|
||||
|
||||
Default implementation does nothing, so if you are going to do nothing,
|
||||
you don't need to implement it.
|
||||
|
||||
The responability of notifying it is of os manager actor, and it's
|
||||
directly invoked by os managers (right now, linux os manager and windows
|
||||
os manager)
|
||||
|
||||
The user provided is just an string, that is provided by actor.
|
||||
'''
|
||||
# We store the value at storage, but never get used, just an example
|
||||
self.storage().saveData('user', user)
|
||||
|
||||
def userLoggedOut(self, user):
|
||||
'''
|
||||
This method must be available so os managers can invoke it whenever
|
||||
an user get logged out if a service.
|
||||
|
||||
Default implementation does nothing, so if you are going to do nothing,
|
||||
you don't need to implement it.
|
||||
|
||||
The responability of notifying it is of os manager actor, and it's
|
||||
directly invoked by os managers (right now, linux os manager and windows
|
||||
os manager)
|
||||
|
||||
The user provided is just an string, that is provided by actor.
|
||||
'''
|
||||
# We do nothing more that remove the user
|
||||
self.storage().remove('user')
|
||||
|
||||
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.storage().readData('error') or 'No error'
|
||||
|
||||
def destroy(self):
|
||||
'''
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
|
||||
Invoked for destroying a deployed service
|
||||
Do whatever needed here, as deleting associated data if needed (i.e. a copy of the machine, snapshots, etc...)
|
||||
@return: State.FINISHED if no more checks/steps for deployment are needed, State.RUNNING if more steps are needed (steps checked using checkState)
|
||||
'''
|
||||
return State.FINISHED
|
||||
|
||||
def cancel(self):
|
||||
'''
|
||||
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 State.FINISHED
|
||||
|
187
server/src/uds/services/OVirt/OVirtLinkedService.py
Normal file
187
server/src/uds/services/OVirt/OVirtLinkedService.py
Normal file
@ -0,0 +1,187 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# 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,
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
|
||||
from django.utils.translation import ugettext_noop as translatable, ugettext as _
|
||||
from uds.core.services import Service
|
||||
from OVirtPublication import OVirtPublication
|
||||
from OVirtLinkedDeployment import OVirtLinkedDeployment
|
||||
|
||||
from uds.core.ui import gui
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class OVirtLinkedService(Service):
|
||||
'''
|
||||
Basic service, the first part (variables) include the description of the service.
|
||||
|
||||
Remember to fill all variables needed, but at least you must define:
|
||||
* typeName
|
||||
* typeType
|
||||
* typeDescription
|
||||
* iconFile (defaults to service.png)
|
||||
* publicationType, type of publication in case it needs publication.
|
||||
If this is not provided, core will assume that the service do not
|
||||
needs publishing.
|
||||
* deployedType, type of deployed user service. Do not forget this!!!
|
||||
|
||||
The rest of them can be ommited, but its recommended that you fill all
|
||||
declarations shown in this sample (that in fact, are all)
|
||||
|
||||
This description informs the core what this service really provides,
|
||||
and how this is done. Look at description of class variables for more
|
||||
information.
|
||||
|
||||
'''
|
||||
#: Name to show the administrator. This string will be translated BEFORE
|
||||
#: sending it to administration interface, so don't forget to
|
||||
#: mark it as translatable (using ugettext_noop)
|
||||
typeName = translatable('Sample Service One')
|
||||
#: Type used internally to identify this provider
|
||||
typeType = 'SampleService1'
|
||||
#: Description shown at administration interface for this provider
|
||||
typeDescription = translatable('Sample (and dummy) service ONE')
|
||||
#: 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 translatable (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
|
||||
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!
|
||||
usesCache = False
|
||||
#: Tooltip shown to user when this item is pointed at admin interface, none
|
||||
#: because we don't use it
|
||||
cacheTooltip = translatable('None')
|
||||
#: 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 = False
|
||||
#: Tooltip shown to user when this item is pointed at admin interface, None
|
||||
#: also because we don't use it
|
||||
cacheTooltip_L2 = translatable('None')
|
||||
|
||||
#: If the service needs a s.o. manager (managers are related to agents
|
||||
#: provided by services itselfs, i.e. virtual machines with actors)
|
||||
needsManager = 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
|
||||
publicationType = None
|
||||
#: 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"
|
||||
|
||||
colour = gui.ChoiceField(order = 1,
|
||||
label = translatable('Colour'),
|
||||
tooltip = translatable('Colour of the field'),
|
||||
# In this case, the choice can have none value selected by default
|
||||
required = True,
|
||||
values = [ gui.choiceItem('red', 'Red'),
|
||||
gui.choiceItem('green', 'Green'),
|
||||
gui.choiceItem('blue', 'Blue'),
|
||||
gui.choiceItem('nonsense', 'Blagenta')
|
||||
],
|
||||
defvalue = '1' # Default value is the ID of the choicefield
|
||||
)
|
||||
|
||||
passw = gui.PasswordField(order = 2,
|
||||
label = translatable('Password'),
|
||||
tooltip = translatable('Password for testing purposes'),
|
||||
required = True,
|
||||
defvalue = '1234' #: Default password are nonsense?? :-)
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
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.
|
||||
'''
|
||||
|
||||
# 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!!!
|
||||
|
||||
|
||||
# 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.
|
||||
|
||||
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
|
166
server/src/uds/services/OVirt/OVirtProvider.py
Normal file
166
server/src/uds/services/OVirt/OVirtProvider.py
Normal file
@ -0,0 +1,166 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# 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,
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
Created on Jun 22, 2012
|
||||
|
||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
|
||||
from django.utils.translation import ugettext_noop as translatable, ugettext as _
|
||||
from uds.core.services import ServiceProvider
|
||||
from OVirtLinkedService import OVirtLinkedService
|
||||
from uds.core.ui import gui
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
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
|
||||
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 translatable (using ugettext_noop)
|
||||
typeName = translatable('oVirt Platform Provider')
|
||||
#: Type used internally to identify this provider
|
||||
typeType = 'oVirtPlatform'
|
||||
#: Description shown at administration interface for this provider
|
||||
typeDescription = translatable('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 translatable (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
|
||||
# Description, that is a short description that the admin gives to this provider
|
||||
# Now we are going to add a few fields that we need to use this provider
|
||||
# Remember that these are "dummy" fields, that in fact are not required
|
||||
# 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)
|
||||
port = gui.NumericField(length=5, label = _('Port'), defvalue = '443', order = 2, tooltip = _('VMWare VC Server Port (usually 443)'), required = True)
|
||||
username = gui.TextField(length=32, label = _('Username'), order = 3, tooltip = _('User with valid privileges on VC'), required = True)
|
||||
password = gui.PasswordField(lenth=32, label = _('Password'), order = 4, tooltip = _('Password of the user of the VC'), 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 = '00:50:56:00:00:00-00:50:56:3F:FF:FF', order = 6, rdonly = True,
|
||||
tooltip = _('Range of valids macs for created machines'), required = True)
|
||||
|
||||
|
||||
|
||||
# There is more fields type, but not here the best place to cover it
|
||||
def initialize(self, values = None):
|
||||
'''
|
||||
We will use the "autosave" feature for form fields, that is more than
|
||||
enought for most providers. (We simply need to store data provided by user
|
||||
and, maybe, initialize some kind of connection with this values).
|
||||
|
||||
Normally provider values are rally used at sevice level, cause we never
|
||||
instantiate nothing except a service from a provider.
|
||||
'''
|
||||
|
||||
# If you say meth is alive, you are wrong!!! (i guess..)
|
||||
# values are only passed from administration client. Internals
|
||||
# instantiations are always empty.
|
||||
#if values is not None and self.methAlive.isTrue():
|
||||
# raise ServiceProvider.ValidationException(_('Methuselah is not alive!!! :-)'))
|
||||
|
||||
# Marshal and unmarshal are defaults ones, also enought
|
||||
|
||||
# As we use "autosave" fields feature, dictValues is also provided by
|
||||
# base class so we don't have to mess with all those things...
|
||||
|
||||
@staticmethod
|
||||
def test(env, data):
|
||||
'''
|
||||
Create your test method here so the admin can push the "check" button
|
||||
and this gets executed.
|
||||
Args:
|
||||
env: environment passed for testing (temporal environment passed)
|
||||
|
||||
data: data passed for testing (data obtained from the form
|
||||
definition)
|
||||
|
||||
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..
|
||||
|
||||
In this case, wi well do nothing more that use the provider params
|
||||
|
||||
Note also that this is an static method, that will be invoked using
|
||||
the admin user provided data via administration client, and a temporary
|
||||
environment that will be erased after invoking this method
|
||||
'''
|
||||
#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
|
||||
# return [False, str(e)]
|
||||
#except Exception as e:
|
||||
# logger.exception("Exception caugth!!!")
|
||||
# return [False, str(e)]
|
||||
#return [True, _('Nothing tested, but all went fine..')]
|
||||
return [True, _('Connection test successful')]
|
||||
|
||||
# Congratulations!!!, the needed part of your first simple provider 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 host(self):
|
||||
'''
|
||||
Sample method, in fact in this we just return
|
||||
the value of host field, that is an string
|
||||
'''
|
||||
return self.remoteHost.value
|
||||
|
272
server/src/uds/services/OVirt/OVirtPublication.py
Normal file
272
server/src/uds/services/OVirt/OVirtPublication.py
Normal file
@ -0,0 +1,272 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# 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,
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from uds.core.services import Publication
|
||||
from uds.core.util.State import State
|
||||
from datetime import datetime
|
||||
import logging
|
||||
|
||||
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")
|
||||
'''
|
||||
|
||||
suggestedTime = 5 #: 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.
|
||||
|
||||
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 = 'test'
|
||||
self._reason = '' # No error, no reason for it
|
||||
self._number = 1
|
||||
|
||||
def marshal(self):
|
||||
'''
|
||||
returns data from an instance of Sample Publication serialized
|
||||
'''
|
||||
return '\t'.join( [self._name, self._reason, str(self._number)] )
|
||||
|
||||
def unmarshal(self, data):
|
||||
'''
|
||||
deserializes the data and loads it inside instance.
|
||||
'''
|
||||
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])
|
||||
|
||||
|
||||
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.
|
||||
'''
|
||||
self._number = 5
|
||||
self._reason = ''
|
||||
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...
|
||||
'''
|
||||
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:
|
||||
return State.FINISHED
|
||||
else:
|
||||
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
|
||||
'''
|
||||
import string
|
||||
import random
|
||||
# Make simply a random string
|
||||
self._name = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10))
|
||||
|
||||
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 they return State.ERROR
|
||||
|
||||
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..
|
||||
|
||||
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
|
||||
|
||||
|
||||
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.
|
||||
'''
|
||||
return self.destroy()
|
||||
|
||||
# Here ends the publication needed methods.
|
||||
# Methods provided below are specific for this publication
|
||||
# and will be used by user deployments that uses this kind of publication
|
||||
|
||||
def getBaseName(self):
|
||||
'''
|
||||
This sample method (just for this sample publication), provides
|
||||
the name generater for this publication. This is just a sample, and
|
||||
this will do the work
|
||||
'''
|
||||
return self._name
|
44
server/src/uds/services/OVirt/__init__.py
Normal file
44
server/src/uds/services/OVirt/__init__.py
Normal file
@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# 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,
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
Sample Service module.
|
||||
|
||||
This package simply shows how a new service can be implemented.
|
||||
|
||||
|
||||
The first thing to do in every package that is a module is register the
|
||||
class that is responsible of providing the module with the system.
|
||||
|
||||
For this, we must simply import the class at __init__, UDS will take care
|
||||
of the rest
|
||||
'''
|
||||
|
||||
from OVirtProvider import Provider
|
||||
|
BIN
server/src/uds/services/OVirt/provider.png
Normal file
BIN
server/src/uds/services/OVirt/provider.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
server/src/uds/services/OVirt/service.png
Normal file
BIN
server/src/uds/services/OVirt/service.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
Loading…
Reference in New Issue
Block a user