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 -*-
#
# Copyright (c) 2012 Virtual Cable S.L.
# Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved.
#
# 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
"""
import pickle
import logging
import typing
from uds.core.services import UserDeployment
from uds.core.util.State import State
from uds.core.util import log
from . import on
import pickle
import logging
__updated__ = '2019-02-07'
logger = logging.getLogger(__name__)
@ -47,7 +47,6 @@ opCreate, opStart, opSuspend, opRemove, opWait, opError, opFinish, opRetry = ran
NO_MORE_NAMES = 'NO-NAME-ERROR'
class LiveDeployment(UserDeployment):
"""
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.
"""
# : Recheck every six seconds by default (for task methods)
suggestedTime = 6
#
_name = ''
_ip = ''
_mac = ''
_vmid = ''
_reason = ''
_queue: typing.List[int] = []
def initialize(self):
self._name = ''
self._ip = ''
@ -86,11 +93,11 @@ class LiveDeployment(UserDeployment):
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
"""
vals = str_.split(b'\1')
vals = data.split(b'\1')
if vals[0] == b'v1':
self._name = vals[1].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
access it.
"""
logger.debug('Setting IP to {}'.format(ip))
logger.debug('Setting IP to %s', ip)
self._ip = ip
def getUniqueId(self):
@ -204,7 +211,7 @@ class LiveDeployment(UserDeployment):
def notifyReadyFromOsManager(self, data):
# 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:
logger.debug('Machine is ready. Moving to level 2')
self.__popCurrentOp() # Remove current state
@ -235,7 +242,7 @@ class LiveDeployment(UserDeployment):
self._queue = [opCreate, opStart, opWait, opSuspend, opFinish]
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)
# 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
def __getCurrentOp(self):
if len(self._queue) == 0:
if not self._queue:
return opFinish
return self._queue[0]
def __popCurrentOp(self):
if len(self._queue) == 0:
if not self._queue:
return opFinish
res = self._queue.pop(0)
@ -279,14 +286,14 @@ class LiveDeployment(UserDeployment):
Returns:
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)
if self._vmid != '': # Powers off & delete it
try:
self.service().removeMachine(self._vmid)
except:
logger.debug('Can\t set machine state to stopped')
except Exception:
logger.debug('Can\'t set machine state to stopped')
self._queue = [opError]
self._reason = str(reason)
@ -371,7 +378,7 @@ class LiveDeployment(UserDeployment):
if state == on.VmState.ACTIVE: # @UndefinedVariable
subState = self.service().getMachineSubstate(self._vmid)
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)
return
@ -457,7 +464,6 @@ class LiveDeployment(UserDeployment):
(No matter wether it is for cache or for an user)
"""
self.__debug('finish')
pass
def moveToCache(self, newLevel):
"""
@ -529,8 +535,8 @@ class LiveDeployment(UserDeployment):
}.get(op, '????')
def __debug(self, txt):
logger.debug('_name {0}: {1}'.format(txt, self._name))
logger.debug('_ip {0}: {1}'.format(txt, self._ip))
logger.debug('_mac {0}: {1}'.format(txt, self._mac))
logger.debug('_vmid {0}: {1}'.format(txt, self._vmid))
logger.debug('Queue at {0}: {1}'.format(txt, [LiveDeployment.__op2str(op) for op in self._queue]))
logger.debug('_name %s: %s', txt, self._name)
logger.debug('_ip %s: %s', txt, self._ip)
logger.debug('_mac %s: %s', txt, self._mac)
logger.debug('_vmid %s: %s', txt, self._vmid)
logger.debug('Queue at %s: %s', txt, [LiveDeployment.__op2str(op) for op in self._queue])

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Virtual Cable S.L.
# Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved.
#
# 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
"""
import logging
from django.utils.translation import ugettext as _
from uds.core.services import Publication
from uds.core.util.State import State
import six
import logging
__updated__ = '2019-02-07'
logger = logging.getLogger(__name__)
@ -51,6 +46,11 @@ class LivePublication(Publication):
suggestedTime = 2 # : Suggested recheck time if publication is unfinished in seconds
_name = ''
_reason = ''
_templateId = ''
_state = ''
def initialize(self):
"""
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.
"""
logger.debug('Data: {0}'.format(data))
logger.debug('Data: %s', data)
vals = data.decode('utf8').split('\t')
if vals[0] == 'v1':
self._name, self._reason, self._templateId, self._state = vals[1:]
@ -93,7 +93,7 @@ class LivePublication(Publication):
self._templateId = self.service().makeTemplate(self._name)
except Exception as e:
self._state = 'error'
self._reason = six.text_type(e)
self._reason = str(e)
return State.ERROR
return State.RUNNING
@ -109,7 +109,7 @@ class LivePublication(Publication):
self._state = 'ok'
except Exception as e:
self._state = 'error'
self._reason = six.text_type(e)
self._reason = str(e)
if self._state == 'error':
return State.ERROR
@ -124,7 +124,6 @@ class LivePublication(Publication):
"""
In our case, finish does nothing
"""
pass
def reasonOfError(self):
"""

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Virtual Cable S.L.
# Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved.
#
# 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
"""
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.services import Service, types as serviceTypes
from uds.core.ui import gui
from .LivePublication import LivePublication
from .LiveDeployment import LiveDeployment
from uds.core.ui import gui
import logging
__updated__ = '2018-08-20'
logger = logging.getLogger(__name__)
@ -186,7 +185,7 @@ class LiveService(Service):
Returns:
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()
return self.parent().deployFromTemplate(name, templateId)

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Virtual Cable S.L.
# Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved.
#
# 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.
'''
Created on Jun 22, 2012
.. 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 uds.core.services import ServiceProvider
from uds.core.ui import gui
from uds.core.util import validators
from defusedxml import minidom
from .LiveService import LiveService
from . import on
import logging
import six
# Python bindings for OpenNebula
# import oca
__updated__ = '2018-08-20'
logger = logging.getLogger(__name__)
@ -119,7 +108,7 @@ class Provider(ServiceProvider):
if values is not None:
self.timeout.value = validators.validateTimeout(self.timeout.value, returnAsInteger=False)
logger.debug('Endpoint: {}'.format(self.endpoint))
logger.debug('Endpoint: %s', self.endpoint)
@property
def endpoint(self):
@ -130,7 +119,7 @@ class Provider(ServiceProvider):
if self._api is None:
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
def resetApi(self):

View File

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

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Virtual Cable S.L.
# Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -32,16 +32,16 @@
"""
# pylint: disable=maybe-no-member
import six
import types
import xmlrpc.client
from uds.core.util import xml2dict
from . import storage
from . import template
from . import vm
# Import submodules
from .common import *
import types
__updated__ = '2017-03-28'
@ -72,14 +72,14 @@ def checkResult(lst, parseResult=True):
raise Exception('OpenNebula error {}: "{}"'.format(lst[2], lst[1]))
if parseResult:
return xml2dict.parse(lst[1])
else:
return lst[1]
return lst[1]
def asList(element):
if isinstance(element, (tuple, list)):
return element
return element,
return (element,)
# noinspection PyShadowingNames
@ -95,7 +95,7 @@ class OpenNebulaClient(object):
def sessionString(self):
return '{}:{}'.format(self.username, self.password)
@property
@property # type: ignore
@ensureConnected
def version(self):
if self.cachedVersion is None:
@ -108,11 +108,11 @@ class OpenNebulaClient(object):
if self.connection is not None:
return
self.connection = six.moves.xmlrpc_client.ServerProxy(self.endpoint) # @UndefinedVariable
self.connection = xmlrpc.client.ServerProxy(self.endpoint) # @UndefinedVariable
@ensureConnected
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
result = self.connection.one.datastorepool.info(self.sessionString)
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.
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(result)
result = checkResult(self.connection.one.templatepool.info(self.sessionString, -1, -1, -1))
for ds in asList(result['VMTEMPLATE_POOL']['VMTEMPLATE']):
try:
yield(ds['ID'], ds['NAME'], ds['TEMPLATE']['MEMORY'])
@ -182,7 +181,7 @@ class OpenNebulaClient(object):
return checkResult(result, parseResult=False)
@ensureConnected
def updateTemplate(self, templateId, template, updateType=0):
def updateTemplate(self, templateId, templateData, updateType=0):
"""
Updates the template with the templateXml
1.- Session string
@ -190,7 +189,7 @@ class OpenNebulaClient(object):
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
"""
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)
@ensureConnected
@ -208,6 +207,7 @@ class OpenNebulaClient(object):
@ensureConnected
def deleteTemplate(self, templateId):
"""
Deletes the template (not images)
"""
result = self.connection.one.template.delete(self.sessionString, int(templateId))
return checkResult(result, parseResult=False)

View File

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

View File

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

View File

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

View File

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