Merge remote-tracking branch 'origin/v2.2'

This commit is contained in:
Adolfo Gómez García 2018-03-16 09:50:29 +01:00
commit 83ad9141a8
14 changed files with 188 additions and 102 deletions

View File

@ -38,8 +38,7 @@ from .OVirtJobs import OVirtDeferredRemoval
import pickle
import logging
__updated__ = '2017-03-22'
__updated__ = '2018-03-16'
logger = logging.getLogger(__name__)
@ -185,6 +184,13 @@ class OVirtLinkedDeployment(UserDeployment):
self.cache.put('ready', '1')
return State.FINISHED
def reset(self):
'''
o oVirt, reset operation just shutdowns it until v3 support is removed
'''
if self._vmid != '':
self.service().stopMachine(self._vmid)
def getConsoleConnection(self):
return self.service().getConsoleConnection(self._vmid)

View File

@ -41,7 +41,7 @@ from uds.core.ui import gui
import logging
__updated__ = '2017-11-07'
__updated__ = '2018-03-16'
logger = logging.getLogger(__name__)
@ -88,6 +88,7 @@ class OVirtLinkedService(Service):
# : If true, the system can't do an automatic assignation of a deployed user
# : service from this service
mustAssignManually = False
canReset = True
# : Types of publications (preparated data for deploys)
# : In our case, we do no need a publication, so this is None

View File

@ -39,8 +39,7 @@ from . import on
import pickle
import logging
__updated__ = '2017-03-27'
__updated__ = '2018-03-16'
logger = logging.getLogger(__name__)
@ -181,6 +180,10 @@ class LiveDeployment(UserDeployment):
self.cache.put('ready', '1')
return State.FINISHED
def reset(self):
if self._vmid != '':
self.service().resetMachine(self._vmid)
def getConsoleConnection(self):
return self.service().getConsoleConnection(self._vmid)

View File

@ -81,6 +81,7 @@ class LiveService(Service):
# : If true, the system can't do an automatic assignation of a deployed user
# : service from this service
mustAssignManually = False
canReset = True
# : Types of publications (preparated data for deploys)
# : In our case, we do no need a publication, so this is None
@ -111,7 +112,7 @@ class LiveService(Service):
label=_('Machine Names'),
rdonly=False,
order=111,
tooltip='Base name for clones from this machine',
tooltip=_('Base name for clones from this machine'),
tab=_('Machine'),
required=True
)
@ -255,6 +256,9 @@ class LiveService(Service):
"""
return self.parent().suspendMachine(machineId)
def resetMachine(self, machineId):
return self.parent().resetMachine(machineId)
def removeMachine(self, machineId):
"""
Tries to delete a machine. No check is done, it is simply requested to OpenNebula

View File

@ -43,17 +43,17 @@ from defusedxml import minidom
from .LiveService import LiveService
from . import on
import logging
import six
# Python bindings for OpenNebula
# import oca
__updated__ = '2017-03-28'
__updated__ = '2018-03-16'
logger = logging.getLogger(__name__)
class Provider(ServiceProvider):
"""
This class represents the sample services provider
@ -125,7 +125,6 @@ class Provider(ServiceProvider):
def endpoint(self):
return 'http{}://{}:{}/RPC2'.format('s' if self.ssl.isTrue() else '', self.host.value, self.port.value)
@property
def api(self):
if self._api is None:
@ -194,7 +193,6 @@ class Provider(ServiceProvider):
"""
return on.vm.getMachineSubstate(self.api, machineId)
def startMachine(self, machineId):
"""
Tries to start a machine. No check is done, it is simply requested to OpenNebula.
@ -207,6 +205,7 @@ class Provider(ServiceProvider):
Returns:
"""
on.vm.startMachine(self.api, machineId)
return True
def stopMachine(self, machineId):
"""
@ -218,6 +217,7 @@ class Provider(ServiceProvider):
Returns:
"""
on.vm.stopMachine(self.api, machineId)
return True
def suspendMachine(self, machineId):
"""
@ -229,6 +229,13 @@ class Provider(ServiceProvider):
Returns:
"""
on.vm.suspendMachine(self.api, machineId)
return True
def resetMachine(self, machineId):
'''
Resets a machine (hard-reboot)
'''
on.vm.resetMachine(self.api, machineId)
def removeMachine(self, machineId):
"""
@ -240,6 +247,7 @@ class Provider(ServiceProvider):
Returns:
"""
on.vm.removeMachine(self.api, machineId)
return True
def getNetInfo(self, machineId, networkId=None):
"""
@ -263,7 +271,6 @@ class Provider(ServiceProvider):
}
}
@staticmethod
def test(env, data):
"""

View File

@ -27,9 +27,9 @@
# 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
@ -39,13 +39,13 @@ from defusedxml import minidom
# Python bindings for OpenNebula
from .common import VmState
__updated__ = '2017-03-28'
__updated__ = '2018-03-16'
logger = logging.getLogger(__name__)
def getMachineState(api, machineId):
"""
'''
Returns the state of the machine
This method do not uses cache at all (it always tries to get machine state from OpenNebula server)
@ -54,32 +54,32 @@ def getMachineState(api, machineId):
Returns:
one of the on.VmState Values
"""
'''
try:
# vm = oca.VirtualMachine.new_with_id(api, int(machineId))
# vm.info()
# 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 {} on OpenNebula: {}'.format(machineId, e))
return VmState.UNKNOWN
def getMachineSubstate(api, machineId):
"""
'''
Returns the lcm_state
"""
'''
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 state for {} on OpenNebula: {}'.format(machineId, e))
return VmState.UNKNOWN
def startMachine(api, machineId):
"""
'''
Tries to start a machine. No check is done, it is simply requested to OpenNebula.
This start also "resume" suspended/paused machines
@ -88,63 +88,78 @@ def startMachine(api, machineId):
machineId: Id of the machine
Returns:
"""
'''
try:
# vm = oca.VirtualMachine.new_with_id(api, int(machineId))
# vm.resume()
api.VMAction(machineId, 'resume')
except Exception as e:
# logger.error('Error obtaining machine state for {} on opennebula: {}'.format(machineId, e))
# MAybe the machine is already running. If we get error here, simply ignore it for now...
pass
def stopMachine(api, machineId):
"""
'''
Tries to start a machine. No check is done, it is simply requested to OpenNebula
Args:
machineId: Id of the machine
Returns:
"""
'''
try:
# vm = oca.VirtualMachine.new_with_id(api, int(machineId))
# vm.poweroff_hard()
api.VMAction(machineId, 'poweroff-hard')
except Exception as e:
logger.error('Error obtaining machine state for {} on opennebula: {}'.format(machineId, e))
logger.error('Error powering off {} on OpenNebula: {}'.format(machineId, e))
def suspendMachine(api, machineId):
"""
Tries to start a machine. No check is done, it is simply requested to OpenNebula
'''
Tries to suspend a machine. No check is done, it is simply requested to OpenNebula
Args:
machineId: Id of the machine
Returns:
"""
startMachine(api, machineId)
'''
try:
api.VMAction(machineId, 'suspend')
except Exception as e:
logger.error('Error suspending {} on OpenNebula: {}'.format(machineId, e))
def resetMachine(api, machineId):
'''
Tries to suspend a machine. No check is done, it is simply requested to OpenNebula
Args:
machineId: Id of the machine
Returns:
'''
try:
api.VMAction(machineId, 'reboot-hard')
except Exception as e:
logger.error('Error reseting {} on OpenNebula: {}'.format(machineId, e))
def removeMachine(api, machineId):
"""
'''
Tries to delete a machine. No check is done, it is simply requested to OpenNebula
Args:
machineId: Id of the machine
Returns:
"""
'''
try:
# vm = oca.VirtualMachine.new_with_id(api, int(machineId))
# vm.delete()
api.deleteVM(machineId)
except Exception as e:
logger.exception('Error removing machine {} on opennebula: {}'.format(machineId, e))
raise 'Error removing machine {} on opennebula: {}'.format(machineId, e)
logger.exception('Error removing machine {} on OpenNebula: {}'.format(machineId, e))
raise 'Error removing machine {} on OpenNebula: {}'.format(machineId, e)
def enumerateMachines(api):
"""
'''
Obtains the list of machines inside OpenNebula.
Machines starting with UDS are filtered out
@ -157,14 +172,14 @@ def enumerateMachines(api):
'name'
'id'
'cluster_id'
"""
'''
return api.enumVMs()
def getNetInfo(api, machineId, networkId=None):
"""
'''
Changes the mac address of first nic of the machine to the one specified
"""
'''
# md = minidom.parseString(api.call('vm.info', int(machineId)))
md = minidom.parseString(api.VMInfo(machineId)[1])
node = md
@ -187,16 +202,15 @@ def getNetInfo(api, machineId, networkId=None):
except Exception:
ip = ''
return node.getElementsByTagName('MAC')[0].childNodes[0].data, ip
return (node.getElementsByTagName('MAC')[0].childNodes[0].data, ip)
except Exception:
raise Exception('No network interface found on template. Please, add a network and republish.')
def getDisplayConnection(api, machineId):
"""
'''
If machine is not running or there is not a display, will return NONE
SPICE connections should check that 'type' is 'SPICE'
"""
'''
md = minidom.parseString(api.VMInfo(machineId)[1])
try:
graphics = md.getElementsByTagName('GRAPHICS')[0]
@ -219,8 +233,6 @@ def getDisplayConnection(api, machineId):
except Exception:
return None # No SPICE connection
# Sample NIC Content (there will be as much as nics)
# <NIC>
# <BRIDGE><![CDATA[br0]]></BRIDGE>
@ -235,5 +247,3 @@ def getDisplayConnection(api, machineId):
# <VLAN><![CDATA[NO]]></VLAN>
# </NIC>

View File

@ -39,7 +39,7 @@ from . import openStack
import pickle
import logging
__updated__ = '2017-11-21'
__updated__ = '2018-03-16'
logger = logging.getLogger(__name__)
@ -190,6 +190,10 @@ class LiveDeployment(UserDeployment):
self.cache.put('ready', '1')
return State.FINISHED
def reset(self):
if self._vmid != '':
self.service().resetMachine(self._vmid)
def getConsoleConnection(self):
return self.service().getConsoleConnection(self._vmid)

View File

@ -27,9 +27,9 @@
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
'''
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
"""
'''
from django.utils.translation import ugettext_noop as _, ugettext
from uds.core.transports import protocols
from uds.core.services import Service, types as serviceTypes
@ -42,15 +42,15 @@ from uds.core.ui import gui
import six
import logging
__updated__ = '2017-05-16'
__updated__ = '2018-03-16'
logger = logging.getLogger(__name__)
class LiveService(Service):
"""
'''
OpenStack Live Service
"""
'''
# : Name to show the administrator. This string will be translated BEFORE
# : sending it to administration interface, so don't forget to
# : mark it as _ (using ugettext_noop)
@ -85,6 +85,7 @@ class LiveService(Service):
# : If true, the system can't do an automatic assignation of a deployed user
# : service from this service
mustAssignManually = False
canReset = True
# : Types of publications (preparated data for deploys)
# : In our case, we do no need a publication, so this is None
@ -143,12 +144,12 @@ class LiveService(Service):
ev = gui.HiddenField(value=None) # We need to keep the env so we can instantiate the Provider
def initialize(self, values):
"""
'''
We check here form values to see if they are valid.
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:
length = int(self.lenName.value)
if len(self.baseName.value) + length > 15:
@ -161,11 +162,10 @@ class LiveService(Service):
self._api = None
def initGui(self):
"""
'''
Loads required values inside
"""
'''
api = self.parent().api()
regions = [gui.choiceItem(r['id'], r['id']) for r in api.listRegions()]
self.region.setValues(regions)
@ -198,13 +198,13 @@ class LiveService(Service):
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
Args:
@ -214,7 +214,7 @@ class LiveService(Service):
Returns:
Id of the machine being created form template
"""
'''
logger.debug('Deploying from template {0} machine {1}'.format(snapshotId, name))
# self.datastoreHasSpace()
return self.api.createServerFromSnapshot(snapshotId=snapshotId,
@ -225,13 +225,13 @@ class LiveService(Service):
securityGroupsIdsList=self.securityGroups.value)['id']
def removeTemplate(self, templateId):
"""
'''
invokes removeTemplate from parent provider
"""
'''
self.api.deleteSnapshot(templateId)
def getMachineState(self, machineId):
"""
'''
Invokes getServer from openstack client
Args:
@ -256,11 +256,11 @@ class LiveService(Service):
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.api.getServer(machineId)['status']
def startMachine(self, machineId):
"""
'''
Tries to start a machine. No check is done, it is simply requested to OpenStack.
This start also "resume" suspended/paused machines
@ -269,70 +269,80 @@ class LiveService(Service):
machineId: Id of the machine
Returns:
"""
return self.api.startServer(machineId)
'''
self.api.startServer(machineId)
def stopMachine(self, machineId):
"""
'''
Tries to stop a machine. No check is done, it is simply requested to OpenStack
Args:
machineId: Id of the machine
Returns:
"""
return self.api.stopServer(machineId)
'''
self.api.stopServer(machineId)
def resetMachine(self, machineId):
'''
Tries to stop a machine. No check is done, it is simply requested to OpenStack
Args:
machineId: Id of the machine
Returns:
'''
self.api.resetServer(machineId)
def suspendMachine(self, machineId):
"""
'''
Tries to suspend a machine. No check is done, it is simply requested to OpenStack
Args:
machineId: Id of the machine
Returns:
"""
return self.api.suspendServer(machineId)
'''
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)
'''
self.api.resumeServer(machineId)
def removeMachine(self, machineId):
"""
'''
Tries to delete a machine. No check is done, it is simply requested to OpenStack
Args:
machineId: Id of the machine
Returns:
"""
return self.api.deleteServer(machineId)
'''
self.api.deleteServer(machineId)
def getNetInfo(self, machineId):
"""
'''
Gets the mac address of first nic of the machine
"""
'''
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'].upper(), vals['addr']
return (vals['OS-EXT-IPS-MAC:mac_addr'].upper(), vals['addr'])
def getBaseName(self):
"""
'''
Returns the base name
"""
'''
return self.baseName.value
def getLenName(self):
"""
'''
Returns the length of numbers part
"""
'''
return int(self.lenName.value)

View File

@ -39,7 +39,7 @@ import json
import dateutil.parser
import six
__updated__ = '2018-03-02'
__updated__ = '2018-03-16'
logger = logging.getLogger(__name__)
@ -519,6 +519,17 @@ class Client(object):
ensureResponseIsValid(r, 'Resuming server')
@authProjectRequired
def resetServer(self, serverId):
r = requests.post(self._getEndpointFor('compute') + '/servers/{server_id}/action'.format(server_id=serverId),
data='{"reboot":{"type":"HARD"}}',
headers=self._requestHeaders(),
verify=VERIFY_SSL,
timeout=self._timeout)
# Ignore response for this...
# ensureResponseIsValid(r, 'Reseting server')
def testConnection(self):
# First, ensure requested api is supported
# We need api version 3.2 or greater

View File

@ -197,6 +197,10 @@ class XenLinkedDeployment(UserDeployment):
return State.FINISHED
def reset(self):
if self._vmid != '':
self.service().resetVM(self._vmid) # Reset in sync
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))

View File

@ -88,6 +88,7 @@ class XenLinkedService(Service):
# : If true, the system can't do an automatic assignation of a deployed user
# : service from this service
mustAssignManually = False
canReset = True
# : Types of publications (preparated data for deploys)
# : In our case, we do no need a publication, so this is None
@ -97,7 +98,6 @@ class XenLinkedService(Service):
servicesTypeProvided = (serviceTypes.VDI,)
# Now the form part
datastore = gui.ChoiceField(
label=_("Storage SR"),
@ -216,12 +216,9 @@ class XenLinkedService(Service):
self.datastore.setValues(storages_list)
self.network.setValues(network_list)
def checkTaskFinished(self, task):
return self.parent().checkTaskFinished(task)
def datastoreHasSpace(self):
# Get storages for that datacenter
logger.debug('Checking datastore space for {0}'.format(self.datastore.value))
@ -237,7 +234,6 @@ class XenLinkedService(Service):
"""
return name
def startDeployTemplate(self, name, comments):
"""
Invokes makeTemplate from parent provider, completing params
@ -325,6 +321,17 @@ class XenLinkedService(Service):
"""
return self.parent().stopVM(machineId, async)
def resetVM(self, machineId, async=True):
'''
Tries to stop a machine. No check is done, it is simply requested to Xen
Args:
machineId: Id of the machine
Returns:
'''
return self.parent().resetVM(machineId, async)
def canSuspendVM(self, machineId):
"""
The machine can be suspended only when "suspend" is in their operations list (mush have xentools installed)
@ -359,7 +366,6 @@ class XenLinkedService(Service):
"""
return self.parent().suspendVM(machineId, async)
def removeVM(self, machineId):
"""
Tries to delete a machine. No check is done, it is simply requested to Xen

View File

@ -49,8 +49,7 @@ import logging
logger = logging.getLogger(__name__)
__updated__ = '2017-03-06'
__updated__ = '2018-03-16'
CACHE_TIME_FOR_SERVER = 1800
@ -318,6 +317,17 @@ class Provider(ServiceProvider):
"""
return self.__getApi().stopVM(machineId, async)
def resetVM(self, machineId, async=True):
'''
Tries to start a machine. No check is done, it is simply requested to XenServer
Args:
machineId: Id of the machine
Returns:
'''
return self.__getApi().resetVM(machineId, async)
def canSuspendVM(self, machineId):
"""
The machine can be suspended only when "suspend" is in their operations list (mush have xentools installed)

View File

@ -88,6 +88,7 @@ class XenFailure(XenAPI.Failure, XenFault):
class XenException(XenFault):
def __init__(self, message):
XenFault.__init__(self, message)
logger.debug('Exception create: {0}'.format(message))
@ -101,6 +102,7 @@ class XenPowerState(object):
class XenServer(object):
def __init__(self, host, port, username, password, useSSL=False, verifySSL=False):
self._originalHost = self._host = host
self._port = six.text_type(port)
@ -331,6 +333,14 @@ class XenServer(object):
return self.Async.VM.hard_shutdown(vmId)
return self.VM.hard_shutdown(vmId)
def resetVM(self, vmId, async=True):
vmState = self.getVMPowerState(vmId)
if vmState in (XenPowerState.suspended, XenPowerState.halted):
return None # Already powered off
if async:
return self.Async.VM.hard_reboot(vmId)
return self.VM.hard_reboot(vmId)
def canSuspendVM(self, vmId):
operations = self.VM.get_allowed_operations(vmId)
logger.debug('Operations: {}'.format(operations))

View File

@ -42,7 +42,7 @@
<img {% if ser.name != ser.visual_name %}class="with-tooltip" data-content="{{ser.name|capfirst}}"{% endif %} class="se-agranda" src="{% url "uds.web.views.serviceImage" idImage=ser.imageId %}" />
</div>
<span {% if ser.show_transports and numTransports > 1 %} style="width: 130px;"{% endif %}>{{ ser.visual_name|capfirst|truncatechars:16 }}</span>
{% if ser.show_transports and numTransports > 1 or ser.allow_users_remove %}
{% if ser.show_transports and numTransports > 1 or ser.allow_users_remove or ser.allow_users_reset %}
<span class="gear">
{% if ser.show_transports and numTransports > 1 %}
<span class="connection fa fa-gear"> </span>