mirror of
https://github.com/dkmstr/openuds.git
synced 2025-02-02 09:47:13 +03:00
Many more improvements to openstack and advanced on tests. Hope to finish them by tomorrow
This commit is contained in:
parent
d542e65a9c
commit
12548b44cc
@ -98,21 +98,29 @@ class Publication(Environmentable, Serializable):
|
|||||||
service: 'services.Service',
|
service: 'services.Service',
|
||||||
osmanager: typing.Optional['osmanagers.OSManager'] = None,
|
osmanager: typing.Optional['osmanagers.OSManager'] = None,
|
||||||
revision: int = -1,
|
revision: int = -1,
|
||||||
servicepool_name: str = 'Unknown',
|
servicepool_name: typing.Optional[str] = None,
|
||||||
uuid: str = '',
|
uuid: str = '',
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Do not forget to invoke this in your derived class using "super(self.__class__, self).__init__(environment, values)"
|
Instead of overriding __init__, override initialize method, that will be called
|
||||||
|
just after all internal initialization is completed.
|
||||||
We want to use the env, cache and storage methods outside class. If not called, you must implement your own methods
|
We want to use the env, cache and storage methods outside class. If not called, you must implement your own methods
|
||||||
cache and storage are "convenient" methods to access _env.cache and _env.storage
|
cache and storage are "convenient" methods to access _env.cache and _env.storage
|
||||||
@param environment: Environment assigned to this publication
|
|
||||||
|
Args:
|
||||||
|
environment (Environment): Environment of the service
|
||||||
|
service (services.Service): Service that owns this publication
|
||||||
|
osmanager (osmanagers.OSManager, optional): OsManager associated with this publication. Defaults to None.
|
||||||
|
revision (int, optional): Revision of the publication. Defaults to -1.
|
||||||
|
servicepool_name (str, optional): Name of the service pool. Defaults to None. (Unknown)
|
||||||
|
uuid (str, optional): UUID of the publication. Defaults to ''.
|
||||||
"""
|
"""
|
||||||
Environmentable.__init__(self, environment)
|
Environmentable.__init__(self, environment)
|
||||||
Serializable.__init__(self)
|
Serializable.__init__(self)
|
||||||
self._service = service
|
self._service = service
|
||||||
self._osmanager = osmanager
|
self._osmanager = osmanager
|
||||||
self._revision = revision
|
self._revision = revision
|
||||||
self._servicepool_name = servicepool_name
|
self._servicepool_name = servicepool_name or 'Unknown'
|
||||||
self._uuid = uuid
|
self._uuid = uuid
|
||||||
|
|
||||||
self.initialize()
|
self.initialize()
|
||||||
|
@ -109,7 +109,7 @@ class UserService(Environmentable, Serializable):
|
|||||||
method error_reason can be called multiple times, including
|
method error_reason can be called multiple times, including
|
||||||
serializations in middle, so remember to include reason of error in serializations
|
serializations in middle, so remember to include reason of error in serializations
|
||||||
"""
|
"""
|
||||||
|
USER: int = 0 # : Constant for User cache level
|
||||||
L1_CACHE = 1 # : Constant for Cache of level 1
|
L1_CACHE = 1 # : Constant for Cache of level 1
|
||||||
L2_CACHE = 2 # : Constant for Cache of level 2
|
L2_CACHE = 2 # : Constant for Cache of level 2
|
||||||
|
|
||||||
|
@ -51,6 +51,10 @@ if typing.TYPE_CHECKING:
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# How many times we will check for a machine to be ready/stopped/whatever
|
||||||
|
# 25 = 25 * 5 = 125 seconds (5 is suggested_delay)
|
||||||
|
CHECK_COUNT_BEFORE_FAILURE: typing.Final[int] = 25
|
||||||
|
|
||||||
|
|
||||||
class Operation(enum.IntEnum):
|
class Operation(enum.IntEnum):
|
||||||
CREATE = 0
|
CREATE = 0
|
||||||
@ -61,6 +65,7 @@ class Operation(enum.IntEnum):
|
|||||||
ERROR = 5
|
ERROR = 5
|
||||||
FINISH = 6
|
FINISH = 6
|
||||||
RETRY = 7
|
RETRY = 7
|
||||||
|
STOP = 8
|
||||||
|
|
||||||
UNKNOWN = 99
|
UNKNOWN = 99
|
||||||
|
|
||||||
@ -72,7 +77,7 @@ class Operation(enum.IntEnum):
|
|||||||
return Operation.UNKNOWN
|
return Operation.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
class OpenStackLiveDeployment(
|
class OpenStackLiveUserService(
|
||||||
services.UserService, autoserializable.AutoSerializable
|
services.UserService, autoserializable.AutoSerializable
|
||||||
): # pylint: disable=too-many-public-methods
|
): # pylint: disable=too-many-public-methods
|
||||||
"""
|
"""
|
||||||
@ -90,6 +95,7 @@ class OpenStackLiveDeployment(
|
|||||||
_mac = autoserializable.StringField(default='')
|
_mac = autoserializable.StringField(default='')
|
||||||
_vmid = autoserializable.StringField(default='')
|
_vmid = autoserializable.StringField(default='')
|
||||||
_reason = autoserializable.StringField(default='')
|
_reason = autoserializable.StringField(default='')
|
||||||
|
_check_count = autoserializable.IntegerField(default=CHECK_COUNT_BEFORE_FAILURE)
|
||||||
_queue = autoserializable.ListField[Operation]()
|
_queue = autoserializable.ListField[Operation]()
|
||||||
|
|
||||||
# _name: str = ''
|
# _name: str = ''
|
||||||
@ -200,26 +206,32 @@ class OpenStackLiveDeployment(
|
|||||||
"""
|
"""
|
||||||
Deploys an service instance for cache
|
Deploys an service instance for cache
|
||||||
"""
|
"""
|
||||||
self._init_queue_for_deploy(level == self.L2_CACHE)
|
self._init_queue_for_deploy(level)
|
||||||
return self._execute_queue()
|
return self._execute_queue()
|
||||||
|
|
||||||
def _init_queue_for_deploy(self, forLevel2: bool = False) -> None:
|
def _init_queue_for_deploy(self, level: int) -> None:
|
||||||
if forLevel2 is False:
|
if level in (self.USER, self.L1_CACHE):
|
||||||
self._queue = [Operation.CREATE, Operation.FINISH]
|
self._queue = [Operation.CREATE, Operation.FINISH]
|
||||||
else:
|
else:
|
||||||
self._queue = [Operation.CREATE, Operation.WAIT, Operation.SUSPEND, Operation.FINISH]
|
self._queue = [Operation.CREATE, Operation.WAIT, Operation.SUSPEND, Operation.FINISH]
|
||||||
|
|
||||||
def _check_machine_power_state(self, check_state: openstack_types.PowerState) -> types.states.TaskState:
|
def _check_machine_power_state(self, *check_state: 'openstack_types.PowerState') -> types.states.TaskState:
|
||||||
|
self._check_count -= 1
|
||||||
|
if self._check_count < 0:
|
||||||
|
return self._error('Machine is not {str(check_state)} after {CHECK_COUNT_BEFORE_FAILURE} checks')
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
'Checking that state of machine %s (%s) is %s',
|
'Checking that state of machine %s (%s) is %s (remaining checks: %s)',
|
||||||
self._vmid,
|
self._vmid,
|
||||||
self._name,
|
self._name,
|
||||||
check_state,
|
check_state,
|
||||||
|
self._check_count,
|
||||||
)
|
)
|
||||||
power_state = self.service().get_machine_power_state(self._vmid)
|
power_state = self.service().get_machine_power_state(self._vmid)
|
||||||
|
|
||||||
ret = types.states.TaskState.RUNNING
|
ret = types.states.TaskState.RUNNING
|
||||||
if power_state == check_state:
|
|
||||||
|
if power_state in check_state:
|
||||||
ret = types.states.TaskState.FINISHED
|
ret = types.states.TaskState.FINISHED
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
@ -236,6 +248,11 @@ class OpenStackLiveDeployment(
|
|||||||
|
|
||||||
return self._queue.pop(0)
|
return self._queue.pop(0)
|
||||||
|
|
||||||
|
def _reset_check_count(self) -> None:
|
||||||
|
# Check the maximum number of checks before failure
|
||||||
|
# So we dont stuck forever on check state to CHECK_COUNT_BEFORE_FAILURE
|
||||||
|
self._check_count = CHECK_COUNT_BEFORE_FAILURE
|
||||||
|
|
||||||
def _error(self, reason: typing.Any) -> types.states.TaskState:
|
def _error(self, reason: typing.Any) -> types.states.TaskState:
|
||||||
"""
|
"""
|
||||||
Internal method to set object as error state
|
Internal method to set object as error state
|
||||||
@ -244,16 +261,26 @@ class OpenStackLiveDeployment(
|
|||||||
types.states.DeployState.ERROR, so we can do "return self.__error(reason)"
|
types.states.DeployState.ERROR, so we can do "return self.__error(reason)"
|
||||||
"""
|
"""
|
||||||
logger.debug('Setting error state, reason: %s', reason)
|
logger.debug('Setting error state, reason: %s', reason)
|
||||||
|
is_creation = self._get_current_op() == Operation.CREATE
|
||||||
self._queue = [Operation.ERROR]
|
self._queue = [Operation.ERROR]
|
||||||
self._reason = str(reason)
|
self._reason = str(reason)
|
||||||
|
|
||||||
self.do_log(log.LogLevel.ERROR, self._reason)
|
self.do_log(log.LogLevel.ERROR, self._reason)
|
||||||
|
|
||||||
if self._vmid and self.service().keep_on_error() is False: # Powers off & delete it
|
if self._vmid:
|
||||||
|
# Creating machines should be deleted on error
|
||||||
|
if is_creation or self.service().keep_on_error() is False: # Powers off & delete it
|
||||||
try:
|
try:
|
||||||
self.service().delete_machine(self._vmid)
|
self.service().delete_machine(self._vmid)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.warning('Can\t set machine %s state to stopped', self._vmid)
|
logger.warning('Can\t set machine %s state to stopped', self._vmid)
|
||||||
|
else:
|
||||||
|
self.do_log(
|
||||||
|
log.LogLevel.INFO, 'Keep on error is enabled, machine will not be marked for deletion'
|
||||||
|
)
|
||||||
|
# Simple fix queue to FINISH and return it
|
||||||
|
self._queue = [Operation.FINISH]
|
||||||
|
return types.states.TaskState.FINISHED
|
||||||
|
|
||||||
return types.states.TaskState.ERROR
|
return types.states.TaskState.ERROR
|
||||||
|
|
||||||
@ -267,27 +294,18 @@ class OpenStackLiveDeployment(
|
|||||||
if op == Operation.FINISH:
|
if op == Operation.FINISH:
|
||||||
return types.states.TaskState.FINISHED
|
return types.states.TaskState.FINISHED
|
||||||
|
|
||||||
fncs: dict[int, collections.abc.Callable[[], str]] = {
|
|
||||||
Operation.CREATE: self._create,
|
|
||||||
Operation.RETRY: self._retry,
|
|
||||||
Operation.START: self._start_machine,
|
|
||||||
Operation.SUSPEND: self._suspend_machine,
|
|
||||||
Operation.WAIT: self._wait,
|
|
||||||
Operation.REMOVE: self._remove,
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if op not in fncs:
|
if op not in _EXECUTE_FNCS:
|
||||||
return self._error('Unknown operation found at execution queue ({0})'.format(op))
|
return self._error('Unknown operation found at execution queue ({0})'.format(op))
|
||||||
|
|
||||||
fncs[op]()
|
_EXECUTE_FNCS[op](self)
|
||||||
|
|
||||||
return types.states.TaskState.RUNNING
|
return types.states.TaskState.RUNNING
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self._error(e)
|
return self._error(e)
|
||||||
|
|
||||||
# Queue execution methods
|
# Queue execution methods
|
||||||
def _retry(self) -> types.states.TaskState:
|
def _retry(self) -> None:
|
||||||
"""
|
"""
|
||||||
Used to retry an operation
|
Used to retry an operation
|
||||||
In fact, this will not be never invoked, unless we push it twice, because
|
In fact, this will not be never invoked, unless we push it twice, because
|
||||||
@ -295,15 +313,15 @@ class OpenStackLiveDeployment(
|
|||||||
|
|
||||||
At executeQueue this return value will be ignored, and it will only be used at check_state
|
At executeQueue this return value will be ignored, and it will only be used at check_state
|
||||||
"""
|
"""
|
||||||
return types.states.TaskState.FINISHED
|
pass
|
||||||
|
|
||||||
def _wait(self) -> types.states.TaskState:
|
def _wait(self) -> None:
|
||||||
"""
|
"""
|
||||||
Executes opWait, it simply waits something "external" to end
|
Executes opWait, it simply waits something "external" to end
|
||||||
"""
|
"""
|
||||||
return types.states.TaskState.RUNNING
|
pass
|
||||||
|
|
||||||
def _create(self) -> str:
|
def _create(self) -> None:
|
||||||
"""
|
"""
|
||||||
Deploys a machine from template for user/cache
|
Deploys a machine from template for user/cache
|
||||||
"""
|
"""
|
||||||
@ -320,9 +338,11 @@ class OpenStackLiveDeployment(
|
|||||||
if not self._vmid:
|
if not self._vmid:
|
||||||
raise Exception('Can\'t create machine')
|
raise Exception('Can\'t create machine')
|
||||||
|
|
||||||
return types.states.TaskState.RUNNING
|
self._reset_check_count()
|
||||||
|
|
||||||
def _remove(self) -> str:
|
return None
|
||||||
|
|
||||||
|
def _remove(self) -> None:
|
||||||
"""
|
"""
|
||||||
Removes a machine from system
|
Removes a machine from system
|
||||||
"""
|
"""
|
||||||
@ -333,22 +353,40 @@ class OpenStackLiveDeployment(
|
|||||||
|
|
||||||
self.service().delete_machine(self._vmid)
|
self.service().delete_machine(self._vmid)
|
||||||
|
|
||||||
return types.states.TaskState.RUNNING
|
def _start_machine(self) -> None:
|
||||||
|
|
||||||
def _start_machine(self) -> str:
|
|
||||||
"""
|
"""
|
||||||
Powers on the machine
|
Powers on the machine
|
||||||
"""
|
"""
|
||||||
self.service().start_machine(self._vmid)
|
self.service().start_machine(self._vmid)
|
||||||
|
|
||||||
return types.states.TaskState.RUNNING
|
self._reset_check_count()
|
||||||
|
|
||||||
def _suspend_machine(self) -> str:
|
def _stop_machine(self) -> None:
|
||||||
|
"""
|
||||||
|
Powers off the machine
|
||||||
|
"""
|
||||||
|
self.service().stop_machine(self._vmid)
|
||||||
|
|
||||||
|
self._reset_check_count()
|
||||||
|
|
||||||
|
def _suspend_machine(self) -> None:
|
||||||
"""
|
"""
|
||||||
Suspends the machine
|
Suspends the machine
|
||||||
"""
|
"""
|
||||||
self.service().suspend_machine(self._vmid)
|
self.service().suspend_machine(self._vmid)
|
||||||
|
|
||||||
|
self._reset_check_count()
|
||||||
|
|
||||||
|
def _check_retry(self) -> types.states.TaskState:
|
||||||
|
"""
|
||||||
|
This method is invoked when a task has been retried.
|
||||||
|
"""
|
||||||
|
return types.states.TaskState.FINISHED
|
||||||
|
|
||||||
|
def _check_wait(self) -> types.states.TaskState:
|
||||||
|
"""
|
||||||
|
This method is invoked when a task is waiting for something.
|
||||||
|
"""
|
||||||
return types.states.TaskState.RUNNING
|
return types.states.TaskState.RUNNING
|
||||||
|
|
||||||
# Check methods
|
# Check methods
|
||||||
@ -356,8 +394,11 @@ class OpenStackLiveDeployment(
|
|||||||
"""
|
"""
|
||||||
Checks the state of a deploy for an user or cache
|
Checks the state of a deploy for an user or cache
|
||||||
"""
|
"""
|
||||||
|
# Checks if machine has been created
|
||||||
ret = self._check_machine_power_state(openstack_types.PowerState.RUNNING)
|
ret = self._check_machine_power_state(openstack_types.PowerState.RUNNING)
|
||||||
if ret == types.states.TaskState.FINISHED:
|
if ret == types.states.TaskState.FINISHED:
|
||||||
|
# If machine is requested to not be removed never, we may end with
|
||||||
|
# an empty mac and ip, but no problem. Next time we will get it
|
||||||
# Get IP & MAC (early stage)
|
# Get IP & MAC (early stage)
|
||||||
addr = self.service().get_server_address(self._vmid)
|
addr = self.service().get_server_address(self._vmid)
|
||||||
self._mac, self._ip = addr.mac, addr.ip
|
self._mac, self._ip = addr.mac, addr.ip
|
||||||
@ -370,6 +411,16 @@ class OpenStackLiveDeployment(
|
|||||||
"""
|
"""
|
||||||
return self._check_machine_power_state(openstack_types.PowerState.RUNNING)
|
return self._check_machine_power_state(openstack_types.PowerState.RUNNING)
|
||||||
|
|
||||||
|
def _check_stop(self) -> types.states.TaskState:
|
||||||
|
"""
|
||||||
|
Checks if machine has stopped
|
||||||
|
"""
|
||||||
|
return self._check_machine_power_state(
|
||||||
|
openstack_types.PowerState.SHUTDOWN,
|
||||||
|
openstack_types.PowerState.CRASHED,
|
||||||
|
openstack_types.PowerState.SUSPENDED,
|
||||||
|
)
|
||||||
|
|
||||||
def _check_suspend(self) -> types.states.TaskState:
|
def _check_suspend(self) -> types.states.TaskState:
|
||||||
"""
|
"""
|
||||||
Check if the machine has suspended
|
Check if the machine has suspended
|
||||||
@ -395,20 +446,11 @@ class OpenStackLiveDeployment(
|
|||||||
if op == Operation.FINISH:
|
if op == Operation.FINISH:
|
||||||
return types.states.TaskState.FINISHED
|
return types.states.TaskState.FINISHED
|
||||||
|
|
||||||
fncs: dict[int, collections.abc.Callable[[], types.states.TaskState]] = {
|
|
||||||
Operation.CREATE: self._check_create,
|
|
||||||
Operation.RETRY: self._retry,
|
|
||||||
Operation.WAIT: self._wait,
|
|
||||||
Operation.START: self._check_start,
|
|
||||||
Operation.SUSPEND: self._check_suspend,
|
|
||||||
Operation.REMOVE: self._check_removed,
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if op not in fncs:
|
if op not in _CHECK_FNCS:
|
||||||
return self._error('Unknown operation found at execution queue ({0})'.format(op))
|
return self._error('Unknown operation found at execution queue ({0})'.format(op))
|
||||||
|
|
||||||
state = fncs[op]()
|
state = _CHECK_FNCS[op](self)
|
||||||
if state == types.states.TaskState.FINISHED:
|
if state == types.states.TaskState.FINISHED:
|
||||||
self._pop_current_op() # Remove runing op
|
self._pop_current_op() # Remove runing op
|
||||||
return self._execute_queue()
|
return self._execute_queue()
|
||||||
@ -426,7 +468,7 @@ class OpenStackLiveDeployment(
|
|||||||
|
|
||||||
if level == self.L1_CACHE:
|
if level == self.L1_CACHE:
|
||||||
self._queue = [Operation.START, Operation.FINISH]
|
self._queue = [Operation.START, Operation.FINISH]
|
||||||
else:
|
else: # Currently L2 is not supported
|
||||||
self._queue = [Operation.START, Operation.SUSPEND, Operation.FINISH]
|
self._queue = [Operation.START, Operation.SUSPEND, Operation.FINISH]
|
||||||
|
|
||||||
return self._execute_queue()
|
return self._execute_queue()
|
||||||
@ -446,11 +488,15 @@ class OpenStackLiveDeployment(
|
|||||||
if op == Operation.ERROR:
|
if op == Operation.ERROR:
|
||||||
return self._error('Machine is already in error state!')
|
return self._error('Machine is already in error state!')
|
||||||
|
|
||||||
if op == Operation.FINISH or op == Operation.WAIT:
|
ops = [Operation.STOP, Operation.REMOVE, Operation.FINISH]
|
||||||
self._queue = [Operation.REMOVE, Operation.FINISH]
|
|
||||||
return self._execute_queue()
|
if op == Operation.FINISH or op == Operation.WAIT:
|
||||||
|
self._queue = ops
|
||||||
|
return self._execute_queue() # Run it right now
|
||||||
|
|
||||||
|
# If an operation is pending, maybe checking, so we will wait until it finishes
|
||||||
|
self._queue = [op] + ops
|
||||||
|
|
||||||
self._queue = [op, Operation.REMOVE, Operation.FINISH]
|
|
||||||
# Do not execute anything.here, just continue normally
|
# Do not execute anything.here, just continue normally
|
||||||
return types.states.TaskState.RUNNING
|
return types.states.TaskState.RUNNING
|
||||||
|
|
||||||
@ -487,5 +533,28 @@ class OpenStackLiveDeployment(
|
|||||||
self._ip,
|
self._ip,
|
||||||
self._mac,
|
self._mac,
|
||||||
self._vmid,
|
self._vmid,
|
||||||
[OpenStackLiveDeployment._op2str(op) for op in self._queue],
|
[OpenStackLiveUserService._op2str(op) for op in self._queue],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Execution methods
|
||||||
|
_EXECUTE_FNCS: dict[int, collections.abc.Callable[[OpenStackLiveUserService], None]] = {
|
||||||
|
Operation.CREATE: OpenStackLiveUserService._create,
|
||||||
|
Operation.RETRY: OpenStackLiveUserService._retry,
|
||||||
|
Operation.START: OpenStackLiveUserService._start_machine,
|
||||||
|
Operation.STOP: OpenStackLiveUserService._stop_machine,
|
||||||
|
Operation.SUSPEND: OpenStackLiveUserService._suspend_machine,
|
||||||
|
Operation.WAIT: OpenStackLiveUserService._wait,
|
||||||
|
Operation.REMOVE: OpenStackLiveUserService._remove,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check methods
|
||||||
|
_CHECK_FNCS: dict[int, collections.abc.Callable[[OpenStackLiveUserService], types.states.TaskState]] = {
|
||||||
|
Operation.CREATE: OpenStackLiveUserService._check_create,
|
||||||
|
Operation.RETRY: OpenStackLiveUserService._check_retry,
|
||||||
|
Operation.WAIT: OpenStackLiveUserService._check_wait,
|
||||||
|
Operation.START: OpenStackLiveUserService._check_start,
|
||||||
|
Operation.STOP: OpenStackLiveUserService._check_stop,
|
||||||
|
Operation.SUSPEND: OpenStackLiveUserService._check_suspend,
|
||||||
|
Operation.REMOVE: OpenStackLiveUserService._check_removed,
|
||||||
|
}
|
||||||
|
@ -558,6 +558,10 @@ class OpenstackClient: # pylint: disable=too-many-public-methods
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Very small timeout, so repeated operations will use same data
|
||||||
|
# Any cache time less than 5 seconds will be fine, beceuse checks on
|
||||||
|
# openstack are done every 5 seconds
|
||||||
|
@decorators.cached(prefix='svr', timeout=3, key_helper=cache_key_helper)
|
||||||
def get_server(self, server_id: str) -> openstack_types.ServerInfo:
|
def get_server(self, server_id: str) -> openstack_types.ServerInfo:
|
||||||
r = self._request_from_endpoint(
|
r = self._request_from_endpoint(
|
||||||
'get',
|
'get',
|
||||||
@ -771,14 +775,15 @@ class OpenstackClient: # pylint: disable=too-many-public-methods
|
|||||||
expects_json=False,
|
expects_json=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def reset_server(self, server_id: str) -> None:
|
def reset_server(self, server_id: str, hard: bool = True) -> None:
|
||||||
# Does not need return value
|
# Does not need return value
|
||||||
try:
|
try:
|
||||||
|
type_reboot = 'HARD' if hard else 'SOFT'
|
||||||
self._request_from_endpoint(
|
self._request_from_endpoint(
|
||||||
'post',
|
'post',
|
||||||
endpoints_types=COMPUTE_ENDPOINT_TYPES,
|
endpoints_types=COMPUTE_ENDPOINT_TYPES,
|
||||||
path=f'/servers/{server_id}/action',
|
path=f'/servers/{server_id}/action',
|
||||||
data='{"reboot":{"type":"HARD"}}',
|
data='{"reboot":{"type":"' + type_reboot + '"}}',
|
||||||
error_message='Resetting server',
|
error_message='Resetting server',
|
||||||
expects_json=False,
|
expects_json=False,
|
||||||
)
|
)
|
||||||
|
@ -40,7 +40,7 @@ from uds.core.util import fields, validators
|
|||||||
from uds.core.ui import gui
|
from uds.core.ui import gui
|
||||||
|
|
||||||
from .publication import OpenStackLivePublication
|
from .publication import OpenStackLivePublication
|
||||||
from .deployment import OpenStackLiveDeployment
|
from .deployment import OpenStackLiveUserService
|
||||||
from .openstack import types as openstack_types, openstack_client
|
from .openstack import types as openstack_types, openstack_client
|
||||||
from . import helpers
|
from . import helpers
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ class OpenStackLiveService(services.Service):
|
|||||||
# : In our case, we do no need a publication, so this is None
|
# : In our case, we do no need a publication, so this is None
|
||||||
publication_type = OpenStackLivePublication
|
publication_type = OpenStackLivePublication
|
||||||
# : Types of deploys (services in cache and/or assigned to users)
|
# : Types of deploys (services in cache and/or assigned to users)
|
||||||
user_service_type = OpenStackLiveDeployment
|
user_service_type = OpenStackLiveUserService
|
||||||
|
|
||||||
allowed_protocols = types.transports.Protocol.generic_vdi(types.transports.Protocol.SPICE)
|
allowed_protocols = types.transports.Protocol.generic_vdi(types.transports.Protocol.SPICE)
|
||||||
services_type_provided = types.services.ServiceType.VDI
|
services_type_provided = types.services.ServiceType.VDI
|
||||||
@ -175,12 +175,15 @@ class OpenStackLiveService(services.Service):
|
|||||||
basename = fields.basename_field(order=9, tab=_('Machine'))
|
basename = fields.basename_field(order=9, tab=_('Machine'))
|
||||||
lenname = fields.lenname_field(order=10, tab=_('Machine'))
|
lenname = fields.lenname_field(order=10, tab=_('Machine'))
|
||||||
|
|
||||||
maintain_on_error = fields.maintain_on_error_field(order=104)
|
maintain_on_error = fields.maintain_on_error_field(order=11, tab=_('Machine'))
|
||||||
|
|
||||||
prov_uuid = gui.HiddenField()
|
prov_uuid = gui.HiddenField()
|
||||||
|
|
||||||
_api: typing.Optional['openstack_client.OpenstackClient'] = None
|
_api: typing.Optional['openstack_client.OpenstackClient'] = None
|
||||||
|
|
||||||
|
# Note: currently, Openstack does not provides a way of specifying how to stop the server
|
||||||
|
# At least, i have not found it on the documentation
|
||||||
|
|
||||||
def initialize(self, values: types.core.ValuesType) -> None:
|
def initialize(self, values: types.core.ValuesType) -> None:
|
||||||
"""
|
"""
|
||||||
We check here form values to see if they are valid.
|
We check here form values to see if they are valid.
|
||||||
@ -305,6 +308,10 @@ class OpenStackLiveService(services.Service):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
"""
|
"""
|
||||||
|
# if already running, do nothing
|
||||||
|
if self.get_machine_power_state(machineId) == openstack_types.PowerState.RUNNING:
|
||||||
|
return
|
||||||
|
|
||||||
self.api.start_server(machineId)
|
self.api.start_server(machineId)
|
||||||
|
|
||||||
def stop_machine(self, machineId: str) -> None:
|
def stop_machine(self, machineId: str) -> None:
|
||||||
@ -316,6 +323,10 @@ class OpenStackLiveService(services.Service):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
"""
|
"""
|
||||||
|
# If already stopped, do nothing
|
||||||
|
if self.get_machine_power_state(machineId) == openstack_types.PowerState.SHUTDOWN:
|
||||||
|
return
|
||||||
|
|
||||||
self.api.stop_server(machineId)
|
self.api.stop_server(machineId)
|
||||||
|
|
||||||
def reset_machine(self, machineId: str) -> None:
|
def reset_machine(self, machineId: str) -> None:
|
||||||
@ -338,6 +349,9 @@ class OpenStackLiveService(services.Service):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
"""
|
"""
|
||||||
|
# If not running, do nothing
|
||||||
|
if self.get_machine_power_state(machineId) != openstack_types.PowerState.RUNNING:
|
||||||
|
return
|
||||||
self.api.suspend_server(machineId)
|
self.api.suspend_server(machineId)
|
||||||
|
|
||||||
def resume_machine(self, machineid: str) -> None:
|
def resume_machine(self, machineid: str) -> None:
|
||||||
@ -349,6 +363,9 @@ class OpenStackLiveService(services.Service):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
"""
|
"""
|
||||||
|
# If not suspended, do nothing
|
||||||
|
if self.get_machine_power_state(machineid) != openstack_types.PowerState.SUSPENDED:
|
||||||
|
return
|
||||||
self.api.resume_server(machineid)
|
self.api.resume_server(machineid)
|
||||||
|
|
||||||
def delete_machine(self, machine_id: str) -> None:
|
def delete_machine(self, machine_id: str) -> None:
|
||||||
|
@ -85,6 +85,12 @@ class StorageTest(UDSTestCase):
|
|||||||
|
|
||||||
self.assertEqual(d['test_key'], UNICODE_CHARS_2)
|
self.assertEqual(d['test_key'], UNICODE_CHARS_2)
|
||||||
|
|
||||||
|
# Assert that UNICODE_CHARS is in the dict
|
||||||
|
d['test_key2'] = 0
|
||||||
|
d['test_key2'] += 1
|
||||||
|
|
||||||
|
self.assertEqual(d['test_key2'], 1)
|
||||||
|
|
||||||
# The values set inside the "with" are not available "outside"
|
# The values set inside the "with" are not available "outside"
|
||||||
# because the format is not compatible (with the dict, the values are stored as a tuple, with the original key stored
|
# because the format is not compatible (with the dict, the values are stored as a tuple, with the original key stored
|
||||||
# and with old format, only the value is stored
|
# and with old format, only the value is stored
|
||||||
|
@ -43,7 +43,7 @@ from uds.core.ui.user_interface import gui
|
|||||||
|
|
||||||
from ...utils.autospec import autospec, AutoSpecMethodInfo
|
from ...utils.autospec import autospec, AutoSpecMethodInfo
|
||||||
|
|
||||||
from uds.services.OpenStack import provider, provider_legacy, service
|
from uds.services.OpenStack import provider, provider_legacy, service, publication, deployment
|
||||||
from uds.services.OpenStack.openstack import openstack_client, types as openstack_types
|
from uds.services.OpenStack.openstack import openstack_client, types as openstack_types
|
||||||
|
|
||||||
AnyOpenStackProvider: typing.TypeAlias = typing.Union[provider.OpenStackProvider, provider_legacy.OpenStackProviderLegacy]
|
AnyOpenStackProvider: typing.TypeAlias = typing.Union[provider.OpenStackProvider, provider_legacy.OpenStackProviderLegacy]
|
||||||
@ -395,7 +395,7 @@ def create_provider_legacy(**kwargs: typing.Any) -> provider_legacy.OpenStackPro
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_service(
|
def create_live_service(
|
||||||
provider: AnyOpenStackProvider, **kwargs: typing.Any
|
provider: AnyOpenStackProvider, **kwargs: typing.Any
|
||||||
) -> service.OpenStackLiveService:
|
) -> service.OpenStackLiveService:
|
||||||
"""
|
"""
|
||||||
@ -411,3 +411,31 @@ def create_service(
|
|||||||
values=values,
|
values=values,
|
||||||
uuid=uuid_,
|
uuid=uuid_,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def create_publication(service: service.OpenStackLiveService) -> publication.OpenStackLivePublication:
|
||||||
|
"""
|
||||||
|
Create a publication
|
||||||
|
"""
|
||||||
|
uuid_ = str(uuid.uuid4())
|
||||||
|
return publication.OpenStackLivePublication(
|
||||||
|
environment=environment.Environment.private_environment(uuid_),
|
||||||
|
service=service,
|
||||||
|
revision=1,
|
||||||
|
servicepool_name='servicepool_name',
|
||||||
|
uuid=uuid_,
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_live_userservice(
|
||||||
|
service: service.OpenStackLiveService,
|
||||||
|
publication: typing.Optional[publication.OpenStackLivePublication] = None,
|
||||||
|
) -> deployment.OpenStackLiveUserService:
|
||||||
|
"""
|
||||||
|
Create a linked user service
|
||||||
|
"""
|
||||||
|
uuid_ = str(uuid.uuid4())
|
||||||
|
return deployment.OpenStackLiveUserService(
|
||||||
|
environment=environment.Environment.private_environment(uuid_),
|
||||||
|
service=service,
|
||||||
|
publication=publication or create_publication(service),
|
||||||
|
uuid=uuid_,
|
||||||
|
)
|
||||||
|
@ -58,6 +58,7 @@ EXPECTED_FIELDS: typing.Final[set[str]] = {
|
|||||||
'_vmid',
|
'_vmid',
|
||||||
'_reason',
|
'_reason',
|
||||||
'_queue',
|
'_queue',
|
||||||
|
'_check_count'
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_QUEUE: typing.Final[list[deployment.Operation]] = [
|
TEST_QUEUE: typing.Final[list[deployment.Operation]] = [
|
||||||
@ -74,7 +75,7 @@ LAST_VERSION: typing.Final[str] = sorted(SERIALIZED_DEPLOYMENT_DATA.keys(), reve
|
|||||||
|
|
||||||
|
|
||||||
class OpenStackDeploymentSerializationTest(UDSTransactionTestCase):
|
class OpenStackDeploymentSerializationTest(UDSTransactionTestCase):
|
||||||
def check(self, version: str, instance: deployment.OpenStackLiveDeployment) -> None:
|
def check(self, version: str, instance: deployment.OpenStackLiveUserService) -> None:
|
||||||
self.assertEqual(instance._name, 'name')
|
self.assertEqual(instance._name, 'name')
|
||||||
self.assertEqual(instance._ip, 'ip')
|
self.assertEqual(instance._ip, 'ip')
|
||||||
self.assertEqual(instance._mac, 'mac')
|
self.assertEqual(instance._mac, 'mac')
|
||||||
@ -86,8 +87,8 @@ class OpenStackDeploymentSerializationTest(UDSTransactionTestCase):
|
|||||||
# queue is kept on "storage", so we need always same environment
|
# queue is kept on "storage", so we need always same environment
|
||||||
environment = Environment.testing_environment()
|
environment = Environment.testing_environment()
|
||||||
|
|
||||||
def _create_instance(unmarshal_data: 'bytes|None' = None) -> deployment.OpenStackLiveDeployment:
|
def _create_instance(unmarshal_data: 'bytes|None' = None) -> deployment.OpenStackLiveUserService:
|
||||||
instance = deployment.OpenStackLiveDeployment(environment=environment, service=None) # type: ignore
|
instance = deployment.OpenStackLiveUserService(environment=environment, service=None) # type: ignore
|
||||||
if unmarshal_data:
|
if unmarshal_data:
|
||||||
instance.unmarshal(unmarshal_data)
|
instance.unmarshal(unmarshal_data)
|
||||||
return instance
|
return instance
|
||||||
@ -117,8 +118,8 @@ class OpenStackDeploymentSerializationTest(UDSTransactionTestCase):
|
|||||||
# Store queue
|
# Store queue
|
||||||
environment.storage.put_pickle('queue', TEST_QUEUE)
|
environment.storage.put_pickle('queue', TEST_QUEUE)
|
||||||
|
|
||||||
def _create_instance(unmarshal_data: 'bytes|None' = None) -> deployment.OpenStackLiveDeployment:
|
def _create_instance(unmarshal_data: 'bytes|None' = None) -> deployment.OpenStackLiveUserService:
|
||||||
instance = deployment.OpenStackLiveDeployment(environment=environment, service=None) # type: ignore
|
instance = deployment.OpenStackLiveUserService(environment=environment, service=None) # type: ignore
|
||||||
if unmarshal_data:
|
if unmarshal_data:
|
||||||
instance.unmarshal(unmarshal_data)
|
instance.unmarshal(unmarshal_data)
|
||||||
return instance
|
return instance
|
||||||
@ -162,6 +163,6 @@ class OpenStackDeploymentSerializationTest(UDSTransactionTestCase):
|
|||||||
# This test is designed to ensure that all fields are autoserializable
|
# This test is designed to ensure that all fields are autoserializable
|
||||||
# If some field is added or removed, this tests will warn us about it to fix the rest of the related tests
|
# If some field is added or removed, this tests will warn us about it to fix the rest of the related tests
|
||||||
with Environment.temporary_environment() as env:
|
with Environment.temporary_environment() as env:
|
||||||
instance = deployment.OpenStackLiveDeployment(environment=env, service=None) # type: ignore
|
instance = deployment.OpenStackLiveUserService(environment=env, service=None) # type: ignore
|
||||||
|
|
||||||
self.assertSetEqual(set(f[0] for f in instance._autoserializable_fields()), EXPECTED_FIELDS)
|
self.assertSetEqual(set(f[0] for f in instance._autoserializable_fields()), EXPECTED_FIELDS)
|
||||||
|
@ -51,7 +51,7 @@ class TestOpenstackService(UDSTransactionTestCase):
|
|||||||
"""
|
"""
|
||||||
for prov in (fixtures.create_provider_legacy(), fixtures.create_provider()):
|
for prov in (fixtures.create_provider_legacy(), fixtures.create_provider()):
|
||||||
with fixtures.patch_provider_api(legacy=prov.legacy) as client:
|
with fixtures.patch_provider_api(legacy=prov.legacy) as client:
|
||||||
service = fixtures.create_service(prov) # Will use provider patched api
|
service = fixtures.create_live_service(prov) # Will use provider patched api
|
||||||
self.assertEqual(service.api, client)
|
self.assertEqual(service.api, client)
|
||||||
self.assertEqual(service.sanitized_name('a b c'), 'a_b_c')
|
self.assertEqual(service.sanitized_name('a b c'), 'a_b_c')
|
||||||
|
|
||||||
@ -83,24 +83,29 @@ class TestOpenstackService(UDSTransactionTestCase):
|
|||||||
self.assertIsInstance(data, openstack_types.PowerState)
|
self.assertIsInstance(data, openstack_types.PowerState)
|
||||||
client.get_server.assert_called_once_with(fixtures.SERVERS_LIST[0].id)
|
client.get_server.assert_called_once_with(fixtures.SERVERS_LIST[0].id)
|
||||||
|
|
||||||
server_id = fixtures.SERVERS_LIST[0].id
|
server = fixtures.SERVERS_LIST[0]
|
||||||
service.start_machine(server_id)
|
service.start_machine(server.id)
|
||||||
client.start_server.assert_called_once_with(server_id)
|
|
||||||
|
|
||||||
service.stop_machine(server_id)
|
server.power_state = openstack_types.PowerState.SHUTDOWN
|
||||||
client.stop_server.assert_called_once_with(server_id)
|
client.start_server.assert_called_once_with(server.id)
|
||||||
|
|
||||||
service.suspend_machine(server_id)
|
server.power_state = openstack_types.PowerState.RUNNING
|
||||||
client.suspend_server.assert_called_once_with(server_id)
|
service.stop_machine(server.id)
|
||||||
|
client.stop_server.assert_called_once_with(server.id)
|
||||||
|
|
||||||
service.resume_machine(server_id)
|
server.power_state = openstack_types.PowerState.RUNNING
|
||||||
client.resume_server.assert_called_once_with(server_id)
|
service.suspend_machine(server.id)
|
||||||
|
client.suspend_server.assert_called_once_with(server.id)
|
||||||
|
|
||||||
service.reset_machine(server_id)
|
server.power_state = openstack_types.PowerState.SUSPENDED
|
||||||
client.reset_server.assert_called_once_with(server_id)
|
service.resume_machine(server.id)
|
||||||
|
client.resume_server.assert_called_once_with(server.id)
|
||||||
|
|
||||||
service.delete_machine(server_id)
|
service.reset_machine(server.id)
|
||||||
client.delete_server.assert_called_once_with(server_id)
|
client.reset_server.assert_called_once_with(server.id)
|
||||||
|
|
||||||
|
service.delete_machine(server.id)
|
||||||
|
client.delete_server.assert_called_once_with(server.id)
|
||||||
|
|
||||||
self.assertTrue(service.is_avaliable())
|
self.assertTrue(service.is_avaliable())
|
||||||
client.is_available.assert_called_once_with()
|
client.is_available.assert_called_once_with()
|
||||||
|
174
server/tests/services/openstack/test_userservice.py
Normal file
174
server/tests/services/openstack/test_userservice.py
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 Virtual Cable S.L.U.
|
||||||
|
# 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.U. 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
|
||||||
|
"""
|
||||||
|
from uds import models
|
||||||
|
from uds.core import types
|
||||||
|
|
||||||
|
from uds.services.OpenStack.openstack import types as openstack_types
|
||||||
|
from uds.services.OpenStack.deployment import Operation
|
||||||
|
|
||||||
|
from . import fixtures
|
||||||
|
|
||||||
|
from ...utils.test import UDSTransactionTestCase
|
||||||
|
from ...utils.generators import limited_iterator
|
||||||
|
|
||||||
|
|
||||||
|
# We use transactions on some related methods (storage access, etc...)
|
||||||
|
class TestOpenstackLiveDeployment(UDSTransactionTestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Openstack only have l1 cache. L2 is not considered useful right now
|
||||||
|
def test_userservice_cachel1_and_user(self) -> None:
|
||||||
|
"""
|
||||||
|
Test the user service
|
||||||
|
"""
|
||||||
|
# Deploy for cache and deploy for user are the same, so we will test both at the same time
|
||||||
|
for to_test in ['cache', 'user']:
|
||||||
|
for prov in (fixtures.create_provider_legacy(), fixtures.create_provider()):
|
||||||
|
with fixtures.patch_provider_api(legacy=prov.legacy) as api:
|
||||||
|
service = fixtures.create_live_service(prov)
|
||||||
|
userservice = fixtures.create_live_userservice(service=service)
|
||||||
|
publication = userservice.publication()
|
||||||
|
publication._template_id = 'snap1'
|
||||||
|
|
||||||
|
if to_test == 'cache':
|
||||||
|
state = userservice.deploy_for_cache(level=1)
|
||||||
|
else:
|
||||||
|
state = userservice.deploy_for_user(models.User())
|
||||||
|
|
||||||
|
self.assertEqual(state, types.states.TaskState.RUNNING, f'Error on {to_test} deployment')
|
||||||
|
|
||||||
|
# Create server should have been called
|
||||||
|
api.create_server_from_snapshot.assert_called_with(
|
||||||
|
snapshot_id='snap1',
|
||||||
|
name=userservice._name,
|
||||||
|
availability_zone=service.availability_zone.value,
|
||||||
|
flavor_id=service.flavor.value,
|
||||||
|
network_id=service.network.value,
|
||||||
|
security_groups_ids=service.security_groups.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
vmid = userservice._vmid
|
||||||
|
|
||||||
|
# Set power state of machine to running (userservice._vmid)
|
||||||
|
fixtures.get_id(fixtures.SERVERS_LIST, vmid).power_state = (
|
||||||
|
openstack_types.PowerState.RUNNING
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ensure that in the event of failure, we don't loop forever
|
||||||
|
for _ in limited_iterator(lambda: state == types.states.TaskState.RUNNING, limit=128):
|
||||||
|
state = userservice.check_state()
|
||||||
|
|
||||||
|
self.assertEqual(state, types.states.TaskState.FINISHED, f'Error on {to_test} deployment')
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
userservice._name[: len(service.get_basename())],
|
||||||
|
service.get_basename(),
|
||||||
|
f'Error on {to_test} deployment',
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
len(userservice._name),
|
||||||
|
len(service.get_basename()) + service.get_lenname(),
|
||||||
|
f'Error on {to_test} deployment',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get server should have been called at least once
|
||||||
|
api.get_server.assert_called_with(vmid)
|
||||||
|
|
||||||
|
# Mac an ip should have been set
|
||||||
|
self.assertNotEqual(userservice._mac, '', f'Error on {to_test} deployment')
|
||||||
|
self.assertNotEqual(userservice._ip, '', f'Error on {to_test} deployment')
|
||||||
|
|
||||||
|
# And queue must be finished
|
||||||
|
self.assertEqual(userservice._queue, [Operation.FINISH], f'Error on {to_test} deployment')
|
||||||
|
|
||||||
|
def test_userservice_cancel(self) -> None:
|
||||||
|
"""
|
||||||
|
Test the user service
|
||||||
|
"""
|
||||||
|
for prov in (fixtures.create_provider_legacy(), fixtures.create_provider()):
|
||||||
|
with fixtures.patch_provider_api(legacy=prov.legacy) as _api:
|
||||||
|
service = fixtures.create_live_service(prov)
|
||||||
|
userservice = fixtures.create_live_userservice(service=service)
|
||||||
|
publication = userservice.publication()
|
||||||
|
publication._template_id = 'snap1'
|
||||||
|
|
||||||
|
state = userservice.deploy_for_user(models.User())
|
||||||
|
|
||||||
|
self.assertEqual(state, types.states.TaskState.RUNNING)
|
||||||
|
|
||||||
|
server = fixtures.get_id(fixtures.SERVERS_LIST, userservice._vmid)
|
||||||
|
server.power_state = openstack_types.PowerState.RUNNING
|
||||||
|
|
||||||
|
current_op = userservice._get_current_op()
|
||||||
|
|
||||||
|
# Invoke cancel
|
||||||
|
state = userservice.cancel()
|
||||||
|
|
||||||
|
self.assertEqual(state, types.states.TaskState.RUNNING)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
userservice._queue,
|
||||||
|
[current_op] + [Operation.STOP, Operation.REMOVE, Operation.FINISH],
|
||||||
|
)
|
||||||
|
|
||||||
|
counter = 0
|
||||||
|
for counter in limited_iterator(lambda: state == types.states.TaskState.RUNNING, limit=128):
|
||||||
|
state = userservice.check_state()
|
||||||
|
if counter > 5:
|
||||||
|
server.power_state = openstack_types.PowerState.SHUTDOWN
|
||||||
|
|
||||||
|
self.assertGreater(counter, 5)
|
||||||
|
self.assertEqual(state, types.states.TaskState.FINISHED)
|
||||||
|
|
||||||
|
def test_userservice_error(self) -> None:
|
||||||
|
"""
|
||||||
|
This test will not have keep on error active, and will create correctly
|
||||||
|
but will error on set_ready, so it will be put on error state
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_userservice_error_keep_create(self) -> None:
|
||||||
|
"""
|
||||||
|
This test will have keep on error active, and will create incorrectly
|
||||||
|
so vm will be deleted and put on error state
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_userservice_error_keep(self) -> None:
|
||||||
|
"""
|
||||||
|
This test will have keep on error active, and will create correctly
|
||||||
|
but error will came later (on set_ready) and will not be put on error state
|
||||||
|
nor deleted
|
||||||
|
"""
|
||||||
|
pass
|
Loading…
x
Reference in New Issue
Block a user