diff --git a/server/src/uds/services/OpenNebula/deployment.py b/server/src/uds/services/OpenNebula/deployment.py index 8f90c2dc5..74d26e89a 100644 --- a/server/src/uds/services/OpenNebula/deployment.py +++ b/server/src/uds/services/OpenNebula/deployment.py @@ -49,7 +49,7 @@ if typing.TYPE_CHECKING: logger = logging.getLogger(__name__) -opCreate, opStart, opSuspend, opRemove, opWait, opError, opFinish, opRetry = range(8) +opCreate, opStart, opShutdown, opRemove, opWait, opError, opFinish, opRetry = range(8) NO_MORE_NAMES = 'NO-NAME-ERROR' @@ -176,7 +176,7 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods if forLevel2 is False: self._queue = [opCreate, opStart, opFinish] else: - self._queue = [opCreate, opStart, opWait, opSuspend, opFinish] + self._queue = [opCreate, opStart, opWait, opShutdown, opFinish] def __checkMachineState(self, chkState: on.types.VmState) -> str: logger.debug('Checking that state of machine %s (%s) is %s', self._vmid, self._name, chkState) @@ -251,7 +251,7 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods opCreate: self.__create, opRetry: self.__retry, opStart: self.__startMachine, - opSuspend: self.__suspendMachine, + opShutdown: self.__shutdownMachine, opWait: self.__wait, opRemove: self.__remove, } @@ -337,11 +337,11 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods return State.RUNNING - def __suspendMachine(self) -> str: + def __shutdownMachine(self) -> str: """ Suspends the machine """ - self.service().suspendMachine(self._vmid) + self.service().shutdownMachine(self._vmid) return State.RUNNING # Check methods @@ -357,11 +357,11 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods """ return self.__checkMachineState(on.types.VmState.ACTIVE) # @UndefinedVariable - def __checkSuspend(self) -> str: + def __checkShutdown(self) -> str: """ Check if the machine has suspended """ - return self.__checkMachineState(on.types.VmState.SUSPENDED) # @UndefinedVariable + return self.__checkMachineState(on.types.VmState.POWEROFF) # @UndefinedVariable def __checkRemoved(self) -> str: """ @@ -387,7 +387,7 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods opRetry: self.__retry, opWait: self.__wait, opStart: self.__checkStart, - opSuspend: self.__checkSuspend, + opShutdown: self.__checkShutdown, opRemove: self.__checkRemoved, } @@ -416,7 +416,7 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods if newLevel == self.L1_CACHE: self._queue = [opStart, opFinish] else: - self._queue = [opStart, opSuspend, opFinish] + self._queue = [opStart, opShutdown, opFinish] return self.__executeQueue() @@ -467,7 +467,7 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods return { opCreate: 'create', opStart: 'start', - opSuspend: 'suspend', + opShutdown: 'shutdown', opRemove: 'remove', opWait: 'wait', opError: 'error', diff --git a/server/src/uds/services/OpenNebula/on/client.py b/server/src/uds/services/OpenNebula/on/client.py index adcf873ff..b97c734b2 100644 --- a/server/src/uds/services/OpenNebula/on/client.py +++ b/server/src/uds/services/OpenNebula/on/client.py @@ -53,7 +53,7 @@ def ensureConnected(fnc: typing.Callable[..., RT]) -> typing.Callable[..., RT]: # Result checker -def checkResultRaw(lst: typing.List) -> str: +def checkResultRaw(lst: typing.Any) -> str: # Openebula response is always this way: # [Boolean, String, ErrorCode] # First is True if ok, False if not @@ -65,7 +65,7 @@ def checkResultRaw(lst: typing.List) -> str: return str(lst[1]) -def checkResult(lst: typing.List) -> typing.Tuple[typing.Dict, str]: +def checkResult(lst: typing.Any) -> typing.Tuple[typing.Dict, str]: return xml2dict.parse(checkResultRaw(lst)), lst[1] @@ -86,7 +86,8 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods self.username = username self.password = password self.endpoint = endpoint - self.connection = None + # Connection "None" will be treated on ensureConnected, ignore its assignement here + self.connection = None # type: ignore self.cachedVersion = None @property diff --git a/server/src/uds/services/OpenNebula/on/vm.py b/server/src/uds/services/OpenNebula/on/vm.py index 2664c7a23..d195d744e 100644 --- a/server/src/uds/services/OpenNebula/on/vm.py +++ b/server/src/uds/services/OpenNebula/on/vm.py @@ -122,6 +122,20 @@ def suspendMachine(api: 'client.OpenNebulaClient', machineId: str) -> None: except Exception as e: logger.error('Error suspending %s on OpenNebula: %s', machineId, e) +def shutdownMachine(api: 'client.OpenNebulaClient', machineId: str) -> None: + ''' + Tries to "gracefully" shutdown a machine. No check is done, it is simply requested to OpenNebula + + Args: + machineId: Id of the machine + + Returns: + ''' + try: + api.VMAction(machineId, 'poweroff') + except Exception as e: + logger.error('Error shutting down %s on OpenNebula: %s', machineId, e) + def resetMachine(api: 'client.OpenNebulaClient', machineId: str) -> None: ''' diff --git a/server/src/uds/services/OpenNebula/provider.py b/server/src/uds/services/OpenNebula/provider.py index 83c6960a7..c65658c0a 100644 --- a/server/src/uds/services/OpenNebula/provider.py +++ b/server/src/uds/services/OpenNebula/provider.py @@ -186,7 +186,7 @@ class OpenNebulaProvider(ServiceProvider): # pylint: disable=too-many-public-me def stopMachine(self, machineId: str) -> None: ''' - Tries to start a machine. No check is done, it is simply requested to OpenNebula + Tries to stop a machine. No check is done, it is simply requested to OpenNebula Args: machineId: Id of the machine @@ -197,7 +197,7 @@ class OpenNebulaProvider(ServiceProvider): # pylint: disable=too-many-public-me def suspendMachine(self, machineId: str) -> None: ''' - 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 @@ -206,6 +206,17 @@ class OpenNebulaProvider(ServiceProvider): # pylint: disable=too-many-public-me ''' on.vm.suspendMachine(self.api, machineId) + def shutdownMachine(self, machineId: str) -> None: + ''' + Tries to shutdown "gracefully" a machine. No check is done, it is simply requested to OpenNebula + + Args: + machineId: Id of the machine + + Returns: + ''' + on.vm.shutdownMachine(self.api, machineId) + def resetMachine(self, machineId: str) -> None: ''' Resets a machine (hard-reboot) diff --git a/server/src/uds/services/OpenNebula/service.py b/server/src/uds/services/OpenNebula/service.py index fc238a8e3..0943cc203 100644 --- a/server/src/uds/services/OpenNebula/service.py +++ b/server/src/uds/services/OpenNebula/service.py @@ -80,6 +80,12 @@ class LiveService(Service): # : Tooltip shown to user when this item is pointed at admin interface, none # : because we don't use it cacheTooltip = _('Number of desired machines to keep running waiting for an user') + # : If we need to generate a "Level 2" cache for this service (i.e., L1 + # : could be running machines and L2 suspended machines) + usesCache_L2 = True + # : Tooltip shown to user when this item is pointed at admin interface, None + # : also because we don't use it + cacheTooltip_L2 = _('Number of desired VMs to keep stopped waiting for use') # : If the service needs a s.o. manager (managers are related to agents # : provided by services itselfs, i.e. virtual machines with actors) @@ -257,6 +263,17 @@ class LiveService(Service): """ self.parent().suspendMachine(machineId) + def shutdownMachine(self, machineId: str) -> None: + """ + Tries to "gracefully" shutdown machine. No check is done, it is simply requested to OpenNebula + + Args: + machineId: Id of the machine + + Returns: + """ + self.parent().shutdownMachine(machineId) + def resetMachine(self, machineId: str) -> None: self.parent().resetMachine(machineId)