Fixed opennebula provider for python 3.7 && updated setting to nonpersistant (moved to end of image clone process)

This commit is contained in:
Adolfo Gómez García 2019-07-25 11:49:40 +02:00
parent 7d66d92f85
commit 54110f5425
10 changed files with 101 additions and 117 deletions

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012 Virtual Cable S.L. # Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
@ -30,16 +30,16 @@
""" """
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
""" """
import pickle
import logging
import typing
from uds.core.services import UserDeployment from uds.core.services import UserDeployment
from uds.core.util.State import State from uds.core.util.State import State
from uds.core.util import log from uds.core.util import log
from . import on from . import on
import pickle
import logging
__updated__ = '2019-02-07'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -47,7 +47,6 @@ opCreate, opStart, opSuspend, opRemove, opWait, opError, opFinish, opRetry = ran
NO_MORE_NAMES = 'NO-NAME-ERROR' NO_MORE_NAMES = 'NO-NAME-ERROR'
class LiveDeployment(UserDeployment): class LiveDeployment(UserDeployment):
""" """
This class generates the user consumable elements of the service tree. This class generates the user consumable elements of the service tree.
@ -59,10 +58,18 @@ class LiveDeployment(UserDeployment):
The logic for managing ovirt deployments (user machines in this case) is here. The logic for managing ovirt deployments (user machines in this case) is here.
""" """
# : Recheck every six seconds by default (for task methods) # : Recheck every six seconds by default (for task methods)
suggestedTime = 6 suggestedTime = 6
#
_name = ''
_ip = ''
_mac = ''
_vmid = ''
_reason = ''
_queue: typing.List[int] = []
def initialize(self): def initialize(self):
self._name = '' self._name = ''
self._ip = '' self._ip = ''
@ -86,11 +93,11 @@ class LiveDeployment(UserDeployment):
pickle.dumps(self._queue, protocol=0) pickle.dumps(self._queue, protocol=0)
]) ])
def unmarshal(self, str_): def unmarshal(self, data):
""" """
Does nothing here also, all data are keeped at environment storage Does nothing here also, all data are keeped at environment storage
""" """
vals = str_.split(b'\1') vals = data.split(b'\1')
if vals[0] == b'v1': if vals[0] == b'v1':
self._name = vals[1].decode('utf8') self._name = vals[1].decode('utf8')
self._ip = vals[2].decode('utf8') self._ip = vals[2].decode('utf8')
@ -139,7 +146,7 @@ class LiveDeployment(UserDeployment):
:note: This IP is the IP of the "consumed service", so the transport can :note: This IP is the IP of the "consumed service", so the transport can
access it. access it.
""" """
logger.debug('Setting IP to {}'.format(ip)) logger.debug('Setting IP to %s', ip)
self._ip = ip self._ip = ip
def getUniqueId(self): def getUniqueId(self):
@ -204,7 +211,7 @@ class LiveDeployment(UserDeployment):
def notifyReadyFromOsManager(self, data): def notifyReadyFromOsManager(self, data):
# Here we will check for suspending the VM (when full ready) # Here we will check for suspending the VM (when full ready)
logger.debug('Checking if cache 2 for {0}'.format(self._name)) logger.debug('Checking if cache 2 for %s', self._name)
if self.__getCurrentOp() == opWait: if self.__getCurrentOp() == opWait:
logger.debug('Machine is ready. Moving to level 2') logger.debug('Machine is ready. Moving to level 2')
self.__popCurrentOp() # Remove current state self.__popCurrentOp() # Remove current state
@ -235,7 +242,7 @@ class LiveDeployment(UserDeployment):
self._queue = [opCreate, opStart, opWait, opSuspend, opFinish] self._queue = [opCreate, opStart, opWait, opSuspend, opFinish]
def __checkMachineState(self, chkState): def __checkMachineState(self, chkState):
logger.debug('Checking that state of machine {} ({}) is {}'.format(self._vmid, self._name, chkState)) logger.debug('Checking that state of machine %s (%s) is %s', self._vmid, self._name, chkState)
state = self.service().getMachineState(self._vmid) state = 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 we want to check an state and machine does not exists (except in case that we whant to check this)
@ -254,13 +261,13 @@ class LiveDeployment(UserDeployment):
return ret return ret
def __getCurrentOp(self): def __getCurrentOp(self):
if len(self._queue) == 0: if not self._queue:
return opFinish return opFinish
return self._queue[0] return self._queue[0]
def __popCurrentOp(self): def __popCurrentOp(self):
if len(self._queue) == 0: if not self._queue:
return opFinish return opFinish
res = self._queue.pop(0) res = self._queue.pop(0)
@ -279,14 +286,14 @@ class LiveDeployment(UserDeployment):
Returns: Returns:
State.ERROR, so we can do "return self.__error(reason)" State.ERROR, so we can do "return self.__error(reason)"
""" """
logger.debug('Setting error state, reason: {0}'.format(reason)) logger.debug('Setting error state, reason: %s', reason)
self.doLog(log.ERROR, reason) self.doLog(log.ERROR, reason)
if self._vmid != '': # Powers off & delete it if self._vmid != '': # Powers off & delete it
try: try:
self.service().removeMachine(self._vmid) self.service().removeMachine(self._vmid)
except: except Exception:
logger.debug('Can\t set machine state to stopped') logger.debug('Can\'t set machine state to stopped')
self._queue = [opError] self._queue = [opError]
self._reason = str(reason) self._reason = str(reason)
@ -371,7 +378,7 @@ class LiveDeployment(UserDeployment):
if state == on.VmState.ACTIVE: # @UndefinedVariable if state == on.VmState.ACTIVE: # @UndefinedVariable
subState = self.service().getMachineSubstate(self._vmid) subState = self.service().getMachineSubstate(self._vmid)
if subState < 3: # Less than running if subState < 3: # Less than running
logger.info('Must wait before remove: {}'.format(subState)) logger.info('Must wait before remove: %s', subState)
self.__pushFrontOp(opRetry) self.__pushFrontOp(opRetry)
return return
@ -457,7 +464,6 @@ class LiveDeployment(UserDeployment):
(No matter wether it is for cache or for an user) (No matter wether it is for cache or for an user)
""" """
self.__debug('finish') self.__debug('finish')
pass
def moveToCache(self, newLevel): def moveToCache(self, newLevel):
""" """
@ -529,8 +535,8 @@ class LiveDeployment(UserDeployment):
}.get(op, '????') }.get(op, '????')
def __debug(self, txt): def __debug(self, txt):
logger.debug('_name {0}: {1}'.format(txt, self._name)) logger.debug('_name %s: %s', txt, self._name)
logger.debug('_ip {0}: {1}'.format(txt, self._ip)) logger.debug('_ip %s: %s', txt, self._ip)
logger.debug('_mac {0}: {1}'.format(txt, self._mac)) logger.debug('_mac %s: %s', txt, self._mac)
logger.debug('_vmid {0}: {1}'.format(txt, self._vmid)) logger.debug('_vmid %s: %s', txt, self._vmid)
logger.debug('Queue at {0}: {1}'.format(txt, [LiveDeployment.__op2str(op) for op in self._queue])) logger.debug('Queue at %s: %s', txt, [LiveDeployment.__op2str(op) for op in self._queue])

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012 Virtual Cable S.L. # Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
@ -30,16 +30,11 @@
""" """
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
""" """
import logging
from django.utils.translation import ugettext as _
from uds.core.services import Publication from uds.core.services import Publication
from uds.core.util.State import State from uds.core.util.State import State
import six
import logging
__updated__ = '2019-02-07'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -51,6 +46,11 @@ class LivePublication(Publication):
suggestedTime = 2 # : Suggested recheck time if publication is unfinished in seconds suggestedTime = 2 # : Suggested recheck time if publication is unfinished in seconds
_name = ''
_reason = ''
_templateId = ''
_state = ''
def initialize(self): def initialize(self):
""" """
This method will be invoked by default __init__ of base class, so it gives This method will be invoked by default __init__ of base class, so it gives
@ -76,7 +76,7 @@ class LivePublication(Publication):
""" """
deserializes the data and loads it inside instance. deserializes the data and loads it inside instance.
""" """
logger.debug('Data: {0}'.format(data)) logger.debug('Data: %s', data)
vals = data.decode('utf8').split('\t') vals = data.decode('utf8').split('\t')
if vals[0] == 'v1': if vals[0] == 'v1':
self._name, self._reason, self._templateId, self._state = vals[1:] self._name, self._reason, self._templateId, self._state = vals[1:]
@ -93,7 +93,7 @@ class LivePublication(Publication):
self._templateId = self.service().makeTemplate(self._name) self._templateId = self.service().makeTemplate(self._name)
except Exception as e: except Exception as e:
self._state = 'error' self._state = 'error'
self._reason = six.text_type(e) self._reason = str(e)
return State.ERROR return State.ERROR
return State.RUNNING return State.RUNNING
@ -109,7 +109,7 @@ class LivePublication(Publication):
self._state = 'ok' self._state = 'ok'
except Exception as e: except Exception as e:
self._state = 'error' self._state = 'error'
self._reason = six.text_type(e) self._reason = str(e)
if self._state == 'error': if self._state == 'error':
return State.ERROR return State.ERROR
@ -124,7 +124,6 @@ class LivePublication(Publication):
""" """
In our case, finish does nothing In our case, finish does nothing
""" """
pass
def reasonOfError(self): def reasonOfError(self):
""" """

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012 Virtual Cable S.L. # Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
@ -30,17 +30,16 @@
""" """
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
""" """
from django.utils.translation import ugettext_noop as _, ugettext import logging
from django.utils.translation import ugettext_noop as _
from uds.core.transports import protocols from uds.core.transports import protocols
from uds.core.services import Service, types as serviceTypes from uds.core.services import Service, types as serviceTypes
from uds.core.ui import gui
from .LivePublication import LivePublication from .LivePublication import LivePublication
from .LiveDeployment import LiveDeployment from .LiveDeployment import LiveDeployment
from uds.core.ui import gui
import logging
__updated__ = '2018-08-20'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -186,7 +185,7 @@ class LiveService(Service):
Returns: Returns:
Id of the machine being created form template Id of the machine being created form template
""" """
logger.debug('Deploying from template {0} machine {1}'.format(templateId, name)) logger.debug('Deploying from template %s machine %s', templateId, name)
# self.datastoreHasSpace() # self.datastoreHasSpace()
return self.parent().deployFromTemplate(name, templateId) return self.parent().deployFromTemplate(name, templateId)

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012 Virtual Cable S.L. # Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
@ -28,29 +28,18 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # 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 .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals import logging
from django.utils.translation import ugettext_noop as _ from django.utils.translation import ugettext_noop as _
from uds.core.services import ServiceProvider from uds.core.services import ServiceProvider
from uds.core.ui import gui from uds.core.ui import gui
from uds.core.util import validators from uds.core.util import validators
from defusedxml import minidom
from .LiveService import LiveService from .LiveService import LiveService
from . import on from . import on
import logging
import six
# Python bindings for OpenNebula
# import oca
__updated__ = '2018-08-20'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -119,7 +108,7 @@ class Provider(ServiceProvider):
if values is not None: if values is not None:
self.timeout.value = validators.validateTimeout(self.timeout.value, returnAsInteger=False) self.timeout.value = validators.validateTimeout(self.timeout.value, returnAsInteger=False)
logger.debug('Endpoint: {}'.format(self.endpoint)) logger.debug('Endpoint: %s', self.endpoint)
@property @property
def endpoint(self): def endpoint(self):
@ -130,7 +119,7 @@ class Provider(ServiceProvider):
if self._api is None: if self._api is None:
self._api = on.OpenNebulaClient(self.username.value, self.password.value, self.endpoint) self._api = on.OpenNebulaClient(self.username.value, self.password.value, self.endpoint)
logger.debug('Api: {}'.format(self._api)) logger.debug('Api: %s', self._api)
return self._api return self._api
def resetApi(self): def resetApi(self):

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012 Virtual Cable S.L. # Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012 Virtual Cable S.L. # Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
@ -32,16 +32,16 @@
""" """
# pylint: disable=maybe-no-member # pylint: disable=maybe-no-member
import types
import six import xmlrpc.client
from uds.core.util import xml2dict from uds.core.util import xml2dict
from . import storage from . import storage
from . import template from . import template
from . import vm from . import vm
# Import submodules # Import submodules
from .common import * from .common import *
import types
__updated__ = '2017-03-28' __updated__ = '2017-03-28'
@ -72,14 +72,14 @@ def checkResult(lst, parseResult=True):
raise Exception('OpenNebula error {}: "{}"'.format(lst[2], lst[1])) raise Exception('OpenNebula error {}: "{}"'.format(lst[2], lst[1]))
if parseResult: if parseResult:
return xml2dict.parse(lst[1]) return xml2dict.parse(lst[1])
else:
return lst[1] return lst[1]
def asList(element): def asList(element):
if isinstance(element, (tuple, list)): if isinstance(element, (tuple, list)):
return element return element
return element, return (element,)
# noinspection PyShadowingNames # noinspection PyShadowingNames
@ -95,7 +95,7 @@ class OpenNebulaClient(object):
def sessionString(self): def sessionString(self):
return '{}:{}'.format(self.username, self.password) return '{}:{}'.format(self.username, self.password)
@property @property # type: ignore
@ensureConnected @ensureConnected
def version(self): def version(self):
if self.cachedVersion is None: if self.cachedVersion is None:
@ -108,11 +108,11 @@ class OpenNebulaClient(object):
if self.connection is not None: if self.connection is not None:
return return
self.connection = six.moves.xmlrpc_client.ServerProxy(self.endpoint) # @UndefinedVariable self.connection = xmlrpc.client.ServerProxy(self.endpoint) # @UndefinedVariable
@ensureConnected @ensureConnected
def enumStorage(self, storageType=0): def enumStorage(self, storageType=0):
storageType = six.text_type(storageType) # Ensure it is an string storageType = str(storageType) # Ensure it is an string
# Invoke datastore pools info, no parameters except connection string # Invoke datastore pools info, no parameters except connection string
result = self.connection.one.datastorepool.info(self.sessionString) result = self.connection.one.datastorepool.info(self.sessionString)
result = checkResult(result) result = checkResult(result)
@ -129,8 +129,7 @@ class OpenNebulaClient(object):
3.- When the next parameter is >= -1 this is the Range start ID. Can be -1. For smaller values this is the offset used for pagination. 3.- When the next parameter is >= -1 this is the Range start ID. Can be -1. For smaller values this is the offset used for pagination.
4.- For values >= -1 this is the Range end ID. Can be -1 to get until the last ID. For values < -1 this is the page size used for pagination. 4.- For values >= -1 this is the Range end ID. Can be -1 to get until the last ID. For values < -1 this is the page size used for pagination.
""" """
result = self.connection.one.templatepool.info(self.sessionString, -1, -1, -1) result = checkResult(self.connection.one.templatepool.info(self.sessionString, -1, -1, -1))
result = checkResult(result)
for ds in asList(result['VMTEMPLATE_POOL']['VMTEMPLATE']): for ds in asList(result['VMTEMPLATE_POOL']['VMTEMPLATE']):
try: try:
yield(ds['ID'], ds['NAME'], ds['TEMPLATE']['MEMORY']) yield(ds['ID'], ds['NAME'], ds['TEMPLATE']['MEMORY'])
@ -182,7 +181,7 @@ class OpenNebulaClient(object):
return checkResult(result, parseResult=False) return checkResult(result, parseResult=False)
@ensureConnected @ensureConnected
def updateTemplate(self, templateId, template, updateType=0): def updateTemplate(self, templateId, templateData, updateType=0):
""" """
Updates the template with the templateXml Updates the template with the templateXml
1.- Session string 1.- Session string
@ -190,7 +189,7 @@ class OpenNebulaClient(object):
3.- The new template contents. Syntax can be the usual attribute=value or XML. 3.- The new template contents. Syntax can be the usual attribute=value or XML.
4.- Update type. 0 replace the whole template, 1 merge with the existing one 4.- Update type. 0 replace the whole template, 1 merge with the existing one
""" """
result = self.connection.one.template.update(self.sessionString, int(templateId), template, int(updateType)) result = self.connection.one.template.update(self.sessionString, int(templateId), templateData, int(updateType))
return checkResult(result, parseResult=False) return checkResult(result, parseResult=False)
@ensureConnected @ensureConnected
@ -208,6 +207,7 @@ class OpenNebulaClient(object):
@ensureConnected @ensureConnected
def deleteTemplate(self, templateId): def deleteTemplate(self, templateId):
""" """
Deletes the template (not images)
""" """
result = self.connection.one.template.delete(self.sessionString, int(templateId)) result = self.connection.one.template.delete(self.sessionString, int(templateId))
return checkResult(result, parseResult=False) return checkResult(result, parseResult=False)

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012 Virtual Cable S.L. # Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
@ -37,8 +37,6 @@ import re
import logging import logging
__updated__ = '2016-02-09'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
module = sys.modules[__name__] module = sys.modules[__name__]

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012 Virtual Cable S.L. # Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
@ -34,8 +34,6 @@
import logging import logging
__updated__ = '2016-07-11'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def enumerateDatastores(api, datastoreType=0): def enumerateDatastores(api, datastoreType=0):

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012 Virtual Cable S.L. # Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
@ -32,17 +32,14 @@
""" """
import logging import logging
import six
from defusedxml import minidom from defusedxml import minidom
# Python bindings for OpenNebula # Python bindings for OpenNebula
from .common import sanitizeName from .common import sanitizeName
__updated__ = '2017-05-05'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def getTemplates(api, force=False): def getTemplates(api, force=False):
for t in api.enumTemplates(): for t in api.enumTemplates():
@ -76,13 +73,13 @@ def create(api, fromTemplateId, name, toDataStore):
info = api.templateInfo(templateId)[1] info = api.templateInfo(templateId)[1]
template = minidom.parseString(info).getElementsByTagName('TEMPLATE')[0] template = minidom.parseString(info).getElementsByTagName('TEMPLATE')[0]
logger.debug('XML: {}'.format(template.toxml())) logger.debug('XML: %s', template.toxml())
counter = 0 counter = 0
for dsk in template.getElementsByTagName('DISK'): for dsk in template.getElementsByTagName('DISK'):
counter += 1 counter += 1
imgIds = dsk.getElementsByTagName('IMAGE_ID') imgIds = dsk.getElementsByTagName('IMAGE_ID')
if len(imgIds) == 0: if not imgIds:
fromId = False fromId = False
try: try:
node = dsk.getElementsByTagName('IMAGE')[0].childNodes[0] node = dsk.getElementsByTagName('IMAGE')[0].childNodes[0]
@ -99,19 +96,17 @@ def create(api, fromTemplateId, name, toDataStore):
node = imgIds[0].childNodes[0] node = imgIds[0].childNodes[0]
imgId = node.data imgId = node.data
logger.debug('Found {} for cloning'.format(imgId)) logger.debug('Found %s for cloning', imgId)
# if api.imageInfo(imgId)[0]['IMAGE']['STATE'] != '1': # if api.imageInfo(imgId)[0]['IMAGE']['STATE'] != '1':
# raise Exception('The base machines images are not in READY state') # raise Exception('The base machines images are not in READY state')
# Now clone the image # Now clone the image
imgName = sanitizeName(name + ' DSK ' + six.text_type(counter)) imgName = sanitizeName(name + ' DSK ' + str(counter))
newId = api.cloneImage(imgId, imgName, toDataStore) # api.call('image.clone', int(imgId), imgName, int(toDataStore)) newId = api.cloneImage(imgId, imgName, toDataStore) # api.call('image.clone', int(imgId), imgName, int(toDataStore))
# Ensure image is non persistent
api.makePersistentImage(newId, False)
# Now Store id/name # Now Store id/name
if fromId is True: if fromId is True:
node.data = six.text_type(newId) node.data = str(newId)
else: else:
node.data = imgName node.data = imgName
@ -119,9 +114,9 @@ def create(api, fromTemplateId, name, toDataStore):
# api.call('template.update', templateId, template.toxml()) # api.call('template.update', templateId, template.toxml())
api.updateTemplate(templateId, template.toxml()) api.updateTemplate(templateId, template.toxml())
return six.text_type(templateId) return str(templateId)
except Exception as e: except Exception as e:
logger.exception('Creating template on OpenNebula: {}'.format(e)) logger.exception('Creating template on OpenNebula')
try: try:
api.deleteTemplate(templateId) # Try to remove created template in case of fail api.deleteTemplate(templateId) # Try to remove created template in case of fail
except Exception: except Exception:
@ -143,11 +138,11 @@ def remove(api, templateId):
info = api.templateInfo(templateId)[1] info = api.templateInfo(templateId)[1]
template = minidom.parseString(info).getElementsByTagName('TEMPLATE')[0] template = minidom.parseString(info).getElementsByTagName('TEMPLATE')[0]
logger.debug('XML: {}'.format(template.toxml())) logger.debug('XML: %s', template.toxml())
for dsk in template.getElementsByTagName('DISK'): for dsk in template.getElementsByTagName('DISK'):
imgIds = dsk.getElementsByTagName('IMAGE_ID') imgIds = dsk.getElementsByTagName('IMAGE_ID')
if len(imgIds) == 0: if not imgIds:
try: try:
node = dsk.getElementsByTagName('IMAGE')[0].childNodes[0] node = dsk.getElementsByTagName('IMAGE')[0].childNodes[0]
except IndexError: except IndexError:
@ -157,17 +152,16 @@ def remove(api, templateId):
node = imgIds[0].childNodes[0] node = imgIds[0].childNodes[0]
imgId = node.data imgId = node.data
logger.debug('Found {} for cloning'.format(imgId)) logger.debug('Found %s for cloning', imgId)
# Now delete the image # Now delete the image
api.deleteImage(imgId) # api.call('image.delete', int(imgId)) api.deleteImage(imgId) # api.call('image.delete', int(imgId))
except Exception:
except: logger.exception('Removing image')
logger.exception('Exception cloning image')
api.deleteTemplate(templateId) # api.call('template.delete', int(templateId)) api.deleteTemplate(templateId) # api.call('template.delete', int(templateId))
except Exception as e: except Exception:
logger.error('Removing template on OpenNebula: {}'.format(e)) logger.error('Removing template on OpenNebula')
def deployFrom(api, templateId, name): def deployFrom(api, templateId, name):
""" """
@ -182,7 +176,7 @@ def deployFrom(api, templateId, name):
Id of the machine being created form template Id of the machine being created form template
""" """
vmId = api.instantiateTemplate(templateId, name, False, '', False) # api.call('template.instantiate', int(templateId), name, False, '') vmId = api.instantiateTemplate(templateId, name, False, '', False) # api.call('template.instantiate', int(templateId), name, False, '')
return six.text_type(vmId) return str(vmId)
def checkPublished(api, templateId): def checkPublished(api, templateId):
""" """
@ -193,11 +187,11 @@ def checkPublished(api, templateId):
info = api.templateInfo(templateId)[1] info = api.templateInfo(templateId)[1]
template = minidom.parseString(info).getElementsByTagName('TEMPLATE')[0] template = minidom.parseString(info).getElementsByTagName('TEMPLATE')[0]
logger.debug('XML: {}'.format(template.toxml())) logger.debug('XML: %s', template.toxml())
for dsk in template.getElementsByTagName('DISK'): for dsk in template.getElementsByTagName('DISK'):
imgIds = dsk.getElementsByTagName('IMAGE_ID') imgIds = dsk.getElementsByTagName('IMAGE_ID')
if len(imgIds) == 0: if not imgIds:
try: try:
node = dsk.getElementsByTagName('IMAGE')[0].childNodes[0] node = dsk.getElementsByTagName('IMAGE')[0].childNodes[0]
except IndexError: except IndexError:
@ -207,13 +201,16 @@ def checkPublished(api, templateId):
node = imgIds[0].childNodes[0] node = imgIds[0].childNodes[0]
imgId = node.data imgId = node.data
logger.debug('Found {} for checking'.format(imgId)) logger.debug('Found %s for checking', imgId)
state = api.imageInfo(imgId)[0]['IMAGE']['STATE'] state = api.imageInfo(imgId)[0]['IMAGE']['STATE']
if state in ('0', '4'): if state in ('0', '4'):
return False return False
elif state != '1': # If error is not READY if state != '1': # If error is not READY
raise Exception('Error publishing. Image is in an invalid state. (Check it and delete it if not needed anymore)') raise Exception('Error publishing. Image is in an invalid state. (Check it and delete it if not needed anymore)')
# Ensure image is non persistent. This may be invoked more than once, but idoes not matters
api.makePersistentImage(imgId, False)
except Exception: except Exception:
logger.exception('Exception checking published') logger.exception('Exception checking published')
raise raise

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012 Virtual Cable S.L. # Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
@ -32,14 +32,12 @@
''' '''
import logging import logging
import six
# import oca # import oca
from defusedxml import minidom from defusedxml import minidom
# Python bindings for OpenNebula # Python bindings for OpenNebula
from .common import VmState from .common import VmState
__updated__ = '2018-08-20'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -61,7 +59,7 @@ def getMachineState(api, machineId):
# return vm.state # return vm.state
return api.getVMState(machineId) return api.getVMState(machineId)
except Exception as e: except Exception as e:
logger.error('Error obtaining machine state for {} on OpenNebula: {}'.format(machineId, e)) logger.error('Error obtaining machine state for %s on OpenNebula: %s', machineId, e)
return VmState.UNKNOWN return VmState.UNKNOWN
@ -73,7 +71,7 @@ def getMachineSubstate(api, machineId):
try: try:
return api.getVMSubstate(machineId) return api.getVMSubstate(machineId)
except Exception as e: except Exception as e:
logger.error('Error obtaining machine state for {} on OpenNebula: {}'.format(machineId, e)) logger.error('Error obtaining machine substate for %s on OpenNebula: %s', machineId, e)
return VmState.UNKNOWN return VmState.UNKNOWN
@ -91,7 +89,7 @@ def startMachine(api, machineId):
''' '''
try: try:
api.VMAction(machineId, 'resume') api.VMAction(machineId, 'resume')
except Exception as e: except Exception:
# MAybe the machine is already running. If we get error here, simply ignore it for now... # MAybe the machine is already running. If we get error here, simply ignore it for now...
pass pass
@ -108,7 +106,7 @@ def stopMachine(api, machineId):
try: try:
api.VMAction(machineId, 'poweroff-hard') api.VMAction(machineId, 'poweroff-hard')
except Exception as e: except Exception as e:
logger.error('Error powering off {} on OpenNebula: {}'.format(machineId, e)) logger.error('Error powering off %s on OpenNebula: %s', machineId, e)
def suspendMachine(api, machineId): def suspendMachine(api, machineId):
@ -123,7 +121,7 @@ def suspendMachine(api, machineId):
try: try:
api.VMAction(machineId, 'suspend') api.VMAction(machineId, 'suspend')
except Exception as e: except Exception as e:
logger.error('Error suspending {} on OpenNebula: {}'.format(machineId, e)) logger.error('Error suspending %s on OpenNebula: %s', machineId, e)
def resetMachine(api, machineId): def resetMachine(api, machineId):
@ -138,7 +136,7 @@ def resetMachine(api, machineId):
try: try:
api.VMAction(machineId, 'reboot-hard') api.VMAction(machineId, 'reboot-hard')
except Exception as e: except Exception as e:
logger.error('Error reseting {} on OpenNebula: {}'.format(machineId, e)) logger.error('Error reseting %s on OpenNebula: %s', machineId, e)
def removeMachine(api, machineId): def removeMachine(api, machineId):
@ -155,8 +153,9 @@ def removeMachine(api, machineId):
# vm.delete() # vm.delete()
api.deleteVM(machineId) api.deleteVM(machineId)
except Exception as e: except Exception as e:
logger.exception('Error removing machine {} on OpenNebula: {}'.format(machineId, e)) err = 'Error removing machine {} on OpenNebula: {}'.format(machineId, e)
raise Exception('Error removing machine {} on OpenNebula: {}'.format(machineId, e)) logger.exception(err)
raise Exception(err)
def enumerateMachines(api): def enumerateMachines(api):
@ -248,4 +247,3 @@ def getDisplayConnection(api, machineId):
# <NIC_ID><![CDATA[2]]></NIC_ID> # <NIC_ID><![CDATA[2]]></NIC_ID>
# <VLAN><![CDATA[NO]]></VLAN> # <VLAN><![CDATA[NO]]></VLAN>
# </NIC> # </NIC>