OpenStack working (initially)

This commit is contained in:
Adolfo Gómez García 2016-03-07 18:28:32 +01:00
parent c45833c252
commit 9f4ef20dc1
19 changed files with 437 additions and 284 deletions

View File

@ -1,6 +1,6 @@
#!/bin/bash
VERSION=1.9.0
VERSION=`cat ../../VERSION`
RELEASE=1
top=`pwd`

View File

@ -1,3 +1,15 @@
udsactor (2.0.0) stable; urgency=medium
* Upgrade for 2.0.0
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 01 Mar 2016 03:39:21 +0100
udsactor (1.9.1) stable; urgency=medium
* Upgrade for 1.9.1
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 01 Mar 2016 03:19:21 +0100
udsactor (1.9.0) stable; urgency=medium
* Upgrade for 1.9.0 (fixed package version)

View File

@ -1,3 +1,3 @@
udsactor_1.9.0_all.deb admin optional
udsactor-xrdp_1.9.0_all.deb x11 optional
udsactor-nx_1.9.0_all.deb x11 optional
udsactor-nx_2.0.0_all.deb x11 optional
udsactor-xrdp_2.0.0_all.deb x11 optional
udsactor_2.0.0_all.deb admin optional

View File

@ -2,7 +2,13 @@ udsclient (2.0.0) stable; urgency=medium
* Release upgrade
-- Adolfo Gómez García <agomez@virtualcable.es> Fri, 19 Feb 2015 09:33:18 +0200
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 01 Mar 2016 09:33:18 +0100
udsclient (1.9.1) stable; urgency=medium
* Minor fixes & making version match UDS version
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 01 Mar 2016 03:02:37 +0100
udsclient (1.9.0) stable; urgency=medium

View File

@ -44,7 +44,7 @@ import threading
import time
import logging
__updated__ = '2016-02-01'
__updated__ = '2016-03-07'
logger = logging.getLogger(__name__)
@ -116,8 +116,7 @@ class DelayedTaskRunner(object):
if taskInstance is not None:
logger.debug('Executing delayedTask:>{0}<'.format(task))
env = Environment.getEnvForType(taskInstance.__class__)
taskInstance.setEnv(env)
taskInstance.env = Environment.getEnvForType(taskInstance.__class__)
DelayedTaskThread(taskInstance).start()
def __insert(self, instance, delay, tag):

View File

@ -33,8 +33,11 @@
from __future__ import unicode_literals
from . import action
from . import common
from . import group
from . import process
from . import publication
from . import userService
from . import servicePool
from . import task
from . import group
from . import userService

View File

@ -85,7 +85,7 @@ class ServiceCacheUpdater(Job):
sp.userServices.update() # Cleans cached queries
# If this deployedService don't have a publication active and needs it, ignore it
if sp.activePublication() is None and sp.service.getInstance().publicationType is not None:
logger.debug('Needs publication but do not have one, cache test ignored')
logger.debug('{} Needs publication but do not have one, cache test ignored'.format(sp))
continue
# If it has any running publication, do not generate cache anymore
if sp.publications.filter(state=State.PREPARING).count() > 0:

View File

@ -30,13 +30,13 @@
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
# pylint: disable=protected-access
from __future__ import unicode_literals
from django.core.management.base import BaseCommand, CommandError
from django.core.management.base import BaseCommand # , CommandError
from optparse import make_option
from django.conf import settings
from django.utils.daemonize import become_daemon
from uds.core.managers.TaskManager import TaskManager
from uds.core.util.Config import GlobalConfig
import logging
@ -54,6 +54,41 @@ PID_FILE = 'taskmanager.pid'
def getPidFile():
return settings.BASE_DIR + '/' + PID_FILE
# become_daemon seems te be removed on django 1.9
# This is a copy of posix version from django 1.8
buffering = int(six.PY3)
def become_daemon(our_home_dir='.', out_log='/dev/null',
err_log='/dev/null', umask=0o022):
"Robustly turn into a UNIX daemon, running in our_home_dir."
# First fork
try:
if os.fork() > 0:
sys.exit(0) # kill off parent
except OSError as e:
sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror))
sys.exit(1)
os.setsid()
os.chdir(our_home_dir)
os.umask(umask)
# Second fork
try:
if os.fork() > 0:
os._exit(0)
except OSError as e:
sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
os._exit(1)
si = open('/dev/null', 'r')
so = open(out_log, 'a+', buffering)
se = open(err_log, 'a+', buffering)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# Set custom file descriptors so that they get proper buffering.
sys.stdout, sys.stderr = so, se
class Command(BaseCommand):
args = "None"
@ -82,7 +117,7 @@ class Command(BaseCommand):
pid = None
try:
pid = int(file(getPidFile(), 'r').readline())
pid = int(open(getPidFile(), 'r').readline())
except Exception:
pid = None

View File

@ -40,7 +40,7 @@ from uds.core.ui import gui
import logging
__updated__ = '2016-03-04'
__updated__ = '2016-03-07'
logger = logging.getLogger(__name__)
@ -161,7 +161,7 @@ class LiveService(Service):
name: Name (sanitized) of the machine
comments: Comments for machine
templateId: Id of the template to deploy from
displayType: 'vnc' or 'spice'. Display to use ad oVirt admin interface
displayType: 'vnc' or 'spice'. Display to use ad OpenNebula admin interface
memoryMB: Memory requested for machine, in MB
guaranteedMB: Minimum memory guaranteed for this machine
@ -198,7 +198,7 @@ class LiveService(Service):
def startMachine(self, machineId):
'''
Tries to start a machine. No check is done, it is simply requested to oVirt.
Tries to start a machine. No check is done, it is simply requested to OpenNebula.
This start also "resume" suspended/paused machines
@ -211,7 +211,7 @@ class LiveService(Service):
def stopMachine(self, machineId):
'''
Tries to start a machine. No check is done, it is simply requested to oVirt
Tries to start a machine. No check is done, it is simply requested to OpenNebula
Args:
machineId: Id of the machine
@ -222,7 +222,7 @@ class LiveService(Service):
def suspendMachine(self, machineId):
'''
Tries to start a machine. No check is done, it is simply requested to oVirt
Tries to start a machine. No check is done, it is simply requested to OpenNebula
Args:
machineId: Id of the machine
@ -233,7 +233,7 @@ class LiveService(Service):
def removeMachine(self, machineId):
'''
Tries to delete a machine. No check is done, it is simply requested to oVirt
Tries to delete a machine. No check is done, it is simply requested to OpenNebula
Args:
machineId: Id of the machine

View File

@ -39,7 +39,7 @@ from . import openStack
import pickle
import logging
__updated__ = '2016-02-26'
__updated__ = '2016-03-07'
logger = logging.getLogger(__name__)
@ -58,8 +58,13 @@ class LiveDeployment(UserDeployment):
provider of this elements.
The logic for managing ovirt deployments (user machines in this case) is here.
'''
_name = ''
_ip = ''
_mac = ''
_vmid = ''
_reason = ''
_queue = None
# : Recheck every six seconds by default (for task methods)
suggestedTime = 6
@ -171,12 +176,17 @@ class LiveDeployment(UserDeployment):
if self.cache.get('ready') == '1':
return State.FINISHED
state = self.service().getMachineState(self._vmid)
status = self.service().getMachineState(self._vmid)
if state == openStack.VmState.UNKNOWN:
if openStack.statusIsLost(status):
return self.__error('Machine is not available anymore')
self.service().startMachine()
if status == openStack.PAUSED:
self.service().resumeMachine(self._vmid)
elif status == openStack.STOPPED:
self.service().startMachine(self._vmId)
# Right now, we suppose the machine is ready
self.cache.put('ready', '1')
return State.FINISHED
@ -215,25 +225,25 @@ class LiveDeployment(UserDeployment):
def __initQueueForDeploy(self, forLevel2=False):
if forLevel2 is False:
self._queue = [opCreate, opStart, opFinish]
self._queue = [opCreate, opFinish]
else:
self._queue = [opCreate, opStart, opWait, opSuspend, opFinish]
self._queue = [opCreate, opWait, opSuspend, opFinish]
def __checkMachineState(self, chkState):
logger.debug('Checking that state of machine {} ({}) is {}'.format(self._vmid, self._name, chkState))
state = self.service().getMachineState(self._vmid)
status = self.service().getMachineState(self._vmid)
# If we want to check an state and machine does not exists (except in case that we whant to check this)
if state == openStack.VmState.UNKNOWN:
return self.__error('Machine not found')
if openStack.statusIsLost(status):
return self.__error('Machine not available')
ret = State.RUNNING
if type(chkState) is list:
if state in chkState:
if status in chkState:
ret = State.FINISHED
else:
if state == chkState:
if status == chkState:
ret = State.FINISHED
return ret
@ -340,16 +350,13 @@ class LiveDeployment(UserDeployment):
if self._vmid is None:
raise Exception('Can\'t create machine')
# Get IP & MAC (early stage)
self._mac, self._ip = self.service().getNetInfo(self._vmid)
def __remove(self):
'''
Removes a machine from system
'''
state = self.service().getMachineState(self._vmid)
status = self.service().getMachineState(self._vmid)
if state == openStack.VmState.UNKNOWN:
if openStack.statusIsLost(status):
raise Exception('Machine not found')
self.service().removeMachine(self._vmid)
@ -371,19 +378,25 @@ class LiveDeployment(UserDeployment):
'''
Checks the state of a deploy for an user or cache
'''
return self.__checkMachineState(openStack.VmState.ACTIVE)
ret = self.__checkMachineState(openStack.ACTIVE)
if ret == State.FINISHED:
# Get IP & MAC (early stage)
self._mac, self._ip = self.service().getNetInfo(self._vmid)
return ret
def __checkStart(self):
'''
Checks if machine has started
'''
return self.__checkMachineState(openStack.VmState.ACTIVE)
return self.__checkMachineState(openStack.ACTIVE)
def __checkSuspend(self):
'''
Check if the machine has suspended
'''
return self.__checkMachineState(openStack.VmState.SUSPENDED)
return self.__checkMachineState(openStack.SUSPENDED)
def __checkRemoved(self):
'''

View File

@ -35,10 +35,13 @@ 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 six
import logging
__updated__ = '2016-02-08'
__updated__ = '2016-03-07'
logger = logging.getLogger(__name__)
@ -48,6 +51,11 @@ class LivePublication(Publication):
'''
This class provides the publication of a oVirtLinkedService
'''
_name = ''
_reason = ''
_templateId = ''
_state = 'r'
_destroyAfter = 'n'
suggestedTime = 2 # : Suggested recheck time if publication is unfinished in seconds
@ -65,12 +73,13 @@ class LivePublication(Publication):
self._reason = ''
self._templateId = ''
self._state = 'r'
self._destroyAfter = 'n'
def marshal(self):
'''
returns data from an instance of Sample Publication serialized
'''
return '\t'.join(['v1', self._name, self._reason, self._templateId, self._state])
return '\t'.join(['v1', self._name, self._reason, self._templateId, self._state, self._destroyAfter])
def unmarshal(self, data):
'''
@ -79,7 +88,7 @@ class LivePublication(Publication):
logger.debug('Data: {0}'.format(data))
vals = data.split('\t')
if vals[0] == 'v1':
self._name, self._reason, self._templateId, self._state = vals[1:]
self._name, self._reason, self._templateId, self._state, self._destroyAfter = vals[1:]
def publish(self):
'''
@ -87,13 +96,16 @@ class LivePublication(Publication):
'''
self._name = self.service().sanitizeVmName('UDSP ' + self.dsName() + "-" + str(self.revision()))
self._reason = '' # No error, no reason for it
self._state = 'ok'
self._destroyAfter = 'n'
try:
self._templateId = self.service().makeTemplate(self._name)
res = self.service().makeTemplate(self._name)
logger.debug('Result: {}'.format(res))
self._templateId = res['id']
self._state = res['status']
except Exception as e:
self._state = 'error'
self._reason = str(e)
self._reason = 'Got error {}'.format(e)
return State.ERROR
return State.RUNNING
@ -105,11 +117,15 @@ class LivePublication(Publication):
if self._state == 'error':
return State.ERROR
if self._state == 'ok':
if self._state == 'available':
return State.FINISHED
self._state = 'ok'
return State.FINISHED
self._state = self.service().getTemplate(self._templateId)['status'] # For next check
if self._destroyAfter == 'y' and self._state == 'available':
return self.destroy()
return State.RUNNING
def finish(self):
'''
@ -139,11 +155,18 @@ class LivePublication(Publication):
State.FINISHED or State.ERROR.
'''
# We do not do anything else to destroy this instance of publication
if self._state == 'error':
return # Nothing to cancel
if self._state == 'creating':
self._destroyAfter = 'y'
return State.RUNNING
try:
self.service().removeTemplate(self._templateId)
except Exception as e:
self._state = 'error'
self._reason = str(e)
self._reason = six.text_type(e)
return State.ERROR
return State.FINISHED

View File

@ -39,10 +39,10 @@ from . import helpers
from uds.core.ui import gui
import six
import logging
__updated__ = '2016-03-04'
__updated__ = '2016-03-07'
logger = logging.getLogger(__name__)
@ -105,10 +105,10 @@ class LiveService(Service):
},
tooltip=_('Project for this service'), required=True
)
availabilityZone = gui.ChoiceField(label=_("Availability Zones"), order=3, tooltip=_('Service availability zones'), required=True)
availabilityZone = gui.ChoiceField(label=_("Availability Zones"), order=3, tooltip=_('Service availability zones'), required=True, rdonly=True)
volume = gui.ChoiceField(label=_("Volume"), order=4, tooltip=_('Base volume for service'), required=True)
# volumeType = gui.ChoiceField(label=_("Volume Type"), order=5, tooltip=_('Volume type for service'), required=True)
networks = gui.MultiChoiceField(label=_("Networks"), order=6, tooltip=_('Networks to attach to this service'), required=True)
network = gui.ChoiceField(label=_("Network"), order=6, tooltip=_('Network to attach to this service'), required=True)
flavor = gui.ChoiceField(label=_("Flavor"), order=7, tooltip=_('Flavor for service'), required=True)
securityGroups = gui.MultiChoiceField(label=_("Security Groups"), order=8, tooltip=_('Service security groups'), required=True)
@ -137,7 +137,7 @@ class LiveService(Service):
'''
We check here form values to see if they are valid.
Note that we check them throught FROM variables, that already has been
Note that we check them through FROM variables, that already has been
initialized by __init__ method of base class, before invoking this.
'''
if values is not None:
@ -158,7 +158,7 @@ class LiveService(Service):
Loads required values inside
'''
api = self.parent().api()
regions = [gui.choiceItem(r, r) for r in api.listRegions()]
regions = [gui.choiceItem(r['id'], r['id']) for r in api.listRegions()]
self.region.setValues(regions)
tenants = [gui.choiceItem(t['id'], t['name']) for t in api.listProjects()]
@ -187,6 +187,12 @@ class LiveService(Service):
description = 'UDS Template snapshot' if description is None else description
return self.api.createVolumeSnapshot(self.volume.value, templateName, description)
def getTemplate(self, snapshotId):
'''
Checks current state of a template (an snapshot)
'''
return self.api.getSnapshot(snapshotId)
def deployFromTemplate(self, name, snapshotId):
'''
Deploys a virtual machine on selected cluster from selected template
@ -194,45 +200,58 @@ class LiveService(Service):
Args:
name: Name (sanitized) of the machine
comments: Comments for machine
templateId: Id of the template to deploy from
displayType: 'vnc' or 'spice'. Display to use ad oVirt admin interface
memoryMB: Memory requested for machine, in MB
guaranteedMB: Minimum memory guaranteed for this machine
snapshotId: Id of the snapshot to deploy from
Returns:
Id of the machine being created form template
'''
logger.debug('Deploying from template {0} machine {1}'.format(snapshotId, name))
# self.datastoreHasSpace()
# self.api.
return self.api.createServerFromSnapshot(snapshotId=snapshotId,
name=name,
availabilityZone=self.availabilityZone.value,
flavorId=self.flavor.value,
networkId=self.network.value,
securityGroupsIdsList=self.securityGroups.value)['id']
def removeTemplate(self, templateId):
'''
invokes removeTemplate from parent provider
'''
return self.parent().removeTemplate(templateId)
self.api.deleteSnapshot(templateId)
def getMachineState(self, machineId):
'''
Invokes getMachineState from parent provider
(returns if machine is "active" or "inactive"
Invokes getServer from openstack client
Args:
machineId: If of the machine to get state
Returns:
one of this values:
unassigned, down, up, powering_up, powered_down,
paused, migrating_from, migrating_to, unknown, not_responding,
wait_for_launch, reboot_in_progress, saving_state, restoring_state,
suspended, image_illegal, image_locked or powering_down
Also can return'unknown' if Machine is not known
ACTIVE. The server is active.
BUILDING. The server has not finished the original build process.
DELETED. The server is permanently deleted.
ERROR. The server is in error.
HARD_REBOOT. The server is hard rebooting. This is equivalent to pulling the power plug on a physical server, plugging it back in, and rebooting it.
MIGRATING. The server is being migrated to a new host.
PASSWORD. The password is being reset on the server.
PAUSED. In a paused state, the state of the server is stored in RAM. A paused server continues to run in frozen state.
REBOOT. The server is in a soft reboot state. A reboot command was passed to the operating system.
REBUILD. The server is currently being rebuilt from an image.
RESCUED. The server is in rescue mode. A rescue image is running with the original server image attached.
RESIZED. Server is performing the differential copy of data that changed during its initial copy. Server is down for this stage.
REVERT_RESIZE. The resize or migration of a server failed for some reason. The destination server is being cleaned up and the original source server is restarting.
SOFT_DELETED. The server is marked as deleted but the disk images are still available to restore.
STOPPED. The server is powered off and the disk image still persists.
SUSPENDED. The server is suspended, either by request or necessity. This status appears for only the XenServer/XCP, KVM, and ESXi hypervisors. Administrative users can suspend an instance if it is infrequently used or to perform system maintenance. When you suspend an instance, its VM state is stored on disk, all memory is written to disk, and the virtual machine is stopped. Suspending an instance is similar to placing a device in hibernation; memory and vCPUs become available to create other instances.
VERIFY_RESIZE. System is awaiting confirmation that the server is operational after a move or resize.
'''
return self.parent().getMachineState(machineId)
return self.api.getServer(machineId)['status']
def startMachine(self, machineId):
'''
Tries to start a machine. No check is done, it is simply requested to oVirt.
Tries to start a machine. No check is done, it is simply requested to OpenStack.
This start also "resume" suspended/paused machines
@ -241,46 +260,60 @@ class LiveService(Service):
Returns:
'''
return self.parent().startMachine(machineId)
return self.api.startServer(machineId)
def stopMachine(self, machineId):
'''
Tries to start a machine. No check is done, it is simply requested to oVirt
Tries to stop a machine. No check is done, it is simply requested to OpenStack
Args:
machineId: Id of the machine
Returns:
'''
return self.parent().stopMachine(machineId)
return self.api.stopServer(machineId)
def suspendMachine(self, machineId):
'''
Tries to start a machine. No check is done, it is simply requested to oVirt
Tries to suspend a machine. No check is done, it is simply requested to OpenStack
Args:
machineId: Id of the machine
Returns:
'''
return self.parent().suspendMachine(machineId)
return self.api.suspendServer(machineId)
def resumeMachine(self, machineId):
'''
Tries to start a machine. No check is done, it is simply requested to OpenStack
Args:
machineId: Id of the machine
Returns:
'''
return self.api.suspendServer(machineId)
def removeMachine(self, machineId):
'''
Tries to delete a machine. No check is done, it is simply requested to oVirt
Tries to delete a machine. No check is done, it is simply requested to OpenStack
Args:
machineId: Id of the machine
Returns:
'''
return self.parent().removeMachine(machineId)
return self.api.deleteServer(machineId)
def getNetInfo(self, machineId, networkId=None):
def getNetInfo(self, machineId):
'''
Changes the mac address of first nic of the machine to the one specified
Gets the mac address of first nic of the machine
'''
return self.parent().getNetInfo(machineId, networkId=None)
net = self.api.getServer(machineId)['addresses']
vals = six.next(six.itervalues(net))[0] # Returns "any" mac address of any interface. We just need only one interface info
return (vals['OS-EXT-IPS-MAC:mac_addr'], vals['addr'])
def getBaseName(self):
'''
@ -293,9 +326,3 @@ class LiveService(Service):
Returns the length of numbers part
'''
return int(self.lenName.value)
def getDisplay(self):
'''
Returns the selected display type (for created machines, for administration
'''
return self.display.value

View File

@ -38,7 +38,7 @@ def getResources(parameters):
data = [
{'name': 'availabilityZone', 'values': zones },
{'name': 'volume', 'values': volumes },
{'name': 'networks', 'values': networks },
{'name': 'network', 'values': networks },
{'name': 'flavor', 'values': flavors },
{'name': 'securityGroups', 'values': securityGroups },
{'name': 'volumeType', 'values': volumeTypes },

View File

@ -31,20 +31,17 @@
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
'''
# pylint: disable=maybe-no-member
from django.utils.translation import ugettext as _
from uds.core.util.Cache import Cache
import logging
import requests
import json
import six
import hashlib
import dateutil.parser
# Python bindings for OpenNebula
from .common import sanitizeName
__updated__ = '2016-03-04'
__updated__ = '2016-03-07'
logger = logging.getLogger(__name__)
@ -64,32 +61,36 @@ VERIFY_SSL = False
# Helpers
def ensureResponseIsValid(response, errMsg=None):
if response.ok is False:
print "False"
try:
_x, err = response.json().popitem() # Extract any key, in case of error is expected to have only one top key so this will work
errMsg = errMsg + ': {message}'.format(**err)
except Exception:
pass # If error geting error message, simply ignore it (will be loged on service log anyway)
if errMsg is None:
errMsg = 'Error checking response'
logger.error('{}: {}'.format(errMsg, response.content))
print response.content
print response
raise Exception(errMsg)
def getRecurringUrlJson(url, headers, key, errMsg=None):
def getRecurringUrlJson(url, headers, key, params=None, errMsg=None, timeout=10):
counter = 0
while True:
counter += 1
logger.debug('Requesting url #{}: {}'.format(counter, url))
r = requests.get(url, headers=headers, verify=VERIFY_SSL)
logger.debug('Requesting url #{}: {} / {}'.format(counter, url, params))
r = requests.get(url, params=params, headers=headers, verify=VERIFY_SSL, timeout=timeout)
ensureResponseIsValid(r, errMsg)
json = r.json()
j = r.json()
for v in json[key]:
for v in j[key]:
yield v
if 'next' not in json:
if 'next' not in j:
break
url = json['next']
url = j['next']
# Decorators
@ -214,68 +215,70 @@ class Client(object):
@authRequired
def listProjects(self):
for p in getRecurringUrlJson(self._authUrl + 'v3/users/{user_id}/projects'.format(user_id=self._userId),
return getRecurringUrlJson(self._authUrl + 'v3/users/{user_id}/projects'.format(user_id=self._userId),
headers=self._requestHeaders(),
key='projects',
errMsg='List Projects'):
yield p
errMsg='List Projects',
timeout=self._timeout)
@authRequired
def listRegions(self):
for r in getRecurringUrlJson(self._authUrl + 'v3/regions/',
return getRecurringUrlJson(self._authUrl + 'v3/regions/',
headers=self._requestHeaders(),
key='regions',
errMsg='List Regions'):
yield r['id']
errMsg='List Regions',
timeout=self._timeout)
@authProjectRequired
def listVms(self):
for v in getRecurringUrlJson(self._getEndpointFor('compute') + '/servers',
def listServers(self, detail=False, params=None):
path = '/servers/' + 'detail' if detail is True else ''
return getRecurringUrlJson(self._getEndpointFor('compute') + path,
headers=self._requestHeaders(),
key='servers',
errMsg='List Vms'):
yield { 'name': v['name'], 'id': v['id'] }
params=params,
errMsg='List Vms',
timeout=self._timeout)
@authProjectRequired
def listImages(self):
for i in getRecurringUrlJson(self._getEndpointFor('image') + '/v2/images?status=active',
return getRecurringUrlJson(self._getEndpointFor('image') + '/v2/images?status=active',
headers=self._requestHeaders(),
key='images',
errMsg='List Images'):
yield { 'name': i['name'], 'size': i['size'], 'visibility': i['visibility'], 'format': i['disk_format'] }
errMsg='List Images',
timeout=self._timeout)
@authProjectRequired
def listVolumeTypes(self):
for t in getRecurringUrlJson(self._getEndpointFor('volumev2') + '/types',
return getRecurringUrlJson(self._getEndpointFor('volumev2') + '/types',
headers=self._requestHeaders(),
key='volume_types',
errMsg='List Volume Types'):
yield { 'id': t['id'], 'name': t['name'] }
errMsg='List Volume Types',
timeout=self._timeout)
@authProjectRequired
def listVolumes(self):
# self._getEndpointFor('volumev2') + '/volumes'
for v in getRecurringUrlJson(self._getEndpointFor('volumev2') + '/volumes/detail',
return getRecurringUrlJson(self._getEndpointFor('volumev2') + '/volumes/detail',
headers=self._requestHeaders(),
key='volumes',
errMsg='List Volumes'):
yield { 'id': v['id'], 'name': v['name'], 'size': v['size'], 'status': v['status'] }
errMsg='List Volumes',
timeout=self._timeout)
@authProjectRequired
def listVolumeSnapshots(self, volumeId):
def listVolumeSnapshots(self, volumeId=None):
for s in getRecurringUrlJson(self._getEndpointFor('volumev2') + '/snapshots',
headers=self._requestHeaders(),
key='snapshots',
errMsg='List snapshots'):
if s['volume_id'] == volumeId:
yield { 'id': s['id'], 'name': s['name'], 'description': s['description'], 'status': s['status'] }
errMsg='List snapshots',
timeout=self._timeout):
if volumeId is None or s['volume_id'] == volumeId:
yield s
@authProjectRequired
@ -283,43 +286,69 @@ class Client(object):
for az in getRecurringUrlJson(self._getEndpointFor('compute') + '/os-availability-zone',
headers=self._requestHeaders(),
key='availabilityZoneInfo',
errMsg='List Availability Zones'):
errMsg='List Availability Zones',
timeout=self._timeout):
if az['zoneState']['available'] is True:
yield az['zoneName']
@authProjectRequired
def listFlavors(self):
for f in getRecurringUrlJson(self._getEndpointFor('compute') + '/flavors',
return getRecurringUrlJson(self._getEndpointFor('compute') + '/flavors',
headers=self._requestHeaders(),
key='flavors',
errMsg='List Flavors'):
yield { 'id': f['id'], 'name': f['name'] }
errMsg='List Flavors',
timeout=self._timeout)
@authProjectRequired
def listNetworks(self):
for n in getRecurringUrlJson(self._getEndpointFor('compute') + '/os-tenant-networks',
return getRecurringUrlJson(self._getEndpointFor('network') + '/v2.0/networks',
headers=self._requestHeaders(),
key='networks',
errMsg='List Networks'):
yield { 'id': n['id'], 'name': n['label'] }
errMsg='List Networks',
timeout=self._timeout)
@authProjectRequired
def listPorts(self, networkId=None, ownerId=None):
params = {}
if networkId is not None:
params['network_id'] = networkId
if ownerId is not None:
params['device_owner'] = ownerId
return getRecurringUrlJson(self._getEndpointFor('network') + '/v2.0/ports',
headers=self._requestHeaders(),
key='ports',
params=params,
errMsg='List ports',
timeout=self._timeout)
@authProjectRequired
def listSecurityGroups(self):
for s in getRecurringUrlJson(self._getEndpointFor('compute') + '/os-security-groups',
return getRecurringUrlJson(self._getEndpointFor('compute') + '/os-security-groups',
headers=self._requestHeaders(),
key='security_groups',
errMsg='List security groups'):
yield { 'id': s['id'], 'name': s['name'] }
errMsg='List security groups',
timeout=self._timeout)
@authProjectRequired
def getServer(self, serverId):
r = requests.get(self._getEndpointFor('compute') + '/servers/{server_id}'.format(server_id=serverId),
headers=self._requestHeaders(),
verify=VERIFY_SSL,
timeout=self._timeout)
ensureResponseIsValid(r, 'Get Server information')
return r.json()['server']
@authProjectRequired
def getVolume(self, volumeId):
r = requests.get(self._getEndpointFor('volumev2') + '/volumes/{volume_id}'.format(volume_id=volumeId),
headers=self._requestHeaders(),
verify=VERIFY_SSL)
verify=VERIFY_SSL,
timeout=self._timeout)
ensureResponseIsValid(r, 'Get Volume information')
@ -330,17 +359,43 @@ class Client(object):
@authProjectRequired
def getSnapshot(self, snapshotId):
'''
States are:
creating, available, deleting, error, error_deleting
'''
r = requests.get(self._getEndpointFor('volumev2') + '/snapshots/{snapshot_id}'.format(snapshot_id=snapshotId),
headers=self._requestHeaders(),
verify=VERIFY_SSL)
verify=VERIFY_SSL,
timeout=self._timeout)
ensureResponseIsValid(r, 'Get Snaphost information')
v = r.json()['snapshot']
return { 'id': v['id'], 'name': v['name'], 'status': v['status'], 'volume_id': v['volume_id'] }
return v
@authProjectRequired
def updateSnapshot(self, snapshotId, name=None, description=None):
data = { 'snapshot': {} }
if name is not None:
data['snapshot']['name'] = name
if description is not None:
data['snapshot']['description'] = description
r = requests.put(self._getEndpointFor('volumev2') + '/snapshots/{snapshot_id}'.format(snapshot_id=snapshotId),
data=json.dumps(data),
headers=self._requestHeaders(),
verify=VERIFY_SSL,
timeout=self._timeout)
ensureResponseIsValid(r, 'Update Snaphost information')
v = r.json()['snapshot']
return v
@authProjectRequired
def createVolumeSnapshot(self, volumeId, name, description=None):
@ -364,7 +419,8 @@ class Client(object):
ensureResponseIsValid(r, 'Cannot create snapshot. Ensure volume is in state "available"')
return r.json()
return r.json()['snapshot']
@authProjectRequired
def createVolumeFromSnapshot(self, snapshotId, name, description=None):
@ -373,7 +429,7 @@ class Client(object):
'volume': {
'name': name,
'description': description,
# 'volume_type': volType, # This is the volume type, not the id
# 'volume_type': volType, # This seems to be the volume type name, not the id
'snapshot_id': snapshotId
}
}
@ -388,6 +444,111 @@ class Client(object):
return r.json()
@authProjectRequired
def createServerFromSnapshot(self, snapshotId, name, availabilityZone, flavorId, networkId, securityGroupsIdsList, count=1):
data = {
'server': {
'name': name,
'imageRef': '',
'os-availability-zone': availabilityZone,
'availability_zone': availabilityZone,
'block_device_mapping_v2': [{
'boot_index': '0',
'uuid': snapshotId,
# 'volume_size': 1,
# 'device_name': 'vda',
'source_type': 'snapshot',
'destination_type': 'volume',
'delete_on_termination': True
}],
'flavorRef': flavorId,
# 'OS-DCF:diskConfig': 'AUTO',
'max_count': count,
'min_count': count,
'networks': [ { 'uuid': networkId } ],
'security_groups': [{'name': sg} for sg in securityGroupsIdsList]
}
}
r = requests.post(self._getEndpointFor('compute') + '/servers',
data=json.dumps(data),
headers=self._requestHeaders(),
verify=VERIFY_SSL,
timeout=self._timeout)
ensureResponseIsValid(r, 'Cannot create instance from snapshot.')
return r.json()['server']
@authProjectRequired
def deleteServer(self, serverId):
r = requests.post(self._getEndpointFor('compute') + '/servers/{server_id}/action'.format(server_id=serverId),
data='{"forceDelete": null}',
headers=self._requestHeaders(),
verify=VERIFY_SSL,
timeout=self._timeout)
ensureResponseIsValid(r, 'Cannot start server (probably server does not exists).')
# This does not returns anything
@authProjectRequired
def deleteSnapshot(self, snapshotId):
r = requests.delete(self._getEndpointFor('volumev2') + '/snapshots/{snapshot_id}'.format(snapshot_id=snapshotId),
headers=self._requestHeaders(),
verify=VERIFY_SSL,
timeout=self._timeout)
ensureResponseIsValid(r, 'Cannot remove snapshot.')
# Does not returns a message body
@authProjectRequired
def startServer(self, serverId):
r = requests.post(self._getEndpointFor('compute') + '/servers/{server_id}/action'.format(server_id=serverId),
data='{"os-start": null}',
headers=self._requestHeaders(),
verify=VERIFY_SSL,
timeout=self._timeout)
ensureResponseIsValid(r, 'Starting server')
# This does not returns anything
@authProjectRequired
def stopServer(self, serverId):
r = requests.post(self._getEndpointFor('compute') + '/servers/{server_id}/action'.format(server_id=serverId),
data='{"os-stop": null}',
headers=self._requestHeaders(),
verify=VERIFY_SSL,
timeout=self._timeout)
ensureResponseIsValid(r, 'Stoping server')
@authProjectRequired
def suspendServer(self, serverId):
r = requests.post(self._getEndpointFor('compute') + '/servers/{server_id}/action'.format(server_id=serverId),
data='{"suspend": null}',
headers=self._requestHeaders(),
verify=VERIFY_SSL,
timeout=self._timeout)
ensureResponseIsValid(r, 'Suspending server')
@authProjectRequired
def resumeServer(self, serverId):
r = requests.post(self._getEndpointFor('compute') + '/servers/{server_id}/action'.format(server_id=serverId),
data='{"resume": null}',
headers=self._requestHeaders(),
verify=VERIFY_SSL,
timeout=self._timeout)
ensureResponseIsValid(r, 'Resuming server')
def testConnection(self):
# First, ensure requested api is supported
@ -405,6 +566,6 @@ class Client(object):
self.authPassword()
return True
except Exception:
raise Exception('Authentication error')
raise Exception(_('Authentication error'))
raise Exception(_('Openstack does not support identity API 3.2 or newer. This OpenStack server is not compatible with UDS.'))

View File

@ -41,7 +41,7 @@ import six
from defusedxml import minidom
__updated__ = '2016-03-04'
__updated__ = '2016-03-07'
logger = logging.getLogger(__name__)
@ -50,7 +50,3 @@ from .common import *
from .UDSOpenStackClient import Client
from . import template
from . import vm
from . import storage

View File

@ -33,15 +33,26 @@
import re
from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver
import logging
__updated__ = '2016-03-04'
__updated__ = '2016-03-07'
logger = logging.getLogger(__name__)
(ACTIVE, BUILDING, DELETED, ERROR,
HARD_REBOOT, MIGRATING, PASSWORD,
PAUSED, REBOOT, REBUILD, RESCUED,
RESIZED, REVERT_RESIZE, SOFT_DELETED,
STOPPED, SUSPENDED, UNKNOWN, VERIFY_RESIZE) = ('ACTIVE', 'BUILDING', 'DELETED', 'ERROR',
'HARD_REBOOT', 'MIGRATING', 'PASSWORD',
'PAUSED', 'REBOOT', 'REBUILD', 'RESCUED',
'RESIZED', 'REVERT_RESIZE', 'SOFT_DELETED',
'STOPPED', 'SUSPENDED', 'UNKNOWN', 'VERIFY_RESIZE')
# Helpers to check statuses
def statusIsLost(status):
return status in [DELETED, ERROR, UNKNOWN, SOFT_DELETED]
def sanitizeName(name):
'''
machine names with [a-zA-Z0-9_-]

View File

@ -1,42 +0,0 @@
# -*- 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
'''
import logging
import six
import oca
__updated__ = '2016-02-25'
logger = logging.getLogger(__name__)

View File

@ -1,46 +0,0 @@
# -*- 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
'''
import logging
import six
import oca
from defusedxml import minidom
# Python bindings for OpenNebula
from .common import sanitizeName
__updated__ = '2016-02-25'
logger = logging.getLogger(__name__)

View File

@ -1,45 +0,0 @@
# -*- 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
'''
import logging
import six
import oca
from defusedxml import minidom
# Python bindings for OpenNebula
__updated__ = '2016-02-25'
logger = logging.getLogger(__name__)