Fixed OpenGnsys Provider

This commit is contained in:
Adolfo Gómez García 2019-11-06 13:01:29 +01:00
parent 2dc1634004
commit 99fe68608c
9 changed files with 250 additions and 244 deletions

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# Copyright (c) 2017-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -28,5 +28,4 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from .Provider import OGProvider
from .provider import OGProvider

View File

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Virtual Cable S.L.
# All rights reserved.
@ -32,16 +31,19 @@
"""
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 uds.models.util import getSqlDatetimeAsUnix
from . import og
__updated__ = '2019-02-07'
# Not imported at runtime, just for type checking
if typing.TYPE_CHECKING:
from uds import models
from .service import OGService
from .publication import OGPublication
from uds.core.util.storage import Storage
logger = logging.getLogger(__name__)
@ -63,18 +65,32 @@ class OGDeployment(UserDeployment):
# : Recheck every N seconds by default (for task methods)
suggestedTime = 20
def initialize(self):
self._name = 'unknown'
self._ip = ''
self._mac = ''
self._machineId = ''
self._stamp = 0
self._reason = ''
_name: str = 'unknown'
_ip: str = ''
_mac: str = ''
_machineId: str = ''
_stamp: int = 0
_reason: str = ''
_queue: typing.List[int] # Do not initialize mutable, just declare and it is initialized on "initialize"
_uuid: str
def initialize(self) -> None:
self._queue = []
self._uuid = self.dbservice().uuid
dbs = self.dbservice()
self._uuid = dbs.uuid if dbs else ''
def service(self) -> 'OGService':
return typing.cast('OGService', super().service())
def publication(self) -> 'OGPublication':
pub = super().publication()
if pub is None:
raise Exception('No publication for this element!')
return typing.cast('OGPublication', pub)
# Serializable needed methods
def marshal(self):
def marshal(self) -> bytes:
"""
Does nothing right here, we will use environment storage in this sample
"""
@ -89,11 +105,11 @@ class OGDeployment(UserDeployment):
pickle.dumps(self._queue, protocol=0)
])
def unmarshal(self, str_):
def unmarshal(self, data: bytes) -> None:
"""
Does nothing here also, all data are kept 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')
@ -103,27 +119,29 @@ class OGDeployment(UserDeployment):
self._stamp = int(vals[6].decode('utf8'))
self._queue = pickle.loads(vals[7])
def getName(self):
def getName(self) -> str:
return self._name
def getUniqueId(self):
def getUniqueId(self) -> str:
return self._mac.upper()
def getIp(self):
def getIp(self) -> str:
return self._ip
def setReady(self):
def setReady(self) -> str:
"""
Notifies the current "deadline" to the user, before accessing by UDS
The machine has been already been started.
The problem is that currently there is no way that a machine is in FACT started.
OpenGnsys will try it best by sending an WOL
"""
self.service().notifyDeadline(self._machineId, self.dbservice().deployed_service.getDeadline())
dbs = self.dbservice()
deadline = dbs.deployed_service.getDeadline() if dbs else 0
self.service().notifyDeadline(self._machineId, deadline)
return State.FINISHED
def deployForUser(self, user):
def deployForUser(self, user: 'models.User') -> str:
"""
Deploys an service instance for an user.
"""
@ -131,19 +149,18 @@ class OGDeployment(UserDeployment):
self.__initQueueForDeploy()
return self.__executeQueue()
def deployForCache(self, cacheLevel):
def deployForCache(self, cacheLevel: int) -> str:
"""
Deploys an service instance for cache
"""
self.__initQueueForDeploy() # No Level2 Cache possible
return self.__executeQueue()
def __initQueueForDeploy(self):
def __initQueueForDeploy(self) -> None:
self._queue = [opCreate, opFinish]
def __checkMachineReady(self):
logger.debug('Checking that state of machine {} ({}) is ready'.format(self._machineId, self._name))
def __checkMachineReady(self) -> str:
logger.debug('Checking that state of machine %s (%s) is ready', self._machineId, self._name)
try:
status = self.service().status(self._machineId)
@ -157,33 +174,33 @@ class OGDeployment(UserDeployment):
return State.RUNNING
def __getCurrentOp(self):
def __getCurrentOp(self) -> int:
if len(self._queue) == 0:
return opFinish
return self._queue[0]
def __popCurrentOp(self):
def __popCurrentOp(self) -> int:
if len(self._queue) == 0:
return opFinish
res = self._queue.pop(0)
return res
def __pushFrontOp(self, op):
def __pushFrontOp(self, op: int) -> None:
self._queue.insert(0, op)
def __pushBackOp(self, op):
def __pushBackOp(self, op: int) -> None:
self._queue.append(op)
def __error(self, reason):
def __error(self, reason: typing.Any) -> str:
"""
Internal method to set object as error state
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)
# TODO: Unreserve machine?? Maybe it just better to keep it assigned so UDS don't get it again in a while...
@ -192,7 +209,7 @@ class OGDeployment(UserDeployment):
self._reason = str(reason)
return State.ERROR
def __executeQueue(self):
def __executeQueue(self) -> str:
self.__debug('executeQueue')
op = self.__getCurrentOp()
@ -202,14 +219,14 @@ class OGDeployment(UserDeployment):
if op == opFinish:
return State.FINISHED
fncs = {
fncs: typing.Dict[int, typing.Optional[typing.Callable[[], str]]] = {
opCreate: self.__create,
opRetry: self.__retry,
opRemove: self.__remove,
}
try:
execFnc = fncs.get(op, None)
execFnc: typing.Optional[typing.Callable[[], str]] = fncs.get(op, None)
if execFnc is None:
return self.__error('Unknown operation found at execution queue ({0})'.format(op))
@ -218,11 +235,11 @@ class OGDeployment(UserDeployment):
return State.RUNNING
except Exception as e:
logger.exception('Got Exception')
# logger.exception('Got Exception')
return self.__error(e)
# Queue execution methods
def __retry(self):
def __retry(self) -> str:
"""
Used to retry an operation
In fact, this will not be never invoked, unless we push it twice, because
@ -232,7 +249,7 @@ class OGDeployment(UserDeployment):
"""
return State.FINISHED
def __create(self):
def __create(self) -> str:
"""
Deploys a machine from template for user/cache
"""
@ -241,7 +258,7 @@ class OGDeployment(UserDeployment):
self.service().notifyEvents(r['id'], self._uuid)
except Exception as e:
# logger.exception('Creating machine')
return self.__error('Error creating reservation: {}'.format(e))
raise Exception('Error creating reservation: {}'.format(e))
self._machineId = r['id']
self._name = r['name']
@ -250,29 +267,34 @@ class OGDeployment(UserDeployment):
self._stamp = getSqlDatetimeAsUnix()
# Store actor version & Known ip
self.dbservice().setProperty('actor_version', '1.0-OpenGnsys')
self.dbservice().logIP(self._ip)
dbs = self.dbservice()
if dbs:
dbs.setProperty('actor_version', '1.0-OpenGnsys')
dbs.logIP(self._ip)
def __remove(self):
return State.RUNNING
def __remove(self) -> str:
"""
Removes a machine from system
"""
self.service().unreserve(self._machineId)
return State.RUNNING
# Check methods
def __checkCreate(self):
def __checkCreate(self) -> str:
"""
Checks the state of a deploy for an user or cache
"""
return self.__checkMachineReady()
def __checkRemoved(self):
def __checkRemoved(self) -> str:
"""
Checks if a machine has been removed
"""
return State.FINISHED # No check at all, always true
def checkState(self):
def checkState(self) -> str:
"""
Check what operation is going on, and acts acordly to it
"""
@ -285,14 +307,14 @@ class OGDeployment(UserDeployment):
if op == opFinish:
return State.FINISHED
fncs = {
fncs: typing.Dict[int, typing.Optional[typing.Callable[[], str]]] = {
opCreate: self.__checkCreate,
opRetry: self.__retry,
opRemove: self.__checkRemoved,
}
try:
chkFnc = fncs.get(op, None)
chkFnc: typing.Optional[typing.Optional[typing.Callable[[], str]]] = fncs.get(op, None)
if chkFnc is None:
return self.__error('Unknown operation found at check queue ({0})'.format(op))
@ -306,15 +328,7 @@ class OGDeployment(UserDeployment):
except Exception as e:
return self.__error(e)
def finish(self):
"""
Invoked when the core notices that the deployment of a service has finished.
(No matter wether it is for cache or for an user)
"""
self.__debug('finish')
pass
def reasonOfError(self):
def reasonOfError(self) -> str:
"""
Returns the reason of the error.
@ -324,7 +338,7 @@ class OGDeployment(UserDeployment):
"""
return self._reason
def destroy(self):
def destroy(self) -> str:
"""
Invoked for destroying a deployed service
"""
@ -334,7 +348,7 @@ class OGDeployment(UserDeployment):
self._queue = [opRemove, opFinish]
return self.__executeQueue()
def cancel(self):
def cancel(self) -> str:
"""
This is a task method. As that, the excepted return values are
State values RUNNING, FINISHED or ERROR.
@ -347,7 +361,7 @@ class OGDeployment(UserDeployment):
return self.destroy()
@staticmethod
def __op2str(op):
def __op2str(op: int) -> str:
return {
opCreate: 'create',
opRemove: 'remove',
@ -357,8 +371,4 @@ class OGDeployment(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('_machineId {0}: {1}'.format(txt, self._machineId))
logger.debug('Queue at {0}: {1}'.format(txt, [OGDeployment.__op2str(op) for op in self._queue]))
logger.debug('State at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s', txt, self._name, self._ip, self._mac, self._vmId, [OGDeployment.__op2str(op) for op in self._queue])

View File

@ -1,27 +1,51 @@
# -*- 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.
"""
@author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import logging
import typing
from django.utils.translation import ugettext as _
from uds.core.environment import Environment
from uds.core.ui import gui
# Not imported at runtime, just for type checking
if typing.TYPE_CHECKING:
from .provider import OGProvider
logger = logging.getLogger(__name__)
def getResources(parameters):
"""
This helper is designed as a callback for Project Selector
"""
from .Provider import OGProvider
from uds.core.environment import Environment
def getResources(parameters: typing.Any) -> typing.List[typing.Dict[str, typing.Any]]:
from .provider import OGProvider
logger.debug('Parameters received by getResources Helper: %s', parameters)
env = Environment(parameters['ev'])
provider = OGProvider(env)
@ -37,4 +61,5 @@ def getResources(parameters):
{'name': 'image', 'values': images},
]
logger.debug('Return data: %s', data)
return data

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# Copyright (c) 2017-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -30,40 +30,39 @@
"""
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
"""
from __future__ import unicode_literals
import re
import json
import logging
import typing
import requests
from . import urls
from . import fake
import re
import logging
import six
import requests
import json
logger = logging.getLogger(__name__)
# URLS
if typing.TYPE_CHECKING:
from uds.core.util.cache import Cache
# Fake part
FAKE = False
CACHE_VALIDITY = 180
RT = typing.TypeVar('RT')
# Decorator
def ensureConnected(fnc):
def inner(*args, **kwargs):
def ensureConnected(fnc: typing.Callable[..., RT]) -> typing.Callable[..., RT]:
def inner(*args, **kwargs) -> RT:
args[0].connect()
return fnc(*args, **kwargs)
return inner
# Result checker
def ensureResponseIsValid(response, errMsg=None):
if response.ok is False:
if errMsg is None:
def ensureResponseIsValid(response: requests.Response, errMsg: typing.Optional[str] = None) -> typing.Any:
if not response.ok:
if not errMsg:
errMsg = 'Invalid response'
try:
@ -71,7 +70,7 @@ def ensureResponseIsValid(response, errMsg=None):
except Exception:
err = response.content
errMsg = '{}: {}, ({})'.format(errMsg, err, response.status_code)
logger.error('{}: {}'.format(errMsg, response.content))
logger.error('%s: %s', errMsg, response.content)
raise Exception(errMsg)
try:
@ -80,8 +79,16 @@ def ensureResponseIsValid(response, errMsg=None):
raise Exception('Error communicating with OpenGnsys: {}'.format(response.content[:128]))
class OpenGnsysClient(object):
def __init__(self, username, password, endpoint, cache, verifyCert=False):
class OpenGnsysClient:
username: str
password: str
endpoint: str
auth: typing.Optional[str]
cache: 'Cache'
verifyCert: bool
cachedVersion: typing.Optional[str]
def __init__(self, username: str, password: str, endpoint: str, cache: 'Cache', verifyCert: bool = False):
self.username = username
self.password = password
self.endpoint = endpoint
@ -91,17 +98,17 @@ class OpenGnsysClient(object):
self.cachedVersion = None
@property
def headers(self):
def headers(self) -> typing.MutableMapping[str, str]:
headers = {'content-type': 'application/json'}
if self.auth is not None:
if self.auth:
headers['Authorization'] = self.auth
return headers
def _ogUrl(self, path):
def _ogUrl(self, path: str) -> str:
return self.endpoint + '/' + path
def _post(self, path, data, errMsg=None):
def _post(self, path: str, data: typing.Any, errMsg: typing.Optional[str] = None) -> typing.Any:
if not FAKE:
return ensureResponseIsValid(
requests.post(self._ogUrl(path), data=json.dumps(data), headers=self.headers, verify=self.verifyCert),
@ -110,7 +117,7 @@ class OpenGnsysClient(object):
# FAKE Connection :)
return fake.post(path, data, errMsg)
def _get(self, path, errMsg=None):
def _get(self, path: str, errMsg: typing.Optional[str] = None) -> typing.Any:
if not FAKE:
return ensureResponseIsValid(
requests.get(self._ogUrl(path), headers=self.headers, verify=self.verifyCert),
@ -119,7 +126,7 @@ class OpenGnsysClient(object):
# FAKE Connection :)
return fake.get(path, errMsg)
def _delete(self, path, errMsg=None):
def _delete(self, path: str, errMsg: typing.Optional[str] = None) -> typing.Any:
if not FAKE:
return ensureResponseIsValid(
requests.delete(self._ogUrl(path), headers=self.headers, verify=self.verifyCert),
@ -127,13 +134,13 @@ class OpenGnsysClient(object):
)
return fake.delete(path, errMsg)
def connect(self):
if self.auth is not None:
def connect(self) -> None:
if self.auth:
return
cacheKey = 'auth{}{}'.format(self.endpoint, self.username)
self.auth = self.cache.get(cacheKey)
if self.auth is not None:
if self.auth:
return
auth = self._post(
@ -149,17 +156,17 @@ class OpenGnsysClient(object):
self.cache.put(cacheKey, self.auth, CACHE_VALIDITY)
@property
def version(self):
def version(self) -> str:
logger.debug('Getting version')
if self.cachedVersion is None:
if not self.cachedVersion:
# Retrieve Version & keep it
info = self._get(urls.INFO, errMsg="Retrieving info")
self.cachedVersion = info['version']
return self.cachedVersion
return typing.cast(str, self.cachedVersion)
@ensureConnected
def getOus(self):
def getOus(self) -> typing.Any:
# Returns an array of elements with:
# 'id': OpenGnsys Id
# 'name': OU name
@ -167,7 +174,7 @@ class OpenGnsysClient(object):
return self._get(urls.OUS, errMsg='Getting list of ous')
@ensureConnected
def getLabs(self, ou):
def getLabs(self, ou: str) -> typing.List[typing.MutableMapping[str, str]]:
# Returns a list of available labs on an ou
# /ous/{ouid}/labs
# Take into accout that we must exclude the ones with "inremotepc" set to false.
@ -175,7 +182,7 @@ class OpenGnsysClient(object):
return [{'id': l['id'], 'name': l['name']} for l in self._get(urls.LABS.format(ou=ou), errMsg=errMsg) if l.get('inremotepc', False) is True]
@ensureConnected
def getImages(self, ou):
def getImages(self, ou: str) -> typing.List[typing.MutableMapping[str, str]]:
# Returns a list of available labs on an ou
# /ous/{ouid}/images
# Take into accout that we must exclude the ones with "inremotepc" set to false.
@ -183,7 +190,7 @@ class OpenGnsysClient(object):
return [{'id': l['id'], 'name': l['name']} for l in self._get(urls.IMAGES.format(ou=ou), errMsg=errMsg) if l.get('inremotepc', False) is True]
@ensureConnected
def reserve(self, ou, image, lab=0, maxtime=24):
def reserve(self, ou: str, image: str, lab: int = 0, maxtime: int = 24) -> typing.MutableMapping[str, typing.Union[str, int]]:
# This method is inteded to "get" a machine from OpenGnsys
# The method used is POST
# invokes /ous/{ouid}}/images/{imageid}/reserve
@ -200,14 +207,14 @@ class OpenGnsysClient(object):
'image': image,
'lab': lab,
'client': res['id'],
'id': '.'.join((six.text_type(ou), six.text_type(res['lab']['id']), six.text_type(res['id']))),
'id': '.'.join((str(ou), str(res['lab']['id']), str(res['id']))),
'name': res['name'],
'ip': res['ip'],
'mac': ':'.join(re.findall('..', res['mac']))
}
@ensureConnected
def unreserve(self, machineId):
def unreserve(self, machineId: str) -> typing.Any:
# This method releases the previous reservation
# Invoked every time we need to release a reservation (i mean, if a reservation is done, this will be called with the obtained id from that reservation)
ou, lab, client = machineId.split('.')
@ -215,30 +222,29 @@ class OpenGnsysClient(object):
return self._delete(urls.UNRESERVE.format(ou=ou, lab=lab, client=client), errMsg=errMsg)
@ensureConnected
def notifyURLs(self, machineId, loginURL, logoutURL):
def notifyURLs(self, machineId: str, loginURL: str, logoutURL: str) -> typing.Any:
ou, lab, client = machineId.split('.')
errMsg = 'Notifying login/logout urls'
data = {
'urlLogin': loginURL,
'urlLogout': logoutURL
'urlLogin': loginURL,
'urlLogout': logoutURL
}
return self._post(urls.EVENTS.format(ou=ou, lab=lab, client=client), data, errMsg=errMsg)
@ensureConnected
def notifyDeadline(self, machineId, deadLine):
def notifyDeadline(self, machineId: str, deadLine: typing.Optional[int]) -> typing.Any:
ou, lab, client = machineId.split('.')
if deadLine is None:
deadLine = 0
deadLine = deadLine or 0
errMsg = 'Notifying deadline'
data = {
'deadLine': deadLine
'deadLine': deadLine
}
return self._post(urls.SESSIONS.format(ou=ou, lab=lab, client=client), data, errMsg=errMsg)
@ensureConnected
def status(self, id_):
def status(self, id_: str) -> typing.Any:
# This method gets the status of the machine
# /ous/{uoid}/labs/{labid}/clients/{clientid}/status
# possible status are ("off", "oglive", "busy", "linux", "windows", "macos" o "unknown").

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# Copyright (c) 2017-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -30,14 +30,13 @@
"""
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
"""
from __future__ import unicode_literals
from . import urls
import copy
import random
import six
import logging
import typing
from . import urls
__updated__ = '2017-09-29'
logger = logging.getLogger(__name__)
@ -145,7 +144,7 @@ IMAGES = [
},
]
RESERVE = {
RESERVE: typing.Dict[str, typing.Any] = {
"id": 4,
"name": "pcpruebas",
"mac": "4061860521FE",
@ -184,13 +183,14 @@ STATUS_READY_WINDOWS = {
# FAKE post
def post(path, data, errMsg):
logger.info('FAKE POST request to {} with {} data. ({})'.format(path, data, errMsg))
def post(path: str, data: typing.Any, errMsg: typing.Optional[str] = None) -> typing.Any:
logger.info('FAKE POST request to %s with %s data. (%s)', path, data, errMsg)
if path == urls.LOGIN:
return AUTH
elif path == urls.RESERVE.format(ou=1, image=1) or path == urls.RESERVE.format(ou=1, image=2):
if path == urls.RESERVE.format(ou=1, image=1) or path == urls.RESERVE.format(ou=1, image=2):
res = copy.deepcopy(RESERVE)
res['name'] += six.text_type(random.randint(5000, 100000))
res['name'] += str(random.randint(5000, 100000))
res['mac'] = ''.join(random.choice('0123456789ABCDEF') for _ in range(12))
return res
@ -198,33 +198,33 @@ def post(path, data, errMsg):
# FAKE get
def get(path, errMsg):
logger.info('FAKE GET request to {}. ({})'.format(path, errMsg))
def get(path, errMsg: typing.Optional[str]) -> typing.Any: # pylint: disable=too-many-return-statements
logger.info('FAKE GET request to %s. (%s)', path, errMsg)
if path == urls.INFO:
return INFO
elif path == urls.OUS:
if path == urls.OUS:
return OUS
elif path == urls.LABS.format(ou=1):
if path == urls.LABS.format(ou=1):
return LABS
elif path == urls.LABS.format(ou=2):
if path == urls.LABS.format(ou=2):
return [] # Empty
elif path == urls.IMAGES.format(ou=1):
if path == urls.IMAGES.format(ou=1):
return IMAGES
elif path == urls.IMAGES.format(ou=2):
if path == urls.IMAGES.format(ou=2):
return []
elif path[-6:] == 'status':
if path[-6:] == 'status':
rnd = random.randint(0, 100)
if rnd < 25:
return STATUS_READY_LINUX
return STATUS_OFF
elif path[-6:] == 'events':
if path[-6:] == 'events':
return ''
raise Exception('Unknown FAKE URL on GET: {}'.format(path))
def delete(path, errMsg):
logger.info('FAKE DELETE request to {}. ({})'.format(path, errMsg))
def delete(path: str, errMsg: typing.Optional[str]):
logger.info('FAKE DELETE request to %s. (%s)', path, errMsg)
# Right now, only "unreserve" uses delete, so simply return
return UNRESERVE
# raise Exception('Unknown FAKE URL on DELETE: {}'.format(path))

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# Copyright (c) 2017-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -30,8 +30,6 @@
"""
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
"""
from __future__ import unicode_literals
# API URL 1: https://www.informatica.us.es/~ramon/opengnsys/?url=opengnsys-api.yml
# API URL 2: http://opengnsys.es/wiki/ApiRest

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,20 +32,23 @@ Created on Jun 22, 2012
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
"""
from __future__ import unicode_literals
import logging
import typing
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 .OGService import OGService
from .service import OGService
from . import og
# Not imported at runtime, just for type checking
if typing.TYPE_CHECKING:
from uds.core import Module
from uds.core.environment import Environment
import logging
import six
__updated__ = '2017-10-16'
@ -106,9 +109,9 @@ class OGProvider(ServiceProvider):
timeout = gui.NumericField(length=3, label=_('Timeout'), defvalue='10', order=90, tooltip=_('Timeout in seconds of connection to OpenGnsys'), required=True, tab=gui.ADVANCED_TAB)
# Own variables
_api = None
_api: typing.Optional[og.OpenGnsysClient] = None
def initialize(self, values=None):
def initialize(self, values: 'Module.ValuesType') -> None:
"""
We will use the "autosave" feature for form fields
"""
@ -116,7 +119,7 @@ class OGProvider(ServiceProvider):
# Just reset _api connection variable
self._api = None
if values is not None:
if values:
self.timeout.value = validators.validateTimeout(self.timeout.value)
logger.debug('Endpoint: %s', self.endpoint)
@ -132,21 +135,21 @@ class OGProvider(ServiceProvider):
pass
@property
def endpoint(self):
def endpoint(self) -> str:
return 'https://{}:{}/opengnsys/rest'.format(self.host.value, self.port.value)
@property
def api(self):
if self._api is None:
def api(self) -> og.OpenGnsysClient:
if not self._api:
self._api = og.OpenGnsysClient(self.username.value, self.password.value, self.endpoint, self.cache, self.checkCert.isTrue())
logger.debug('Api: {}'.format(self._api))
logger.debug('Api: %s', self._api)
return self._api
def resetApi(self):
def resetApi(self) -> None:
self._api = None
def testConnection(self):
def testConnection(self) -> typing.List[typing.Any]:
"""
Test that conection to OpenGnsys server is fine
@ -164,7 +167,7 @@ class OGProvider(ServiceProvider):
return [True, _('OpenGnsys test connection passed')]
@staticmethod
def test(env, data):
def test(env: 'Environment', data: 'Module.ValuesType') -> typing.List[typing.Any]:
"""
Test ovirt Connectivity
@ -182,20 +185,20 @@ class OGProvider(ServiceProvider):
"""
return OGProvider(env, data).testConnection()
def getUDSServerAccessUrl(self):
def getUDSServerAccessUrl(self) -> str:
return self.udsServerAccessUrl.value
def reserve(self, ou, image, lab=0, maxtime=0):
def reserve(self, ou: str, image: str, lab: int = 0, maxtime: int = 0) -> typing.Any:
return self.api.reserve(ou, image, lab, maxtime)
def unreserve(self, machineId):
def unreserve(self, machineId: str) -> typing.Any:
return self.api.unreserve(machineId)
def notifyEvents(self, machineId, loginURL, logoutURL):
def notifyEvents(self, machineId: str, loginURL: str, logoutURL: str) -> typing.Any:
return self.api.notifyURLs(machineId, loginURL, logoutURL)
def notifyDeadline(self, machineId, deadLine):
def notifyDeadline(self, machineId: str, deadLine: typing.Optional[int]) -> typing.Any:
return self.api.notifyDeadline(machineId, deadLine)
def status(self, machineId):
def status(self, machineId: str) -> typing.Any:
return self.api.status(machineId)

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Virtual Cable S.L.
# Copyright (c) 2017-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -30,14 +30,16 @@
"""
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
"""
from __future__ import unicode_literals
import logging
import typing
from uds.core.services import Publication
from uds.core.util.state import State
from uds.models.util import getSqlDatetime
import logging
__updated__ = '2019-02-07'
# Not imported at runtime, just for type checking
if typing.TYPE_CHECKING:
from .service import OGService
logger = logging.getLogger(__name__)
@ -46,29 +48,20 @@ class OGPublication(Publication):
"""
This class provides the publication of a oVirtLinkedService
"""
_name = ''
_name: str = ''
suggestedTime = 5 # : Suggested recheck time if publication is unfinished in seconds
def initialize(self):
"""
This method will be invoked by default __init__ of base class, so it gives
us the oportunity to initialize whataver we need here.
def service(self) -> 'OGService':
return typing.cast('OGService', super().service())
In our case, we setup a few attributes..
"""
# We do not check anything at marshal method, so we ensure that
# default values are correctly handled by marshal.
self._name = ''
def marshal(self):
def marshal(self) -> bytes:
"""
returns data from an instance of Sample Publication serialized
"""
return '\t'.join(['v1', self._name]).encode('utf8')
def unmarshal(self, data):
def unmarshal(self, data: bytes) -> None:
"""
deserializes the data and loads it inside instance.
"""
@ -76,50 +69,24 @@ class OGPublication(Publication):
if vals[0] == 'v1':
self._name = vals[1]
def publish(self):
def publish(self) -> str:
"""
Realizes the publication of the service
"""
self._name = 'Publication {}'.format(getSqlDatetime())
return State.FINISHED
def checkState(self):
def checkState(self) -> str:
"""
Checks state of publication creation
"""
return State.FINISHED
def finish(self):
"""
In our case, finish does nothing
"""
pass
def reasonOfError(self):
"""
If a publication produces an error, here we must notify the reason why
it happened. This will be called just after publish or checkState
if they return State.ERROR
Returns an string, in our case, set at checkState
"""
def reasonOfError(self) -> str:
return 'No error possible :)'
def destroy(self):
"""
This is called once a publication is no more needed.
This method do whatever needed to clean up things, such as
removing created "external" data (environment gets cleaned by core),
etc..
The retunred value is the same as when publishing, State.RUNNING,
State.FINISHED or State.ERROR.
"""
def destroy(self) -> str:
return State.FINISHED
def cancel(self):
"""
Do same thing as destroy
"""
def cancel(self) -> str:
return self.destroy()

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Virtual Cable S.L.
# Copyright (c) 2017-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -30,18 +30,23 @@
"""
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
"""
from django.utils.translation import ugettext_noop as _, ugettext
import logging
import typing
from django.utils.translation import ugettext_noop as _
from uds.core.transports import protocols
from uds.core.services import Service, types as serviceTypes
from .OGDeployment import OGDeployment
from .OGPublication import OGPublication
from . import helpers
from uds.core.ui import gui
import logging
from . import helpers
from .deployment import OGDeployment
from .publication import OGPublication
__updated__ = '2018-11-08'
# Not imported at runtime, just for type checking
if typing.TYPE_CHECKING:
from .provider import OGProvider
from uds.core import Module
logger = logging.getLogger(__name__)
@ -134,17 +139,7 @@ class OGService(Service):
ov = gui.HiddenField(value=None)
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 FROM variables, that already has been
initialized by __init__ method of base class, before invoking this.
"""
if values is not None:
pass
def initGui(self):
def initGui(self) -> None:
"""
Loads required values inside
"""
@ -154,22 +149,25 @@ class OGService(Service):
self.ov.setDefValue(self.parent().serialize())
self.ev.setDefValue(self.parent().env.key)
def status(self, machineId):
def parent(self) -> 'OGProvider':
return typing.cast('OGProvider', super().parent())
def status(self, machineId: str) -> typing.Any:
return self.parent().status(machineId)
def reserve(self):
def reserve(self) -> typing.Any:
return self.parent().reserve(self.ou.value, self.image.value, self.lab.value, self.maxReservationTime.num())
def unreserve(self, machineId):
def unreserve(self, machineId: str) -> typing.Any:
return self.parent().unreserve(machineId)
def notifyEvents(self, machineId, serviceUUID):
def notifyEvents(self, machineId: str, serviceUUID: str) -> typing.Any:
return self.parent().notifyEvents(machineId, self.getLoginNotifyURL(serviceUUID), self.getLogoutNotifyURL(serviceUUID))
def notifyDeadline(self, machineId, deadLine):
def notifyDeadline(self, machineId: str, deadLine: typing.Optional[int]) -> typing.Any:
return self.parent().notifyDeadline(machineId, deadLine)
def _notifyURL(self, uuid, message):
def _notifyURL(self, uuid: str, message: str) -> str:
# The URL is "GET messages URL".
return '{accessURL}rest/actor/PostThoughGet/{uuid}/{message}'.format(
accessURL=self.parent().getUDSServerAccessUrl(),
@ -177,8 +175,8 @@ class OGService(Service):
message=message
)
def getLoginNotifyURL(self, serviceUUID):
def getLoginNotifyURL(self, serviceUUID: str) -> str:
return self._notifyURL(serviceUUID, 'login')
def getLogoutNotifyURL(self, serviceUUID):
def getLogoutNotifyURL(self, serviceUUID: str) -> str:
return self._notifyURL(serviceUUID, 'logout')