mirror of
https://github.com/dkmstr/openuds.git
synced 2025-03-12 04:58:34 +03:00
Added type checking to all deployment tasks
This commit is contained in:
parent
7e33f381d4
commit
73042dd76f
@ -32,11 +32,10 @@ Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import datetime
|
||||
import logging
|
||||
import re
|
||||
import typing
|
||||
import collections.abc
|
||||
import xml.sax # nosec: used to parse trusted xml provided only by administrators
|
||||
from urllib import parse
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import requests
|
||||
from django.utils.translation import gettext
|
||||
@ -55,6 +54,7 @@ from uds.core.util.model import sql_datetime
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from django.http import HttpRequest
|
||||
from urllib.parse import ParseResult
|
||||
|
||||
from uds.core.types.requests import ExtendedHttpRequestWithUser
|
||||
|
||||
@ -454,9 +454,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
xml.sax.parseString(idp_metadata, xml.sax.ContentHandler()) # type: ignore # nosec: url provided by admin
|
||||
except Exception as e:
|
||||
msg = (gettext(' (obtained from URL)') if from_url else '') + str(e)
|
||||
raise exceptions.ui.ValidationError(
|
||||
gettext('XML does not seem valid for IDP Metadata ') + msg
|
||||
)
|
||||
raise exceptions.ui.ValidationError(gettext('XML does not seem valid for IDP Metadata ') + msg)
|
||||
|
||||
# Now validate regular expressions, if they exists
|
||||
auth_utils.validate_regex_field(self.attrs_username)
|
||||
@ -468,7 +466,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
request: 'ExtendedHttpRequest',
|
||||
params: typing.Optional['types.auth.AuthCallbackParams'] = None,
|
||||
) -> dict[str, typing.Any]:
|
||||
manage_url_obj: 'parse.ParseResult' = parse.urlparse(self.manage_url.value)
|
||||
manage_url_obj = typing.cast('ParseResult', urlparse(self.manage_url.value))
|
||||
script_path: str = manage_url_obj.path
|
||||
host: str = manage_url_obj.netloc
|
||||
if ':' in host:
|
||||
@ -524,7 +522,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
else:
|
||||
val = self.idp_metadata.value
|
||||
|
||||
return OneLogin_Saml2_IdPMetadataParser.parse(val)
|
||||
return OneLogin_Saml2_IdPMetadataParser.parse(val) # pyright: ignore reportUnknownVariableType
|
||||
|
||||
def build_onelogin_settings(self) -> dict[str, typing.Any]:
|
||||
return {
|
||||
@ -547,14 +545,17 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
'idp': self.get_idp_metadata_dict()['idp'],
|
||||
'security': {
|
||||
# in days, converted to seconds, this is a duration
|
||||
'metadataCacheDuration': self.metadata_cache_duration.as_int() * 86400
|
||||
if self.metadata_cache_duration.value > 0
|
||||
else 86400 * 365 * 10,
|
||||
'metadataCacheDuration': (
|
||||
self.metadata_cache_duration.as_int() * 86400
|
||||
if self.metadata_cache_duration.value > 0
|
||||
else 86400 * 365 * 10
|
||||
),
|
||||
# This is a date of end of validity
|
||||
'metadataValidUntil': sql_datetime()
|
||||
+ datetime.timedelta(days=self.metadata_validity_duration.as_int())
|
||||
if self.metadata_cache_duration.value > 0
|
||||
else sql_datetime() + datetime.timedelta(days=365 * 10),
|
||||
'metadataValidUntil': (
|
||||
sql_datetime() + datetime.timedelta(days=self.metadata_validity_duration.as_int())
|
||||
if self.metadata_cache_duration.value > 0
|
||||
else sql_datetime() + datetime.timedelta(days=365 * 10)
|
||||
),
|
||||
'nameIdEncrypted': self.use_name_id_encrypted.as_bool(),
|
||||
'authnRequestsSigned': self.use_authn_requests_signed.as_bool(),
|
||||
'logoutRequestSigned': self.logout_request_signed.as_bool(),
|
||||
@ -585,8 +586,10 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
)
|
||||
def get_sp_metadata(self) -> str:
|
||||
saml_settings = OneLogin_Saml2_Settings(settings=self.build_onelogin_settings())
|
||||
metadata = saml_settings.get_sp_metadata()
|
||||
errors = saml_settings.validate_metadata(metadata)
|
||||
metadata: typing.Any = saml_settings.get_sp_metadata()
|
||||
errors: list[typing.Any] = saml_settings.validate_metadata( # pyright: ignore reportUnknownVariableType
|
||||
metadata
|
||||
)
|
||||
if len(errors) > 0:
|
||||
raise exceptions.auth.AuthenticatorException(
|
||||
gettext('Error validating SP metadata: ') + str(errors)
|
||||
@ -640,12 +643,14 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
settings = OneLogin_Saml2_Settings(settings=self.build_onelogin_settings())
|
||||
auth = OneLogin_Saml2_Auth(req, settings)
|
||||
|
||||
url = auth.process_slo(request_id=logout_req_id)
|
||||
url: str = auth.process_slo(request_id=logout_req_id) # pyright: ignore reportUnknownVariableType
|
||||
|
||||
errors = auth.get_errors()
|
||||
errors: list[str] = auth.get_errors()
|
||||
|
||||
if errors:
|
||||
logger.debug('Error on SLO: %s', auth.get_last_response_xml())
|
||||
logger.debug(
|
||||
'Error on SLO: %s', auth.get_last_response_xml() # pyright: ignore reportUnknownVariableType
|
||||
)
|
||||
logger.debug('post_data: %s', req['post_data'])
|
||||
logger.info('Errors processing logout request: %s', errors)
|
||||
raise exceptions.auth.AuthenticatorException(gettext('Error processing SLO: ') + str(errors))
|
||||
@ -664,20 +669,20 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
self,
|
||||
parameters: 'types.auth.AuthCallbackParams',
|
||||
gm: 'auths.GroupsManager',
|
||||
request: 'ExtendedHttpRequestWithUser',
|
||||
request: 'ExtendedHttpRequest',
|
||||
) -> types.auth.AuthenticationResult:
|
||||
req = self.build_req_from_request(request, params=parameters)
|
||||
|
||||
if 'logout' in parameters.get_params:
|
||||
return self.logout_callback(req, request)
|
||||
return self.logout_callback(req, typing.cast('ExtendedHttpRequestWithUser', request))
|
||||
|
||||
try:
|
||||
settings = OneLogin_Saml2_Settings(settings=self.build_onelogin_settings())
|
||||
auth = OneLogin_Saml2_Auth(req, settings)
|
||||
auth.process_response()
|
||||
auth.process_response() # pyright: ignore reportUnknownVariableType
|
||||
except Exception as e:
|
||||
raise exceptions.auth.AuthenticatorException(gettext('Error processing SAML response: ') + str(e))
|
||||
errors = auth.get_errors()
|
||||
errors: list[str] = auth.get_errors()
|
||||
if errors:
|
||||
raise exceptions.auth.AuthenticatorException('SAML response error: ' + str(errors))
|
||||
|
||||
@ -705,24 +710,34 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
# url=auth.redirect_to(req['post_data']['RelayState'])
|
||||
# )
|
||||
|
||||
attributes = auth.get_attributes().copy()
|
||||
attributes: dict[str, typing.Any] = ( # pyright: ignore reportUnknownVariableType
|
||||
auth.get_attributes().copy() # pyright: ignore reportUnknownVariableType
|
||||
)
|
||||
# Append attributes by its friendly name
|
||||
attributes.update(auth.get_friendlyname_attributes())
|
||||
attributes.update(auth.get_friendlyname_attributes()) # pyright: ignore reportUnknownVariableType
|
||||
|
||||
if not attributes:
|
||||
raise exceptions.auth.AuthenticatorException(gettext('No attributes returned from IdP'))
|
||||
logger.debug("Attributes: %s", attributes)
|
||||
logger.debug("Attributes: %s", attributes) # pyright: ignore reportUnknownVariableType
|
||||
|
||||
# Now that we have attributes, we can extract values from this, map groups, etc...
|
||||
username = ''.join(
|
||||
auth_utils.process_regex_field(self.attrs_username.value, attributes)
|
||||
auth_utils.process_regex_field(
|
||||
self.attrs_username.value, attributes # pyright: ignore reportUnknownVariableType
|
||||
)
|
||||
) # in case of multiple values is returned, join them
|
||||
logger.debug('Username: %s', username)
|
||||
|
||||
groups = auth_utils.process_regex_field(self.attrs_groupname.value, attributes)
|
||||
groups = auth_utils.process_regex_field(
|
||||
self.attrs_groupname.value, attributes # pyright: ignore reportUnknownVariableType
|
||||
)
|
||||
logger.debug('Groups: %s', groups)
|
||||
|
||||
realName = ' '.join(auth_utils.process_regex_field(self.attrs_realname.value, attributes))
|
||||
realName = ' '.join(
|
||||
auth_utils.process_regex_field(
|
||||
self.attrs_realname.value, attributes # pyright: ignore reportUnknownVariableType
|
||||
)
|
||||
)
|
||||
logger.debug('Real name: %s', realName)
|
||||
|
||||
# store groups for this username at storage, so we can check it at a later stage
|
||||
@ -732,7 +747,11 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
if self.mfa_attr.value.strip():
|
||||
self.storage.put_pickle(
|
||||
self.mfa_storage_key(username),
|
||||
''.join(auth_utils.process_regex_field(self.mfa_attr.value, attributes)),
|
||||
''.join(
|
||||
auth_utils.process_regex_field(
|
||||
self.mfa_attr.value, attributes # pyright: ignore reportUnknownVariableType
|
||||
)
|
||||
),
|
||||
) # in case multipel values is returned, join them
|
||||
else:
|
||||
self.storage.remove(self.mfa_storage_key(username))
|
||||
@ -768,7 +787,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
|
||||
return types.auth.AuthenticationResult(
|
||||
success=types.auth.AuthenticationState.REDIRECT,
|
||||
url=auth.logout(
|
||||
url=auth.logout( # pyright: ignore reportUnknownVariableType
|
||||
name_id=saml.get('nameid'),
|
||||
session_index=saml.get('session_index'),
|
||||
nq=saml.get('nameid_namequalifier'),
|
||||
@ -796,11 +815,11 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
req = self.build_req_from_request(request)
|
||||
auth = OneLogin_Saml2_Auth(req, self.build_onelogin_settings())
|
||||
|
||||
return f'window.location="{auth.login()}";'
|
||||
return f'window.location="{auth.login()}";' # pyright: ignore reportUnknownVariableType
|
||||
|
||||
def remove_user(self, username):
|
||||
def remove_user(self, username: str) -> None:
|
||||
"""
|
||||
Clean ups storage data
|
||||
"""
|
||||
self.storage.remove(username)
|
||||
self.storage.remove('lasso-' + username)
|
||||
self.mfa_clean(username)
|
@ -48,6 +48,7 @@ if typing.TYPE_CHECKING:
|
||||
HttpResponse,
|
||||
)
|
||||
from uds import models
|
||||
from uds.core.types.requests import ExtendedHttpRequest
|
||||
from uds.core.environment import Environment
|
||||
from uds.core import types
|
||||
from .groups_manager import GroupsManager
|
||||
@ -516,7 +517,7 @@ class Authenticator(Module):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_javascript(self, request: 'HttpRequest') -> typing.Optional[str]:
|
||||
def get_javascript(self, request: 'ExtendedHttpRequest') -> typing.Optional[str]:
|
||||
"""
|
||||
If you override this method, and returns something different of None,
|
||||
UDS will consider your authenticator as "Owner draw", that is, that it
|
||||
|
@ -72,7 +72,7 @@ BROWSERS_RE: dict[types.os.KnownBrowser, tuple[typing.Pattern[str], ...]] = {
|
||||
types.os.KnownBrowser.EDGE: (re.compile(r'Edg/([0-9.]+)'),),
|
||||
}
|
||||
|
||||
BROWSER_RULES: dict[types.os.KnownBrowser, tuple] = {
|
||||
BROWSER_RULES: dict[types.os.KnownBrowser, tuple[types.os.KnownBrowser, tuple[types.os.KnownBrowser, ...]]] = {
|
||||
types.os.KnownBrowser.EDGE: (types.os.KnownBrowser.EDGE, ()),
|
||||
types.os.KnownBrowser.CHROME: (
|
||||
types.os.KnownBrowser.CHROME,
|
||||
|
@ -86,7 +86,7 @@ class DelayedTaskRunner(metaclass=singleton.Singleton):
|
||||
_hostname: typing.ClassVar[str] # "Our" hostname
|
||||
_keep_running: typing.ClassVar[bool] # If we should keep it running
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
DelayedTaskRunner._hostname = gethostname()
|
||||
DelayedTaskRunner._keep_running = True
|
||||
logger.debug("Initialized delayed task runner for host %s", DelayedTaskRunner._hostname)
|
||||
@ -117,7 +117,7 @@ class DelayedTaskRunner(metaclass=singleton.Singleton):
|
||||
task: DBDelayedTask = (
|
||||
DBDelayedTask.objects.select_for_update()
|
||||
.filter(filt)
|
||||
.order_by('execution_time')[0] # type: ignore # Slicing is not supported by pylance right now
|
||||
.order_by('execution_time')[0]
|
||||
) # @UndefinedVariable
|
||||
if task.insert_date > now + timedelta(seconds=30):
|
||||
logger.warning('Executed %s due to insert_date being in the future!', task.type)
|
||||
|
@ -161,7 +161,7 @@ class Scheduler:
|
||||
job: DBScheduler = (
|
||||
DBScheduler.objects.select_for_update()
|
||||
.filter(fltr)
|
||||
.order_by('next_execution')[0]# type: ignore # Slicing is not supported by pylance right now
|
||||
.order_by('next_execution')[0]
|
||||
)
|
||||
if job.last_execution > now:
|
||||
logger.warning(
|
||||
|
@ -270,7 +270,7 @@ class PublicationManager(metaclass=singleton.Singleton):
|
||||
Manager responsible of controlling publications
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
@ -284,7 +284,7 @@ class PublicationManager(metaclass=singleton.Singleton):
|
||||
|
||||
def publish(
|
||||
self, servicePool: ServicePool, changeLog: typing.Optional[str] = None
|
||||
): # pylint: disable=no-self-use
|
||||
) -> None:
|
||||
"""
|
||||
Initiates the publication of a service pool, or raises an exception if this cannot be done
|
||||
:param servicePool: Service pool object (db object)
|
||||
@ -330,7 +330,7 @@ class PublicationManager(metaclass=singleton.Singleton):
|
||||
|
||||
def cancel(
|
||||
self, publication: ServicePoolPublication
|
||||
): # pylint: disable=no-self-use
|
||||
) -> ServicePoolPublication:
|
||||
"""
|
||||
Invoked to cancel a publication.
|
||||
Double invokation (i.e. invokation over a "cancelling" item) will lead to a "forced" cancellation (unclean)
|
||||
@ -372,7 +372,7 @@ class PublicationManager(metaclass=singleton.Singleton):
|
||||
|
||||
def unpublish(
|
||||
self, servicepool_publication: ServicePoolPublication
|
||||
): # pylint: disable=no-self-use
|
||||
) -> None:
|
||||
"""
|
||||
Unpublishes an active (usable) or removable publication
|
||||
:param servicePoolPub: Publication to unpublish
|
||||
|
@ -30,7 +30,6 @@ Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import datetime
|
||||
import logging
|
||||
import datetime
|
||||
import typing
|
||||
import collections.abc
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
@ -60,10 +59,8 @@ class ServerManager(metaclass=singleton.Singleton):
|
||||
MAX_COUNTERS_AGE: typing.Final[datetime.timedelta] = datetime.timedelta(days=3)
|
||||
BASE_PROPERTY_NAME: typing.Final[str] = 'sm_usr_'
|
||||
|
||||
last_counters_clean: datetime.datetime
|
||||
|
||||
def __init__(self):
|
||||
self.last_counters_clean = datetime.datetime.now()
|
||||
# Singleton, can initialize here
|
||||
last_counters_clean: datetime.datetime = datetime.datetime.now()
|
||||
|
||||
@staticmethod
|
||||
def manager() -> 'ServerManager':
|
||||
@ -230,7 +227,7 @@ class ServerManager(metaclass=singleton.Singleton):
|
||||
# If server is forced, and server is part of the group, use it
|
||||
if server:
|
||||
if (
|
||||
server.groups.filter(uuid=server_group.uuid).exclude(uuid__in=excluded_servers_uuids).count()
|
||||
server.groups.filter(uuid=server_group.uuid).exclude(uuid__in=excluded_servers_uuids).count()
|
||||
== 0
|
||||
):
|
||||
raise exceptions.UDSException(_('Server is not part of the group'))
|
||||
@ -481,7 +478,7 @@ class ServerManager(metaclass=singleton.Singleton):
|
||||
Args:
|
||||
serverGroup: Server group to realize maintenance on
|
||||
"""
|
||||
for k, v in serverGroup.properties.items():
|
||||
for k, _ in serverGroup.properties.items():
|
||||
if k.startswith(self.BASE_PROPERTY_NAME):
|
||||
uuid = k[len(self.BASE_PROPERTY_NAME) :]
|
||||
try:
|
||||
|
@ -28,6 +28,7 @@
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import abc
|
||||
import threading
|
||||
import time
|
||||
import signal
|
||||
@ -45,24 +46,26 @@ from uds.core.util import singleton
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseThread(threading.Thread):
|
||||
def request_stop(self):
|
||||
class BaseThread(threading.Thread, abc.ABC):
|
||||
|
||||
@abc.abstractmethod
|
||||
def request_stop(self) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class SchedulerThread(BaseThread):
|
||||
def run(self):
|
||||
def run(self) -> None:
|
||||
Scheduler.scheduler().run()
|
||||
|
||||
def request_stop(self):
|
||||
def request_stop(self) -> None:
|
||||
Scheduler.scheduler().notify_termination()
|
||||
|
||||
|
||||
class DelayedTaskThread(BaseThread):
|
||||
def run(self):
|
||||
def run(self) -> None:
|
||||
DelayedTaskRunner.runner().run()
|
||||
|
||||
def request_stop(self):
|
||||
def request_stop(self) -> None:
|
||||
DelayedTaskRunner.runner().request_stop()
|
||||
|
||||
|
||||
@ -73,7 +76,7 @@ class TaskManager(metaclass=singleton.Singleton):
|
||||
keep_running: bool
|
||||
threads: list[BaseThread]
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.keep_running = True
|
||||
self.threads = []
|
||||
|
||||
|
@ -231,7 +231,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
assigned = self._create_assigned_user_service_at_db_from_pool(service_pool, user)
|
||||
|
||||
assignedInstance = assigned.get_instance()
|
||||
state = assignedInstance.deploy_for_user(user)
|
||||
state = types.states.State.from_str(assignedInstance.deploy_for_user(user))
|
||||
|
||||
UserServiceOpChecker.make_unique(assigned, assignedInstance, state)
|
||||
|
||||
@ -271,7 +271,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
|
||||
# Now, get from serviceInstance the data
|
||||
assigned_userservice_instance = assigned.get_instance()
|
||||
state = serviceInstance.assign_from_assignables(assignable_id, user, assigned_userservice_instance)
|
||||
state = types.states.State.from_str(serviceInstance.assign_from_assignables(assignable_id, user, assigned_userservice_instance))
|
||||
# assigned.u(assignedInstance)
|
||||
|
||||
UserServiceOpChecker.make_unique(assigned, assigned_userservice_instance, state)
|
||||
@ -323,7 +323,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
else:
|
||||
user_service.set_state(State.CANCELING)
|
||||
# We simply notify service that it should cancel operation
|
||||
state = user_service_instance.cancel()
|
||||
state = types.states.State.from_str(user_service_instance.cancel())
|
||||
|
||||
# Data will be serialized on makeUnique process
|
||||
# If cancel is not supported, base cancel always returns "FINISHED", and
|
||||
@ -690,7 +690,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
if kind in 'A': # This is an assigned service
|
||||
logger.debug('Getting A service %s', uuid_user_service)
|
||||
user_service = UserService.objects.get(uuid=uuid_user_service, user=user)
|
||||
typing.cast(UserService, user_service).deployed_service.validate_user(user)
|
||||
user_service.deployed_service.validate_user(user)
|
||||
else:
|
||||
try:
|
||||
service_pool: ServicePool = ServicePool.objects.get(uuid=uuid_user_service)
|
||||
|
@ -77,13 +77,13 @@ class StateUpdater:
|
||||
def log_ip(self):
|
||||
ip = self.user_service_instance.get_ip()
|
||||
|
||||
if ip is not None and ip != '':
|
||||
if ip:
|
||||
self.user_service.log_ip(ip)
|
||||
|
||||
def check_later(self):
|
||||
UserServiceOpChecker.check_later(self.user_service, self.user_service_instance)
|
||||
|
||||
def run(self, state):
|
||||
def run(self, state: 'State'):
|
||||
executor = {
|
||||
State.RUNNING: self.running,
|
||||
State.ERROR: self.error,
|
||||
@ -104,7 +104,7 @@ class StateUpdater:
|
||||
|
||||
logger.debug('Executor for %s done', self.user_service.friendly_name)
|
||||
|
||||
def finish(self):
|
||||
def finish(self) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
def running(self):
|
||||
@ -212,7 +212,7 @@ class UserServiceOpChecker(DelayedTask):
|
||||
self._state = service.state
|
||||
|
||||
@staticmethod
|
||||
def make_unique(userservice: UserService, userservice_instance: services.UserService, state: str):
|
||||
def make_unique(userservice: UserService, userservice_instance: services.UserService, state: State):
|
||||
"""
|
||||
This method ensures that there will be only one delayedtask related to the userService indicated
|
||||
"""
|
||||
@ -220,7 +220,7 @@ class UserServiceOpChecker(DelayedTask):
|
||||
UserServiceOpChecker.state_updater(userservice, userservice_instance, state)
|
||||
|
||||
@staticmethod
|
||||
def state_updater(userservice: UserService, userservice_instance: services.UserService, state: str):
|
||||
def state_updater(userservice: UserService, userservice_instance: services.UserService, state: State):
|
||||
"""
|
||||
Checks the value returned from invocation to publish or checkPublishingState, updating the servicePoolPub database object
|
||||
Return True if it has to continue checking, False if finished
|
||||
@ -278,7 +278,7 @@ class UserServiceOpChecker(DelayedTask):
|
||||
logger.debug('Checking user service finished %s', self._svrId)
|
||||
user_service: 'UserService|None' = None
|
||||
try:
|
||||
user_service = typing.cast(UserService, UserService.objects.get(pk=self._svrId))
|
||||
user_service = UserService.objects.get(pk=self._svrId)
|
||||
if user_service.state != self._state:
|
||||
logger.debug('Task overrided by another task (state of item changed)')
|
||||
# This item is no longer valid, returning will not check it again (no checkLater called)
|
||||
|
@ -53,7 +53,7 @@ class MessageProcessorThread(BaseThread):
|
||||
]
|
||||
_cached_stamp: float
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.name = 'MessageProcessorThread'
|
||||
self._cached_providers = None
|
||||
@ -73,7 +73,7 @@ class MessageProcessorThread(BaseThread):
|
||||
self._cached_stamp = time.time()
|
||||
return self._cached_providers
|
||||
|
||||
def run(self):
|
||||
def run(self) -> None:
|
||||
while self._keep_running:
|
||||
# Locate all notifications from "persistent" and try to process them
|
||||
# If no notification can be fully resolved, it will be kept in the database
|
||||
@ -149,5 +149,5 @@ class MessageProcessorThread(BaseThread):
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
def request_stop(self):
|
||||
def request_stop(self) -> None:
|
||||
self._keep_running = False
|
||||
|
@ -33,6 +33,7 @@ Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
import abc
|
||||
import typing
|
||||
import collections.abc
|
||||
from uds.core import types
|
||||
|
||||
from uds.core.environment import Environmentable
|
||||
from uds.core.serializable import Serializable
|
||||
@ -178,7 +179,7 @@ class Publication(Environmentable, Serializable):
|
||||
return self._uuid
|
||||
|
||||
@abc.abstractmethod
|
||||
def publish(self) -> str:
|
||||
def publish(self) -> types.states.State:
|
||||
"""
|
||||
This method is invoked whenever the administrator requests a new publication.
|
||||
|
||||
@ -214,7 +215,7 @@ class Publication(Environmentable, Serializable):
|
||||
raise NotImplementedError(f'publish method for class {self.__class__.__name__} not provided! ')
|
||||
|
||||
@abc.abstractmethod
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the expected return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
@ -267,7 +268,7 @@ class Publication(Environmentable, Serializable):
|
||||
return 'unknown'
|
||||
|
||||
@abc.abstractmethod
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the expected return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
@ -288,7 +289,7 @@ class Publication(Environmentable, Serializable):
|
||||
raise NotImplementedError(f'destroy method for class {self.__class__.__name__} not provided!')
|
||||
|
||||
@abc.abstractmethod
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the expected return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
|
@ -382,7 +382,7 @@ class Service(Module):
|
||||
"""
|
||||
return None
|
||||
|
||||
def get_valid_id(self, idsList: collections.abc.Iterable[str]) -> typing.Optional[str]:
|
||||
def get_valid_id(self, ids: collections.abc.Iterable[str]) -> typing.Optional[str]:
|
||||
"""
|
||||
Looks for an "owned" id in the provided list. If found, returns it, else return None
|
||||
|
||||
|
@ -36,11 +36,9 @@ import enum
|
||||
import logging
|
||||
import typing
|
||||
import collections.abc
|
||||
from webbrowser import Opera
|
||||
|
||||
from uds.core import services, consts
|
||||
from uds.core import services, consts, types
|
||||
from uds.core.managers.userservice import UserServiceManager
|
||||
from uds.core.types.states import State
|
||||
from uds.core.util import log, autoserializable
|
||||
from uds.core.util.model import sql_stamp_seconds
|
||||
|
||||
@ -136,9 +134,9 @@ class FixedUserService(services.UserService, autoserializable.AutoSerializable,
|
||||
@typing.final
|
||||
def _retry_later(self) -> str:
|
||||
self._push_front_op(Operation.RETRY)
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _error(self, reason: typing.Union[str, Exception]) -> str:
|
||||
def _error(self, reason: typing.Union[str, Exception]) -> types.states.State:
|
||||
"""
|
||||
Internal method to set object as error state
|
||||
|
||||
@ -159,7 +157,7 @@ class FixedUserService(services.UserService, autoserializable.AutoSerializable,
|
||||
|
||||
self._queue = [Operation.ERROR]
|
||||
self._reason = reason
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
# Utility overrides for type checking...
|
||||
# Probably, overriden again on child classes
|
||||
@ -188,7 +186,7 @@ class FixedUserService(services.UserService, autoserializable.AutoSerializable,
|
||||
return ''
|
||||
|
||||
@typing.final
|
||||
def deploy_for_user(self, user: 'models.User') -> str:
|
||||
def deploy_for_user(self, user: 'models.User') -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for an user.
|
||||
"""
|
||||
@ -205,15 +203,15 @@ class FixedUserService(services.UserService, autoserializable.AutoSerializable,
|
||||
return self._execute_queue()
|
||||
|
||||
@typing.final
|
||||
def _execute_queue(self) -> str:
|
||||
def _execute_queue(self) -> types.states.State:
|
||||
self._debug('executeQueue')
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: dict[Operation, collections.abc.Callable[[], None]] = {
|
||||
Operation.CREATE: self._create,
|
||||
@ -237,7 +235,7 @@ class FixedUserService(services.UserService, autoserializable.AutoSerializable,
|
||||
|
||||
operation_runner()
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
except Exception as e:
|
||||
logger.exception('Unexpected FixedUserService exception: %s', e)
|
||||
return self._error(str(e))
|
||||
@ -305,38 +303,38 @@ class FixedUserService(services.UserService, autoserializable.AutoSerializable,
|
||||
self.service().remove_and_free_machine(self._vmid)
|
||||
|
||||
# Check methods
|
||||
def _create_checker(self) -> str:
|
||||
def _create_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks the state of a deploy for an user or cache
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _snapshot_create_checker(self) -> str:
|
||||
def _snapshot_create_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks the state of a snapshot creation
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _snapshot_recover_checker(self) -> str:
|
||||
def _snapshot_recover_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks the state of a snapshot recovery
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _process_token_checker(self) -> str:
|
||||
def _process_token_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks the state of a token processing
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _retry_checker(self) -> str:
|
||||
return State.FINISHED
|
||||
def _retry_checker(self) -> types.states.State:
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _wait_checker(self) -> str:
|
||||
return State.FINISHED
|
||||
def _wait_checker(self) -> types.states.State:
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _nop_checker(self) -> str:
|
||||
return State.FINISHED
|
||||
def _nop_checker(self) -> types.states.State:
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _start_machine(self) -> None:
|
||||
"""
|
||||
@ -344,11 +342,11 @@ class FixedUserService(services.UserService, autoserializable.AutoSerializable,
|
||||
"""
|
||||
pass
|
||||
|
||||
def _start_checker(self) -> str:
|
||||
def _start_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if machine has started
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _stop_machine(self) -> None:
|
||||
"""
|
||||
@ -356,11 +354,11 @@ class FixedUserService(services.UserService, autoserializable.AutoSerializable,
|
||||
"""
|
||||
pass
|
||||
|
||||
def _stop_checker(self) -> str:
|
||||
def _stop_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if machine has stoped
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
# Not abstract methods, defaults to stop machine
|
||||
def _soft_shutdown_machine(self) -> None:
|
||||
@ -368,17 +366,17 @@ class FixedUserService(services.UserService, autoserializable.AutoSerializable,
|
||||
"""
|
||||
return self._stop_machine() # Default is to stop the machine
|
||||
|
||||
def _soft_shutdown_checker(self) -> str:
|
||||
def _soft_shutdown_checker(self) -> types.states.State:
|
||||
return self._stop_checker() # Default is to check if machine has stopped
|
||||
|
||||
def _removed_checker(self) -> str:
|
||||
def _removed_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if a machine has been removed
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
@typing.final
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Check what operation is going on, and acts acordly to it
|
||||
"""
|
||||
@ -386,12 +384,12 @@ class FixedUserService(services.UserService, autoserializable.AutoSerializable,
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: dict[Operation, collections.abc.Callable[[], str]] = {
|
||||
fncs: dict[Operation, collections.abc.Callable[[], types.states.State]] = {
|
||||
Operation.CREATE: self._create_checker,
|
||||
Operation.RETRY: self._retry_checker,
|
||||
Operation.WAIT: self._wait_checker,
|
||||
@ -406,13 +404,13 @@ class FixedUserService(services.UserService, autoserializable.AutoSerializable,
|
||||
}
|
||||
|
||||
try:
|
||||
check_function: typing.Optional[collections.abc.Callable[[], str]] = fncs.get(op, None)
|
||||
check_function: typing.Optional[collections.abc.Callable[[], types.states.State]] = fncs.get(op, None)
|
||||
|
||||
if check_function is None:
|
||||
return self._error('Unknown operation found at check queue ({0})'.format(op))
|
||||
|
||||
state = check_function()
|
||||
if state == State.FINISHED:
|
||||
if state == types.states.State.FINISHED:
|
||||
self._pop_current_op() # Remove runing op, till now only was "peek"
|
||||
return self._execute_queue()
|
||||
|
||||
@ -441,7 +439,7 @@ class FixedUserService(services.UserService, autoserializable.AutoSerializable,
|
||||
return self._reason
|
||||
|
||||
@typing.final
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
"""
|
||||
Invoked for destroying a deployed service
|
||||
"""
|
||||
@ -449,7 +447,7 @@ class FixedUserService(services.UserService, autoserializable.AutoSerializable,
|
||||
return self._execute_queue()
|
||||
|
||||
@typing.final
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
|
@ -30,23 +30,21 @@
|
||||
"""
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import typing
|
||||
import collections.abc
|
||||
from uds.core import types
|
||||
import typing
|
||||
|
||||
from uds.core import types
|
||||
from uds.core.environment import Environmentable
|
||||
from uds.core.serializable import Serializable
|
||||
from uds.core.types.states import State
|
||||
from uds.core.util import log
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds import models
|
||||
from uds.core import services
|
||||
from uds.core import osmanagers
|
||||
from uds.core import osmanagers, services
|
||||
from uds.core.environment import Environment
|
||||
from uds.core.util.unique_name_generator import UniqueNameGenerator
|
||||
from uds.core.util.unique_mac_generator import UniqueMacGenerator
|
||||
from uds.core.util.unique_gid_generator import UniqueGIDGenerator
|
||||
from uds.core.util.unique_mac_generator import UniqueMacGenerator
|
||||
from uds.core.util.unique_name_generator import UniqueNameGenerator
|
||||
|
||||
|
||||
class UserService(Environmentable, Serializable):
|
||||
@ -275,7 +273,7 @@ class UserService(Environmentable, Serializable):
|
||||
"""
|
||||
raise NotImplementedError('Base getUniqueId for User Deployment called!!!')
|
||||
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> str: # pylint: disable=unused-argument
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
@ -301,7 +299,7 @@ class UserService(Environmentable, Serializable):
|
||||
to the core. Take that into account and handle exceptions inside
|
||||
this method.
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def get_ip(self) -> str:
|
||||
"""
|
||||
@ -320,7 +318,7 @@ class UserService(Environmentable, Serializable):
|
||||
If you assign the service IP by your own methods, do not override this
|
||||
"""
|
||||
|
||||
def set_ready(self) -> str:
|
||||
def set_ready(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
@ -352,9 +350,9 @@ class UserService(Environmentable, Serializable):
|
||||
to the core. Take that into account and handle exceptions inside
|
||||
this method.
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def deploy_for_cache(self, cacheLevel: int) -> str:
|
||||
def deploy_for_cache(self, level: int) -> types.states.State:
|
||||
"""
|
||||
Deploys a user deployment as cache.
|
||||
|
||||
@ -393,7 +391,7 @@ class UserService(Environmentable, Serializable):
|
||||
"""
|
||||
raise Exception(f'Base deploy for cache invoked! for class {self.__class__.__name__}')
|
||||
|
||||
def deploy_for_user(self, user: 'models.User') -> str:
|
||||
def deploy_for_user(self, user: 'models.User') -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for an user.
|
||||
|
||||
@ -428,7 +426,7 @@ class UserService(Environmentable, Serializable):
|
||||
"""
|
||||
raise NotImplementedError(f'Base deploy for user invoked! for class {self.__class__.__name__}')
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the expected return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
@ -466,8 +464,9 @@ class UserService(Environmentable, Serializable):
|
||||
not needed, but can be provided (default implementation of base class does
|
||||
nothing)
|
||||
"""
|
||||
pass
|
||||
|
||||
def move_to_cache(self, newLevel: int) -> str: # pylint: disable=unused-argument
|
||||
def move_to_cache(self, level: int) -> types.states.State: # pylint: disable=unused-argument
|
||||
"""
|
||||
This method is invoked whenever the core needs to move from the current
|
||||
cache level to a new cache level an user deployment.
|
||||
@ -494,7 +493,7 @@ class UserService(Environmentable, Serializable):
|
||||
to the core. Take that into account and handle exceptions inside
|
||||
this method.
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def user_logged_in(self, username: str) -> None:
|
||||
"""
|
||||
@ -510,6 +509,7 @@ class UserService(Environmentable, Serializable):
|
||||
|
||||
The user provided is just an string, that is provided by actors.
|
||||
"""
|
||||
pass
|
||||
|
||||
def user_logged_out(self, username: str) -> None:
|
||||
"""
|
||||
@ -525,6 +525,7 @@ class UserService(Environmentable, Serializable):
|
||||
|
||||
The user provided is just an string, that is provided by actor.
|
||||
"""
|
||||
pass
|
||||
|
||||
def error_reason(self) -> str:
|
||||
"""
|
||||
@ -541,7 +542,7 @@ class UserService(Environmentable, Serializable):
|
||||
"""
|
||||
return 'unknown'
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
@ -559,7 +560,7 @@ class UserService(Environmentable, Serializable):
|
||||
"""
|
||||
raise NotImplementedError(f'destroy method for class {self.__class__.__name__} not provided!')
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
@ -576,7 +577,7 @@ class UserService(Environmentable, Serializable):
|
||||
to the core. Take that into account and handle exceptions inside
|
||||
this method.
|
||||
"""
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
@classmethod
|
||||
def supports_cancel(cls: type['UserService']) -> bool:
|
||||
|
@ -44,9 +44,9 @@ class ExtendedHttpRequest(HttpRequest):
|
||||
ip_version: int
|
||||
ip_proxy: str
|
||||
os: 'types.os.DetectedOsInfo'
|
||||
user: typing.Optional['User']
|
||||
user: typing.Optional['User'] # type: ignore # Override base user to be optional
|
||||
authorized: bool
|
||||
|
||||
|
||||
class ExtendedHttpRequestWithUser(ExtendedHttpRequest):
|
||||
user: 'User'
|
||||
user: 'User' # type: ignore # Has the user always
|
||||
|
@ -40,7 +40,7 @@ from uds.core.util import ensure
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def validate_regex_field(field: ui.gui.TextField, field_value: typing.Optional[str] = None):
|
||||
def validate_regex_field(field: ui.gui.TextField, field_value: typing.Optional[str] = None) -> None:
|
||||
"""
|
||||
Validates the multi line fields refering to attributes
|
||||
"""
|
||||
@ -70,7 +70,7 @@ def get_attributes_regex_field(field: 'ui.gui.TextField|str') -> set[str]:
|
||||
|
||||
res: set[str] = set()
|
||||
for line in content.splitlines():
|
||||
attr, pattern = (line.split('=')[0:2] + [''])[0:2]
|
||||
attr, _pattern = (line.split('=')[0:2] + [''])[0:2]
|
||||
|
||||
# If attributes concateated with +, add all
|
||||
if '+' in attr:
|
||||
|
@ -30,7 +30,6 @@ Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import dataclasses
|
||||
import functools
|
||||
import hashlib
|
||||
import inspect
|
||||
import logging
|
||||
import threading
|
||||
|
@ -58,16 +58,16 @@ def uds_link(request: 'HttpRequest', ticket: str, scrambler: str) -> str:
|
||||
|
||||
def uds_access_link(
|
||||
request: 'HttpRequest', # pylint: disable=unused-argument
|
||||
serviceId: str,
|
||||
transportId: typing.Optional[str],
|
||||
service_id: str,
|
||||
transport_id: typing.Optional[str],
|
||||
) -> str:
|
||||
'''
|
||||
If transportId (uuid) is None, this will be a metaLink
|
||||
'''
|
||||
return f'{consts.system.UDS_ACTION_SCHEME}{serviceId}/{transportId or "meta"}'
|
||||
return f'{consts.system.UDS_ACTION_SCHEME}{service_id}/{transport_id or "meta"}'
|
||||
|
||||
|
||||
def parse_date(dateToParse) -> datetime.date:
|
||||
def parse_date(string_date: str) -> datetime.date:
|
||||
if get_language() == 'fr':
|
||||
date_format = '%d/%m/%Y'
|
||||
else:
|
||||
@ -75,28 +75,15 @@ def parse_date(dateToParse) -> datetime.date:
|
||||
formats.get_format('SHORT_DATE_FORMAT').replace('Y', '%Y').replace('m', '%m').replace('d', '%d')
|
||||
) # pylint: disable=maybe-no-member
|
||||
|
||||
return datetime.datetime.strptime(dateToParse, date_format).date()
|
||||
return datetime.datetime.strptime(string_date, date_format).date()
|
||||
|
||||
|
||||
def date_to_literal(date) -> str:
|
||||
def date_to_literal(date: datetime.datetime) -> str:
|
||||
# Fix for FR lang for datepicker
|
||||
if get_language() == 'fr':
|
||||
date = date.strftime('%d/%m/%Y')
|
||||
d = date.strftime('%d/%m/%Y')
|
||||
else:
|
||||
date = formats.date_format(date, 'SHORT_DATE_FORMAT')
|
||||
d = formats.date_format(date, 'SHORT_DATE_FORMAT')
|
||||
|
||||
return date
|
||||
return d
|
||||
|
||||
|
||||
def extract_key(
|
||||
dictionary: dict, key: typing.Any, fmt: typing.Optional[str] = None, default: typing.Any = None
|
||||
):
|
||||
fmt = fmt or '{0}'
|
||||
default = default or ''
|
||||
|
||||
if key in dictionary:
|
||||
value = fmt.format(dictionary[key])
|
||||
del dictionary[key]
|
||||
else:
|
||||
value = default
|
||||
return value
|
||||
|
@ -36,6 +36,8 @@ import logging
|
||||
import typing
|
||||
import collections.abc
|
||||
|
||||
from numpy import mat
|
||||
|
||||
from uds.core import types, consts
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -47,7 +49,7 @@ def detect_os(
|
||||
"""
|
||||
Basic OS Client detector (very basic indeed :-))
|
||||
"""
|
||||
ua = (headers.get('User-Agent') or types.os.KnownOS.UNKNOWN.value[0])
|
||||
ua = headers.get('User-Agent') or types.os.KnownOS.UNKNOWN.value[0]
|
||||
|
||||
res = types.os.DetectedOsInfo(
|
||||
os=types.os.KnownOS.UNKNOWN, browser=types.os.KnownBrowser.OTHER, version='0.0'
|
||||
@ -87,16 +89,16 @@ def detect_os(
|
||||
# Try to detect browser from User-Agent
|
||||
match = None
|
||||
|
||||
ruleKey, ruleValue = None, None
|
||||
for ruleKey, ruleValue in consts.os.BROWSER_RULES.items():
|
||||
must, mustNot = ruleValue
|
||||
browser_type = None
|
||||
for browser_type, rules in consts.os.BROWSER_RULES.items():
|
||||
must, must_not = rules
|
||||
|
||||
for mustRe in consts.os.BROWSERS_RE[must]:
|
||||
match = mustRe.search(ua)
|
||||
for must_re in consts.os.BROWSERS_RE[must]:
|
||||
match = must_re.search(ua)
|
||||
if match is None:
|
||||
continue
|
||||
# Check against no maching rules
|
||||
for mustNotREs in mustNot:
|
||||
for mustNotREs in must_not:
|
||||
for cre in consts.os.BROWSERS_RE[mustNotREs]:
|
||||
if cre.search(ua) is not None:
|
||||
match = None
|
||||
@ -109,8 +111,8 @@ def detect_os(
|
||||
break
|
||||
|
||||
if match is not None:
|
||||
res.browser = ruleKey or types.os.KnownBrowser.OTHER
|
||||
res.version = match.groups(1)[0]
|
||||
res.browser = browser_type or types.os.KnownBrowser.OTHER
|
||||
res.version = '0.0'
|
||||
|
||||
logger.debug('Detected: %s %s', res.os, res.browser)
|
||||
|
||||
|
@ -91,7 +91,7 @@ def add_user_permission(
|
||||
user: 'models.User',
|
||||
obj: 'Model',
|
||||
permission: PermissionType = PermissionType.READ,
|
||||
):
|
||||
) -> None:
|
||||
# Some permissions added to some object types needs at least READ_PERMISSION on parent
|
||||
models.Permissions.add_permission(
|
||||
user=user,
|
||||
@ -105,7 +105,7 @@ def add_group_permission(
|
||||
group: 'models.Group',
|
||||
obj: 'Model',
|
||||
permission: PermissionType = PermissionType.READ,
|
||||
):
|
||||
) -> None:
|
||||
models.Permissions.add_permission(
|
||||
group=group,
|
||||
object_type=objtype.ObjectType.from_model(obj),
|
||||
@ -119,7 +119,7 @@ def has_access(
|
||||
obj: 'Model',
|
||||
permission: PermissionType = PermissionType.ALL,
|
||||
for_type: bool = False,
|
||||
):
|
||||
) -> bool:
|
||||
return effective_permissions(user, obj, for_type).contains(permission)
|
||||
|
||||
|
||||
|
@ -54,27 +54,27 @@ CounterClass = typing.TypeVar('CounterClass', Provider, Service, ServicePool, Au
|
||||
|
||||
|
||||
# Helpers
|
||||
def _get_id(obj):
|
||||
def _get_id(obj: 'CounterClass') -> typing.Optional[int]:
|
||||
return obj.id if obj.id != -1 else None
|
||||
|
||||
|
||||
def _get_prov_serv_ids(provider) -> tuple:
|
||||
def _get_prov_serv_ids(provider: 'Provider') -> tuple[int, ...]:
|
||||
return tuple(i.id for i in provider.services.all())
|
||||
|
||||
|
||||
def _get_serv_pool_ids(service) -> tuple:
|
||||
def _get_serv_pool_ids(service: 'Service') -> tuple[int, ...]:
|
||||
return tuple(i.id for i in service.deployedServices.all())
|
||||
|
||||
|
||||
def _get_prov_serv_pool_ids(provider) -> tuple:
|
||||
res: tuple = ()
|
||||
def _get_prov_serv_pool_ids(provider: 'Provider') -> tuple[int, ...]:
|
||||
res: tuple[int, ...] = ()
|
||||
for i in provider.services.all():
|
||||
res += _get_serv_pool_ids(i)
|
||||
return res
|
||||
|
||||
|
||||
_id_retriever: typing.Final[
|
||||
collections.abc.Mapping[type[Model], collections.abc.Mapping[int, collections.abc.Callable]]
|
||||
collections.abc.Mapping[type[Model], collections.abc.Mapping[int, collections.abc.Callable[[typing.Any], typing.Any]]]
|
||||
] = {
|
||||
Provider: {
|
||||
types.stats.CounterType.LOAD: _get_id,
|
||||
@ -149,7 +149,7 @@ def add_counter(
|
||||
|
||||
|
||||
def enumerate_counters(
|
||||
obj: CounterClass, counterType: types.stats.CounterType, **kwargs
|
||||
obj: CounterClass, counterType: types.stats.CounterType, **kwargs: typing.Any
|
||||
) -> typing.Generator[tuple[datetime.datetime, int], None, None]:
|
||||
"""
|
||||
Get counters
|
||||
@ -184,7 +184,7 @@ def enumerate_counters(
|
||||
return
|
||||
|
||||
if not kwargs.get('all', False):
|
||||
owner_ids = fnc(obj)
|
||||
owner_ids = fnc(obj) # pyright: ignore
|
||||
else:
|
||||
owner_ids = None
|
||||
|
||||
|
@ -126,6 +126,7 @@ def get_owner(
|
||||
return OSManager.objects.get(pk=ownerId)
|
||||
return None
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class EventTupleType:
|
||||
stamp: datetime.datetime
|
||||
@ -136,7 +137,7 @@ class EventTupleType:
|
||||
event_type: types.stats.EventType
|
||||
|
||||
# aliases for fields
|
||||
def __getitem__(self, item) -> typing.Any:
|
||||
def __getitem__(self, item: str) -> typing.Any:
|
||||
if item in _REVERSE_FLDS_EQUIV:
|
||||
item = _REVERSE_FLDS_EQUIV[item]
|
||||
return self.__getattribute__(item)
|
||||
@ -150,7 +151,7 @@ class EventTupleType:
|
||||
EventClass = typing.Union[Provider, Service, ServicePool, Authenticator]
|
||||
|
||||
|
||||
def add_event(obj: EventClass, eventType: types.stats.EventType, **kwargs) -> bool:
|
||||
def add_event(obj: EventClass, eventType: types.stats.EventType, **kwargs: typing.Any) -> bool:
|
||||
"""
|
||||
Adds a event stat to specified object
|
||||
|
||||
@ -164,7 +165,9 @@ def add_event(obj: EventClass, eventType: types.stats.EventType, **kwargs) -> bo
|
||||
return StatsManager.manager().add_event(_OWNER_FROM_MODEL[type(obj)], obj.id, eventType, **kwargs)
|
||||
|
||||
|
||||
def get_events(obj: EventClass, eventType: types.stats.EventType, **kwargs) -> typing.Generator[EventTupleType, None, None]:
|
||||
def get_events(
|
||||
obj: EventClass, eventType: types.stats.EventType, **kwargs: typing.Any
|
||||
) -> typing.Generator[EventTupleType, None, None]:
|
||||
"""
|
||||
Get events
|
||||
|
||||
|
@ -72,7 +72,7 @@ def _decode_value(dbk: str, value: typing.Optional[str]) -> tuple[str, typing.An
|
||||
if isinstance(v, tuple) and v[0] == MARK:
|
||||
return typing.cast(tuple[str, typing.Any], v[1:])
|
||||
# Fix value so it contains also the "key" (in this case, the original key is lost, we have only the hash value...)
|
||||
return ('#' + dbk, v)
|
||||
return ('#' + dbk, typing.cast(typing.Any, v))
|
||||
except Exception:
|
||||
try:
|
||||
return ('#' + dbk, base64.b64decode(value.encode()).decode())
|
||||
@ -110,7 +110,7 @@ class StorageAsDict(MutableMapping[str, typing.Any]):
|
||||
self._atomic = atomic # Not used right now, maybe removed
|
||||
|
||||
@property
|
||||
def _db(self) -> typing.Union[models.QuerySet, models.Manager]:
|
||||
def _db(self) -> typing.Union[models.QuerySet[DBStorage], models.Manager[DBStorage]]:
|
||||
if self._atomic:
|
||||
return DBStorage.objects.select_for_update()
|
||||
return DBStorage.objects
|
||||
@ -131,7 +131,7 @@ class StorageAsDict(MutableMapping[str, typing.Any]):
|
||||
return _old_calculate_key(self._owner.encode(), key.encode())
|
||||
|
||||
def __getitem__(self, key: str) -> typing.Any:
|
||||
if not isinstance(key, str):
|
||||
if not isinstance(key, str): # pyright: ignore reportUnnecessaryIsInstance
|
||||
raise TypeError(f'Key must be str, {type(key)} found')
|
||||
|
||||
# First, try new key, and, if needed, old key
|
||||
@ -139,11 +139,11 @@ class StorageAsDict(MutableMapping[str, typing.Any]):
|
||||
for use_old_method in (False, True):
|
||||
db_key = self._key(key, old_method=use_old_method)
|
||||
try:
|
||||
c: DBStorage = typing.cast(DBStorage, self._db.get(pk=db_key))
|
||||
c: DBStorage = self._db.get(pk=db_key)
|
||||
if c.owner != self._owner: # Maybe a key collision,
|
||||
logger.error('Key collision detected for key %s', key)
|
||||
return None
|
||||
okey, value = _decode_value(db_key, c.data)
|
||||
_, value = _decode_value(db_key, c.data)
|
||||
if use_old_method:
|
||||
# Update key on db
|
||||
c.delete()
|
||||
@ -154,7 +154,7 @@ class StorageAsDict(MutableMapping[str, typing.Any]):
|
||||
return None
|
||||
|
||||
def __setitem__(self, key: str, value: typing.Any) -> None:
|
||||
if not isinstance(key, str):
|
||||
if not isinstance(key, str): # pyright: ignore reportUnnecessaryIsInstance
|
||||
raise TypeError(f'Key must be str type, {type(key)} found')
|
||||
|
||||
dbk = self._key(key)
|
||||
@ -246,7 +246,7 @@ class Storage:
|
||||
if isinstance(owner, bytes):
|
||||
self._owner = owner.decode('utf-8')
|
||||
self._bowner = owner
|
||||
elif isinstance(owner, str):
|
||||
elif isinstance(owner, str): # pyright: ignore reportUnnecessaryIsInstance Wants to ensure that it is a string no runtime error
|
||||
self._owner = owner
|
||||
self._bowner = owner.encode('utf8')
|
||||
else:
|
||||
@ -356,8 +356,8 @@ class Storage:
|
||||
keys: 'collections.abc.Iterable[str|bytes]'
|
||||
if isinstance(skey, (str, bytes)):
|
||||
keys = [skey]
|
||||
elif isinstance(skey, collections.abc.Iterable):
|
||||
keys = typing.cast('collections.abc.Iterable[str|bytes]', skey)
|
||||
else:
|
||||
keys = skey # typing.cast('collections.abc.Iterable[str|bytes]', skey)
|
||||
|
||||
try:
|
||||
# Process several keys at once
|
||||
|
@ -118,14 +118,14 @@ class ServiceCacheUpdater(Job):
|
||||
friendly_name = 'Service Cache Updater'
|
||||
|
||||
@staticmethod
|
||||
def _notify_restrain(servicePool) -> None:
|
||||
def _notify_restrain(servicepool: 'ServicePool') -> None:
|
||||
log.log(
|
||||
servicePool,
|
||||
servicepool,
|
||||
log.LogLevel.WARNING,
|
||||
'Service Pool is restrained due to excesive errors',
|
||||
log.LogSource.INTERNAL,
|
||||
)
|
||||
logger.info('%s is restrained, will check this later', servicePool.name)
|
||||
logger.info('%s is restrained, will check this later', servicepool.name)
|
||||
|
||||
def service_pools_needing_cache_update(
|
||||
self,
|
||||
@ -148,7 +148,7 @@ class ServiceCacheUpdater(Job):
|
||||
for servicepool in candidate_servicepools:
|
||||
servicepool.user_services.update() # Cleans cached queries
|
||||
# If this deployedService don't have a publication active and needs it, ignore it
|
||||
service_instance = servicepool.service.get_instance() # type: ignore
|
||||
service_instance = servicepool.service.get_instance()
|
||||
|
||||
if service_instance.uses_cache is False:
|
||||
logger.debug(
|
||||
@ -200,7 +200,7 @@ class ServiceCacheUpdater(Job):
|
||||
)
|
||||
assigned_count: int = (
|
||||
servicepool.assigned_user_services()
|
||||
.filter(UserServiceManager().get_state_filter(servicepool.service)) # type: ignore
|
||||
.filter(UserServiceManager().get_state_filter(servicepool.service))
|
||||
.count()
|
||||
)
|
||||
pool_stat = ServicePoolStats(servicepool, l1_cache_count, l2_cache_count, assigned_count)
|
||||
@ -212,7 +212,6 @@ class ServiceCacheUpdater(Job):
|
||||
l2_cache_count,
|
||||
assigned_count,
|
||||
)
|
||||
l1_assigned_count = l1_cache_count + assigned_count
|
||||
|
||||
# We have more than we want
|
||||
if pool_stat.l1_cache_overflow():
|
||||
@ -390,7 +389,7 @@ class ServiceCacheUpdater(Job):
|
||||
def reduce_l2_cache(
|
||||
self,
|
||||
servicepool_stats: ServicePoolStats,
|
||||
):
|
||||
) -> None:
|
||||
logger.debug("Reducing L2 cache erasing a service in cache for %s", servicepool_stats.servicepool.name)
|
||||
if servicepool_stats.l2_cache_count > 0:
|
||||
cacheItems = (
|
||||
|
@ -142,7 +142,7 @@ class StatsCleaner(Job):
|
||||
frecuency = 3600 * 24 * 15 # Ejecuted just once every 15 days
|
||||
friendly_name = 'Statistic housekeeping'
|
||||
|
||||
def run(self):
|
||||
def run(self) -> None:
|
||||
logger.debug('Starting statistics cleanup')
|
||||
try:
|
||||
StatsManager.manager().perform_counters_maintenance()
|
||||
@ -169,7 +169,7 @@ class StatsAccumulator(Job):
|
||||
)
|
||||
friendly_name = 'Statistics acummulator'
|
||||
|
||||
def run(self):
|
||||
def run(self) -> None:
|
||||
try:
|
||||
StatsManager.manager().acummulate(config.GlobalConfig.STATS_ACCUM_MAX_CHUNK_TIME.as_int())
|
||||
except Exception:
|
||||
|
@ -86,7 +86,7 @@ class ServerGroup(UUIDModel, TaggingMixin, properties.PropertiesMixin):
|
||||
def get_owner_id_and_type(self) -> tuple[str, str]:
|
||||
return self.uuid, 'servergroup'
|
||||
|
||||
class Meta:
|
||||
class Meta: # pyright: ignore
|
||||
# Unique for host and port, so we can have only one group for each host:port
|
||||
app_label = 'uds'
|
||||
|
||||
@ -194,12 +194,12 @@ class Server(UUIDModel, TaggingMixin, properties.PropertiesMixin):
|
||||
|
||||
# Group (of registered servers) this server belongs to
|
||||
# Note that only Tunnel servers can belong to more than one servergroup
|
||||
groups = models.ManyToManyField(
|
||||
groups: 'models.ManyToManyField[ServerGroup, Server]' = models.ManyToManyField(
|
||||
ServerGroup,
|
||||
related_name='servers',
|
||||
)
|
||||
|
||||
class Meta: # pylint: disable=too-few-public-methods
|
||||
class Meta: # pyright: ignore
|
||||
app_label = 'uds'
|
||||
|
||||
# For properties
|
||||
|
@ -56,9 +56,16 @@ from .uuid_model import UUIDModel
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds.models import (CalendarAccess, CalendarAction, Group,
|
||||
MetaPoolMember, ServicePoolPublication,
|
||||
ServicePoolPublicationChangelog, User, UserService)
|
||||
from uds.models import (
|
||||
CalendarAccess,
|
||||
CalendarAction,
|
||||
Group,
|
||||
MetaPoolMember,
|
||||
ServicePoolPublication,
|
||||
ServicePoolPublicationChangelog,
|
||||
User,
|
||||
UserService,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -84,8 +91,12 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
related_name='deployedServices',
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
transports = models.ManyToManyField(Transport, related_name='deployedServices', db_table='uds__ds_trans')
|
||||
assignedGroups = models.ManyToManyField(Group, related_name='deployedServices', db_table='uds__ds_grps')
|
||||
transports: 'models.ManyToManyField[Transport, ServicePool]' = models.ManyToManyField(
|
||||
Transport, related_name='deployedServices', db_table='uds__ds_trans'
|
||||
)
|
||||
assignedGroups: 'models.ManyToManyField[Group, ServicePool]' = models.ManyToManyField(
|
||||
Group, related_name='deployedServices', db_table='uds__ds_grps'
|
||||
)
|
||||
state = models.CharField(max_length=1, default=types.states.State.ACTIVE, db_index=True)
|
||||
state_date = models.DateTimeField(default=consts.NEVER)
|
||||
show_transports = models.BooleanField(default=True)
|
||||
@ -132,7 +143,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
current_pub_revision = models.PositiveIntegerField(default=1)
|
||||
|
||||
# "fake" declarations for type checking
|
||||
objects: 'models.manager.Manager["ServicePool"]'
|
||||
# objects: 'models.manager.Manager["ServicePool"]'
|
||||
publications: 'models.manager.RelatedManager[ServicePoolPublication]'
|
||||
memberOfMeta: 'models.manager.RelatedManager[MetaPoolMember]'
|
||||
userServices: 'models.manager.RelatedManager[UserService]'
|
||||
@ -153,7 +164,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
def calendar_access(self) -> 'models.manager.RelatedManager[CalendarAccess]':
|
||||
return self.calendarAccess
|
||||
|
||||
class Meta(UUIDModel.Meta): # pylint: disable=too-few-public-methods
|
||||
class Meta(UUIDModel.Meta): # pyright: ignore
|
||||
"""
|
||||
Meta class to declare the name of the table at database
|
||||
"""
|
||||
@ -199,13 +210,10 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
|
||||
@staticmethod
|
||||
def restraineds_queryset() -> 'models.QuerySet[ServicePool]':
|
||||
from django.db.models import \
|
||||
Count # pylint: disable=import-outside-toplevel
|
||||
from django.db.models import Count # pylint: disable=import-outside-toplevel
|
||||
|
||||
from uds.core.util.config import \
|
||||
GlobalConfig # pylint: disable=import-outside-toplevel
|
||||
from uds.models.user_service import \
|
||||
UserService # pylint: disable=import-outside-toplevel
|
||||
from uds.core.util.config import GlobalConfig # pylint: disable=import-outside-toplevel
|
||||
from uds.models.user_service import UserService # pylint: disable=import-outside-toplevel
|
||||
|
||||
if GlobalConfig.RESTRAINT_TIME.as_int() <= 0:
|
||||
return (
|
||||
@ -215,12 +223,13 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
date = sql_datetime() - timedelta(seconds=GlobalConfig.RESTRAINT_TIME.as_int())
|
||||
min_ = GlobalConfig.RESTRAINT_COUNT.as_int()
|
||||
|
||||
res = []
|
||||
for v in (
|
||||
res: list[dict[str, typing.Any]] = []
|
||||
for v in typing.cast(
|
||||
list[dict[str, typing.Any]],
|
||||
UserService.objects.filter(state=types.states.State.ERROR, state_date__gt=date)
|
||||
.values('deployed_service')
|
||||
.annotate(how_many=Count('deployed_service'))
|
||||
.order_by('deployed_service')
|
||||
.order_by('deployed_service'),
|
||||
):
|
||||
if v['how_many'] >= min_:
|
||||
res.append(v['deployed_service'])
|
||||
@ -236,7 +245,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
|
||||
@property
|
||||
def visual_name(self) -> str:
|
||||
logger.debug("SHORT: %s %s %s", self.short_name, self.short_name is not None, self.name)
|
||||
logger.debug("SHORT: %s %s %s", self.short_name, self.short_name != '', self.name)
|
||||
if self.short_name and str(self.short_name).strip():
|
||||
return str(self.short_name.strip())
|
||||
return str(self.name)
|
||||
@ -255,13 +264,12 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
The time that a service is in restrain mode is 20 minutes by default (1200 secs), but it can be modified
|
||||
at globalconfig variables
|
||||
"""
|
||||
from uds.core.util.config import \
|
||||
GlobalConfig # pylint: disable=import-outside-toplevel
|
||||
from uds.core.util.config import GlobalConfig # pylint: disable=import-outside-toplevel
|
||||
|
||||
if GlobalConfig.RESTRAINT_TIME.as_int() <= 0:
|
||||
return False # Do not perform any restraint check if we set the globalconfig to 0 (or less)
|
||||
|
||||
date = typing.cast(datetime, sql_datetime()) - timedelta(seconds=GlobalConfig.RESTRAINT_TIME.as_int())
|
||||
date = sql_datetime() - timedelta(seconds=GlobalConfig.RESTRAINT_TIME.as_int())
|
||||
if (
|
||||
self.userServices.filter(state=types.states.State.ERROR, state_date__gt=date).count()
|
||||
>= GlobalConfig.RESTRAINT_COUNT.as_int()
|
||||
@ -312,23 +320,23 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
|
||||
return None
|
||||
|
||||
def is_access_allowed(self, chkDateTime=None) -> bool:
|
||||
def is_access_allowed(self, check_datetime: typing.Optional[datetime] = None) -> bool:
|
||||
"""
|
||||
Checks if the access for a service pool is allowed or not (based esclusively on associated calendars)
|
||||
"""
|
||||
if chkDateTime is None:
|
||||
chkDateTime = sql_datetime()
|
||||
if check_datetime is None:
|
||||
check_datetime = sql_datetime()
|
||||
|
||||
access = self.fallbackAccess
|
||||
# Let's see if we can access by current datetime
|
||||
for ac in sorted(self.calendarAccess.all(), key=operator.attrgetter('priority')):
|
||||
if calendar.CalendarChecker(ac.calendar).check(chkDateTime):
|
||||
if calendar.CalendarChecker(ac.calendar).check(check_datetime):
|
||||
access = ac.access
|
||||
break # Stops on first rule match found
|
||||
|
||||
return access == types.states.State.ALLOW
|
||||
|
||||
def get_deadline(self, chkDateTime: typing.Optional[datetime] = None) -> typing.Optional[int]:
|
||||
def get_deadline(self, check_datetime: typing.Optional[datetime] = None) -> typing.Optional[int]:
|
||||
"""Gets the deadline for an access on chkDateTime in seconds
|
||||
|
||||
Keyword Arguments:
|
||||
@ -337,30 +345,30 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
Returns:
|
||||
typing.Optional[int] -- [Returns deadline in secods. If no deadline (forever), will return None]
|
||||
"""
|
||||
if chkDateTime is None:
|
||||
chkDateTime = typing.cast(datetime, sql_datetime())
|
||||
if check_datetime is None:
|
||||
check_datetime = sql_datetime()
|
||||
|
||||
if self.is_access_allowed(chkDateTime) is False:
|
||||
if self.is_access_allowed(check_datetime) is False:
|
||||
return -1
|
||||
|
||||
deadLine = None
|
||||
deadline = None
|
||||
|
||||
for ac in self.calendarAccess.all():
|
||||
if ac.access == types.states.State.ALLOW and self.fallbackAccess == types.states.State.DENY:
|
||||
nextE = calendar.CalendarChecker(ac.calendar).next_event(chkDateTime, False)
|
||||
if not deadLine or (nextE and deadLine > nextE):
|
||||
deadLine = nextE
|
||||
nextE = calendar.CalendarChecker(ac.calendar).next_event(check_datetime, False)
|
||||
if not deadline or (nextE and deadline > nextE):
|
||||
deadline = nextE
|
||||
elif ac.access == types.states.State.DENY: # DENY
|
||||
nextE = calendar.CalendarChecker(ac.calendar).next_event(chkDateTime, True)
|
||||
if not deadLine or (nextE and deadLine > nextE):
|
||||
deadLine = nextE
|
||||
nextE = calendar.CalendarChecker(ac.calendar).next_event(check_datetime, True)
|
||||
if not deadline or (nextE and deadline > nextE):
|
||||
deadline = nextE
|
||||
|
||||
if deadLine is None:
|
||||
if deadline is None:
|
||||
if self.fallbackAccess == types.states.State.ALLOW:
|
||||
return None
|
||||
return -1
|
||||
|
||||
return int((deadLine - chkDateTime).total_seconds())
|
||||
return int((deadline - check_datetime).total_seconds())
|
||||
|
||||
def set_value(self, name: str, value: typing.Any):
|
||||
"""
|
||||
@ -480,7 +488,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
):
|
||||
raise InvalidServiceException()
|
||||
|
||||
def validate_transport(self, transport) -> None:
|
||||
def validate_transport(self, transport: 'Transport') -> None:
|
||||
if (
|
||||
self.transports.filter(id=transport.id).count() # pylint: disable=no-member
|
||||
== 0 # pylint: disable=no-member
|
||||
@ -520,8 +528,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
Returns:
|
||||
List of accesible deployed services
|
||||
"""
|
||||
from uds.core import \
|
||||
services # pylint: disable=import-outside-toplevel
|
||||
from uds.core import services # pylint: disable=import-outside-toplevel
|
||||
|
||||
servicesNotNeedingPub = [t.get_type() for t in services.factory().services_not_needing_publication()]
|
||||
# Get services that HAS publications
|
||||
@ -590,8 +597,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
|
||||
No check is done, it simply redirects the request to PublicationManager, where checks are done.
|
||||
"""
|
||||
from uds.core.managers import \
|
||||
publication_manager # pylint: disable=import-outside-toplevel
|
||||
from uds.core.managers import publication_manager # pylint: disable=import-outside-toplevel
|
||||
|
||||
publication_manager().publish(self, changeLog)
|
||||
|
||||
@ -634,25 +640,25 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
"""
|
||||
return self.userServices.filter(cache_level=0, user=None)
|
||||
|
||||
def usage(self, cachedValue=-1) -> types.pools.UsageInfo:
|
||||
def usage(self, cached_value: int = -1) -> types.pools.UsageInfo:
|
||||
"""
|
||||
Returns the % used services, then count and the max related to "maximum" user services
|
||||
If no "maximum" number of services, will return 0% ofc
|
||||
cachedValue is used to optimize (if known the number of assigned services, we can avoid to query the db)
|
||||
"""
|
||||
maxs = self.max_srvs
|
||||
if maxs == 0 and self.service:
|
||||
if maxs == 0:
|
||||
maxs = self.service.get_instance().userservices_limit
|
||||
|
||||
if cachedValue == -1:
|
||||
cachedValue = (
|
||||
if cached_value == -1:
|
||||
cached_value = (
|
||||
self.assigned_user_services().filter(state__in=types.states.State.VALID_STATES).count()
|
||||
)
|
||||
|
||||
if maxs == 0 or max == consts.UNLIMITED:
|
||||
return types.pools.UsageInfo(cachedValue, consts.UNLIMITED)
|
||||
if maxs == 0 or maxs == consts.UNLIMITED:
|
||||
return types.pools.UsageInfo(cached_value, consts.UNLIMITED)
|
||||
|
||||
return types.pools.UsageInfo(cachedValue, maxs)
|
||||
return types.pools.UsageInfo(cached_value, maxs)
|
||||
|
||||
def test_connectivity(self, host: str, port: typing.Union[str, int], timeout: float = 4) -> bool:
|
||||
return bool(self.service) and self.service.test_connectivity(host, port, timeout)
|
||||
@ -662,7 +668,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
log.log(self, level, message, log.LogSource.INTERNAL)
|
||||
|
||||
@staticmethod
|
||||
def pre_delete(sender, **kwargs) -> None: # pylint: disable=unused-argument
|
||||
def pre_delete(sender: typing.Any, **kwargs: typing.Any) -> None: # pylint: disable=unused-argument
|
||||
"""
|
||||
Used to invoke the Service class "Destroy" before deleting it from database.
|
||||
|
||||
@ -671,19 +677,18 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
|
||||
:note: If destroy raises an exception, the deletion is not taken.
|
||||
"""
|
||||
from uds.core.util.permissions import \
|
||||
clean # pylint: disable=import-outside-toplevel
|
||||
from uds.core.util.permissions import clean # pylint: disable=import-outside-toplevel
|
||||
|
||||
toDelete: 'ServicePool' = kwargs['instance']
|
||||
to_delete: 'ServicePool' = kwargs['instance']
|
||||
|
||||
logger.debug('Deleting Service Pool %s', toDelete)
|
||||
toDelete.get_environment().clean_related_data()
|
||||
logger.debug('Deleting Service Pool %s', to_delete)
|
||||
to_delete.get_environment().clean_related_data()
|
||||
|
||||
# Clears related logs
|
||||
log.clear_logs(toDelete)
|
||||
log.clear_logs(to_delete)
|
||||
|
||||
# Clears related permissions
|
||||
clean(toDelete)
|
||||
clean(to_delete)
|
||||
|
||||
# returns CSV header
|
||||
@staticmethod
|
||||
|
@ -38,7 +38,6 @@ import typing
|
||||
|
||||
from uds.core import consts, services, types
|
||||
from uds.core.managers.userservice import UserServiceManager
|
||||
from uds.core.types.states import State
|
||||
from uds.core.util import autoserializable, log
|
||||
|
||||
from .jobs import OVirtDeferredRemoval
|
||||
@ -193,13 +192,13 @@ class OVirtLinkedDeployment(services.UserService, autoserializable.AutoSerializa
|
||||
"""
|
||||
return self._ip
|
||||
|
||||
def set_ready(self) -> str:
|
||||
def set_ready(self) -> types.states.State:
|
||||
"""
|
||||
The method is invoked whenever a machine is provided to an user, right
|
||||
before presenting it (via transport rendering) to the user.
|
||||
"""
|
||||
if self.cache.get('ready') == '1':
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
try:
|
||||
state = self.service().get_machine_state(self._vmid)
|
||||
@ -216,7 +215,7 @@ class OVirtLinkedDeployment(services.UserService, autoserializable.AutoSerializa
|
||||
self.do_log(log.LogLevel.ERROR, f'Error on setReady: {e}')
|
||||
# Treat as operation done, maybe the machine is ready and we can continue
|
||||
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def reset(self) -> None:
|
||||
"""
|
||||
@ -247,7 +246,7 @@ if sys.platform == 'win32':
|
||||
if dbUserService:
|
||||
UserServiceManager().send_script(dbUserService, script)
|
||||
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> str:
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> types.states.State:
|
||||
# Here we will check for suspending the VM (when full ready)
|
||||
logger.debug('Checking if cache 2 for %s', self._name)
|
||||
if self._get_current_op() == Operation.WAIT:
|
||||
@ -255,9 +254,9 @@ if sys.platform == 'win32':
|
||||
self._pop_current_op() # Remove current state
|
||||
return self._execute_queue()
|
||||
# Do not need to go to level 2 (opWait is in fact "waiting for moving machine to cache level 2)
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def deploy_for_user(self, user: 'models.User') -> str:
|
||||
def deploy_for_user(self, user: 'models.User') -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for an user.
|
||||
"""
|
||||
@ -265,15 +264,15 @@ if sys.platform == 'win32':
|
||||
self._init_queue_for_deploy(False)
|
||||
return self._execute_queue()
|
||||
|
||||
def deploy_for_cache(self, cacheLevel: int) -> str:
|
||||
def deploy_for_cache(self, level: int) -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for cache
|
||||
"""
|
||||
self._init_queue_for_deploy(cacheLevel == self.L2_CACHE)
|
||||
self._init_queue_for_deploy(level == self.L2_CACHE)
|
||||
return self._execute_queue()
|
||||
|
||||
def _init_queue_for_deploy(self, forLevel2: bool = False) -> None:
|
||||
if forLevel2 is False:
|
||||
def _init_queue_for_deploy(self, for_level_2: bool = False) -> None:
|
||||
if for_level_2 is False:
|
||||
self._queue = [Operation.CREATE, Operation.CHANGEMAC, Operation.START, Operation.FINISH]
|
||||
else:
|
||||
self._queue = [
|
||||
@ -285,7 +284,7 @@ if sys.platform == 'win32':
|
||||
Operation.FINISH,
|
||||
]
|
||||
|
||||
def _check_machine_state(self, check_state: collections.abc.Iterable[str]) -> str:
|
||||
def _check_machine_state(self, check_state: collections.abc.Iterable[str]) -> types.states.State:
|
||||
logger.debug(
|
||||
'Checking that state of machine %s (%s) is %s',
|
||||
self._vmid,
|
||||
@ -298,15 +297,15 @@ if sys.platform == 'win32':
|
||||
if state == 'unknown' and check_state != 'unknown':
|
||||
return self._error('Machine not found')
|
||||
|
||||
ret = State.RUNNING
|
||||
ret = types.states.State.RUNNING
|
||||
if isinstance(check_state, (list, tuple)):
|
||||
for cks in check_state:
|
||||
if state == cks:
|
||||
ret = State.FINISHED
|
||||
ret = types.states.State.FINISHED
|
||||
break
|
||||
else:
|
||||
if state == check_state:
|
||||
ret = State.FINISHED
|
||||
ret = types.states.State.FINISHED
|
||||
|
||||
return ret
|
||||
|
||||
@ -325,12 +324,12 @@ if sys.platform == 'win32':
|
||||
def _push_front_op(self, op: Operation) -> None:
|
||||
self._queue.insert(0, op)
|
||||
|
||||
def _error(self, reason: typing.Union[str, Exception]) -> str:
|
||||
def _error(self, reason: typing.Union[str, Exception]) -> types.states.State:
|
||||
"""
|
||||
Internal method to set object as error state
|
||||
|
||||
Returns:
|
||||
State.ERROR, so we can do "return self.__error(reason)"
|
||||
types.states.State.ERROR, so we can do "return self.__error(reason)"
|
||||
"""
|
||||
reason = str(reason)
|
||||
logger.debug('Setting error state, reason: %s', reason)
|
||||
@ -341,17 +340,17 @@ if sys.platform == 'win32':
|
||||
|
||||
self._queue = [Operation.ERROR]
|
||||
self._reason = reason
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
def _execute_queue(self) -> str:
|
||||
def _execute_queue(self) -> types.states.State:
|
||||
self._debug('executeQueue')
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: dict[Operation, typing.Optional[collections.abc.Callable[[], str]]] = {
|
||||
Operation.CREATE: self._create,
|
||||
@ -372,26 +371,26 @@ if sys.platform == 'win32':
|
||||
|
||||
operation_runner()
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
except Exception as e:
|
||||
return self._error(e)
|
||||
|
||||
# Queue execution methods
|
||||
def _retry(self) -> str:
|
||||
def _retry(self) -> types.states.State:
|
||||
"""
|
||||
Used to retry an operation
|
||||
In fact, this will not be never invoked, unless we push it twice, because
|
||||
check_state method will "pop" first item when a check operation returns State.FINISHED
|
||||
check_state method will "pop" first item when a check operation returns types.states.State.FINISHED
|
||||
|
||||
At executeQueue this return value will be ignored, and it will only be used at check_state
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _wait(self) -> str:
|
||||
def _wait(self) -> types.states.State:
|
||||
"""
|
||||
Executes opWait, it simply waits something "external" to end
|
||||
"""
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _create(self) -> str:
|
||||
"""
|
||||
@ -410,10 +409,10 @@ if sys.platform == 'win32':
|
||||
comments = 'UDS Linked clone'
|
||||
|
||||
self._vmid = self.service().deploy_from_template(name, comments, template_id)
|
||||
if self._vmid is None:
|
||||
if not self._vmid:
|
||||
raise Exception('Can\'t create machine')
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _remove(self) -> str:
|
||||
"""
|
||||
@ -430,7 +429,7 @@ if sys.platform == 'win32':
|
||||
else:
|
||||
self.service().remove_machine(self._vmid)
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _start_machine(self) -> str:
|
||||
"""
|
||||
@ -442,7 +441,7 @@ if sys.platform == 'win32':
|
||||
raise Exception('Machine not found')
|
||||
|
||||
if state in UP_STATES: # Already started, return
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
if state not in ('down', 'suspended'):
|
||||
self._push_front_op(
|
||||
@ -450,7 +449,7 @@ if sys.platform == 'win32':
|
||||
) # Will call "check Retry", that will finish inmediatly and again call this one
|
||||
self.service().start_machine(self._vmid)
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _stop_machine(self) -> str:
|
||||
"""
|
||||
@ -462,7 +461,7 @@ if sys.platform == 'win32':
|
||||
raise Exception('Machine not found')
|
||||
|
||||
if state == 'down': # Already stoped, return
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
if state not in ('up', 'suspended'):
|
||||
self._push_front_op(
|
||||
@ -471,7 +470,7 @@ if sys.platform == 'win32':
|
||||
else:
|
||||
self.service().stop_machine(self._vmid)
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _suspend_machine(self) -> str:
|
||||
"""
|
||||
@ -483,16 +482,16 @@ if sys.platform == 'win32':
|
||||
raise Exception('Machine not found')
|
||||
|
||||
if state == 'suspended': # Already suspended, return
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
if state != 'up':
|
||||
self._push_front_op(
|
||||
Operation.RETRY
|
||||
) # Remember here, the return State.FINISH will make this retry be "poped" right ar return
|
||||
) # Remember here, the return types.states.State.FINISH will make this retry be "poped" right ar return
|
||||
else:
|
||||
self.service().suspend_machine(self._vmid)
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _change_mac(self) -> str:
|
||||
"""
|
||||
@ -502,48 +501,48 @@ if sys.platform == 'win32':
|
||||
# Fix usb if needed
|
||||
self.service().fix_usb(self._vmid)
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
# Check methods
|
||||
def _create_checker(self) -> str:
|
||||
def _create_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks the state of a deploy for an user or cache
|
||||
"""
|
||||
return self._check_machine_state('down')
|
||||
|
||||
def _start_checker(self) -> str:
|
||||
def _start_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if machine has started
|
||||
"""
|
||||
return self._check_machine_state(UP_STATES)
|
||||
|
||||
def _stop_checker(self) -> str:
|
||||
def _stop_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if machine has stoped
|
||||
"""
|
||||
return self._check_machine_state('down')
|
||||
|
||||
def _suspend_checker(self) -> str:
|
||||
def _suspend_checker(self) -> types.states.State:
|
||||
"""
|
||||
Check if the machine has suspended
|
||||
"""
|
||||
return self._check_machine_state('suspended')
|
||||
|
||||
def _remove_checker(self) -> str:
|
||||
def _remove_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if a machine has been removed
|
||||
"""
|
||||
return self._check_machine_state('unknown')
|
||||
|
||||
def _mac_checker(self) -> str:
|
||||
def _mac_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if change mac operation has finished.
|
||||
|
||||
Changing nic configuration es 1-step operation, so when we check it here, it is already done
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Check what operation is going on, and acts acordly to it
|
||||
"""
|
||||
@ -551,12 +550,12 @@ if sys.platform == 'win32':
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: dict[Operation, typing.Optional[collections.abc.Callable[[], str]]] = {
|
||||
fncs: dict[Operation, typing.Optional[collections.abc.Callable[[], types.states.State]]] = {
|
||||
Operation.CREATE: self._create_checker,
|
||||
Operation.RETRY: self._retry,
|
||||
Operation.WAIT: self._wait,
|
||||
@ -568,13 +567,13 @@ if sys.platform == 'win32':
|
||||
}
|
||||
|
||||
try:
|
||||
operation_checker: typing.Optional[typing.Optional[collections.abc.Callable[[], str]]] = fncs.get(op, None)
|
||||
operation_checker: typing.Optional[typing.Optional[collections.abc.Callable[[], types.states.State]]] = fncs.get(op, None)
|
||||
|
||||
if operation_checker is None:
|
||||
return self._error(f'Unknown operation found at check queue ({op})')
|
||||
|
||||
state = operation_checker()
|
||||
if state == State.FINISHED:
|
||||
if state == types.states.State.FINISHED:
|
||||
self._pop_current_op() # Remove runing op
|
||||
return self._execute_queue()
|
||||
|
||||
@ -582,14 +581,14 @@ if sys.platform == 'win32':
|
||||
except Exception as e:
|
||||
return self._error(e)
|
||||
|
||||
def move_to_cache(self, newLevel: int) -> str:
|
||||
def move_to_cache(self, level: int) -> types.states.State:
|
||||
"""
|
||||
Moves machines between cache levels
|
||||
"""
|
||||
if Operation.REMOVE in self._queue:
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
if newLevel == self.L1_CACHE:
|
||||
if level == self.L1_CACHE:
|
||||
self._queue = [Operation.START, Operation.FINISH]
|
||||
else:
|
||||
self._queue = [Operation.START, Operation.SUSPEND, Operation.FINISH]
|
||||
@ -606,7 +605,7 @@ if sys.platform == 'win32':
|
||||
"""
|
||||
return self._reason
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
"""
|
||||
Invoked for destroying a deployed service
|
||||
"""
|
||||
@ -614,7 +613,7 @@ if sys.platform == 'win32':
|
||||
if self._vmid == '':
|
||||
self._queue = []
|
||||
self._reason = "canceled"
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
# If executing something, wait until finished to remove it
|
||||
# We simply replace the execution queue
|
||||
@ -629,12 +628,12 @@ if sys.platform == 'win32':
|
||||
|
||||
self._queue = [op, Operation.STOP, Operation.REMOVE, Operation.FINISH]
|
||||
# Do not execute anything.here, just continue normally
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
types.states.State.values RUNNING, FINISHED or ERROR.
|
||||
|
||||
This can be invoked directly by an administration or by the clean up
|
||||
of the deployed service (indirectly).
|
||||
@ -660,7 +659,7 @@ if sys.platform == 'win32':
|
||||
|
||||
def _debug(self, txt: str) -> None:
|
||||
logger.debug(
|
||||
'State at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s',
|
||||
'types.states.State.at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s',
|
||||
txt,
|
||||
self._name,
|
||||
self._ip,
|
||||
|
@ -38,7 +38,7 @@ from datetime import datetime
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from uds.core.services import Publication
|
||||
from uds.core.types.states import State
|
||||
from uds.core import types
|
||||
from uds.core.util import autoserializable
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
@ -87,7 +87,7 @@ class OVirtPublication(Publication, autoserializable.AutoSerializable):
|
||||
self._destroy_after = destroy_after == 't'
|
||||
self.mark_for_upgrade() # Mark so manager knows it has to be saved again
|
||||
|
||||
def publish(self) -> str:
|
||||
def publish(self) -> types.states.State:
|
||||
"""
|
||||
Realizes the publication of the service
|
||||
"""
|
||||
@ -106,19 +106,19 @@ class OVirtPublication(Publication, autoserializable.AutoSerializable):
|
||||
except Exception as e:
|
||||
self._state = 'error'
|
||||
self._reason = str(e)
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Checks state of publication creation
|
||||
"""
|
||||
if self._state == 'ok':
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
if self._state == 'error':
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
try:
|
||||
self._state = self.service().get_template_state(self._template_id)
|
||||
@ -127,28 +127,28 @@ class OVirtPublication(Publication, autoserializable.AutoSerializable):
|
||||
except Exception as e:
|
||||
self._state = 'error'
|
||||
self._reason = str(e)
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
# If publication os done (template is ready), and cancel was requested, do it just after template becomes ready
|
||||
if self._state == 'ok':
|
||||
if self._destroy_after:
|
||||
self._destroy_after = False
|
||||
return self.destroy()
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def error_reason(self) -> str:
|
||||
"""
|
||||
If a publication produces an error, here we must notify the reason why
|
||||
it happened. This will be called just after publish or check_state
|
||||
if they return State.ERROR
|
||||
if they return types.states.State.ERROR
|
||||
|
||||
Returns an string, in our case, set at check_state
|
||||
"""
|
||||
return self._reason
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
"""
|
||||
This is called once a publication is no more needed.
|
||||
|
||||
@ -156,24 +156,24 @@ class OVirtPublication(Publication, autoserializable.AutoSerializable):
|
||||
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.
|
||||
The retunred value is the same as when publishing, types.states.State.RUNNING,
|
||||
types.states.State.FINISHED or types.states.State.ERROR.
|
||||
"""
|
||||
# We do not do anything else to destroy this instance of publication
|
||||
if self._state == 'locked':
|
||||
self._destroy_after = True
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
try:
|
||||
self.service().remove_template(self._template_id)
|
||||
except Exception as e:
|
||||
self._state = 'error'
|
||||
self._reason = str(e)
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
"""
|
||||
Do same thing as destroy
|
||||
"""
|
||||
|
@ -36,9 +36,8 @@ import logging
|
||||
import typing
|
||||
import collections.abc
|
||||
|
||||
from uds.core import services
|
||||
from uds.core import services, types
|
||||
from uds.core.managers.crypto import CryptoManager
|
||||
from uds.core.types.states import State
|
||||
from uds.core.util import log, autoserializable
|
||||
from uds.core.util.model import sql_stamp_seconds
|
||||
|
||||
@ -145,7 +144,7 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
def get_ip(self) -> str:
|
||||
return self._ip
|
||||
|
||||
def set_ready(self) -> str:
|
||||
def set_ready(self) -> types.states.State:
|
||||
"""
|
||||
Notifies the current "deadline" to the user, before accessing by UDS
|
||||
The machine has been already been started.
|
||||
@ -154,17 +153,17 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
"""
|
||||
dbs = self.db_obj()
|
||||
if not dbs:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
try:
|
||||
# First, check Machine is alive..
|
||||
status = self._check_machine_is_ready()
|
||||
if status == State.FINISHED:
|
||||
if status == types.states.State.FINISHED:
|
||||
self.service().notify_deadline(self._machine_id, dbs.deployed_service.get_deadline())
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
if status == State.ERROR:
|
||||
return State.ERROR
|
||||
if status == types.states.State.ERROR:
|
||||
return types.states.State.ERROR
|
||||
|
||||
# Machine powered off, check what to do...
|
||||
if not self.service().try_start_if_unavailable():
|
||||
@ -179,7 +178,7 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
except Exception as e:
|
||||
return self._error(f'Error setting ready state: {e}')
|
||||
|
||||
def deploy_for_user(self, user: 'models.User') -> str:
|
||||
def deploy_for_user(self, user: 'models.User') -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for an user.
|
||||
"""
|
||||
@ -187,7 +186,7 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
self._init_queue_for_deploy()
|
||||
return self._execute_queue()
|
||||
|
||||
def deploy_for_cache(self, cacheLevel: int) -> str:
|
||||
def deploy_for_cache(self, level: int) -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for cache
|
||||
"""
|
||||
@ -197,7 +196,7 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
def _init_queue_for_deploy(self) -> None:
|
||||
self._queue = [Operation.CREATE, Operation.FINISH]
|
||||
|
||||
def _check_machine_is_ready(self) -> str:
|
||||
def _check_machine_is_ready(self) -> types.states.State:
|
||||
logger.debug(
|
||||
'Checking that state of machine %s (%s) is ready',
|
||||
self._machine_id,
|
||||
@ -212,9 +211,9 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
|
||||
# possible status are ("off", "oglive", "busy", "linux", "windows", "macos" o "unknown").
|
||||
if status['status'] in ("linux", "windows", "macos"):
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _get_current_op(self) -> Operation:
|
||||
if len(self._queue) == 0:
|
||||
@ -229,12 +228,12 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
res = self._queue.pop(0)
|
||||
return res
|
||||
|
||||
def _error(self, reason: typing.Any) -> str:
|
||||
def _error(self, reason: typing.Any) -> types.states.State:
|
||||
"""
|
||||
Internal method to set object as error state
|
||||
|
||||
Returns:
|
||||
State.ERROR, so we can do "return self.__error(reason)"
|
||||
types.states.State.ERROR, so we can do "return self.__error(reason)"
|
||||
"""
|
||||
logger.debug('Setting error state, reason: %s', reason)
|
||||
self.do_log(log.LogLevel.ERROR, reason)
|
||||
@ -247,17 +246,17 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
|
||||
self._queue = [Operation.ERROR]
|
||||
self._reason = str(reason)
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
def _execute_queue(self) -> str:
|
||||
def _execute_queue(self) -> types.states.State:
|
||||
self._debug('executeQueue')
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: dict[int, typing.Optional[collections.abc.Callable[[], str]]] = {
|
||||
Operation.CREATE: self._create,
|
||||
@ -274,21 +273,21 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
|
||||
execFnc()
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
except Exception as e:
|
||||
# logger.exception('Got Exception')
|
||||
return self._error(e)
|
||||
|
||||
# Queue execution methods
|
||||
def _retry(self) -> str:
|
||||
def _retry(self) -> types.states.State:
|
||||
"""
|
||||
Used to retry an operation
|
||||
In fact, this will not be never invoked, unless we push it twice, because
|
||||
check_state method will "pop" first item when a check operation returns State.FINISHED
|
||||
check_state method will "pop" first item when a check operation returns types.states.State.FINISHED
|
||||
|
||||
At executeQueue this return value will be ignored, and it will only be used at check_state
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _create(self) -> str:
|
||||
"""
|
||||
@ -329,12 +328,12 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
dbs.properties['token'] = token
|
||||
dbs.log_ip(self._ip)
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _start(self) -> str:
|
||||
if self._machine_id:
|
||||
self.service().power_on(self._machine_id)
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _remove(self) -> str:
|
||||
"""
|
||||
@ -347,10 +346,10 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
# so we can avoid double unreserve
|
||||
if dbs.properties.get('from_release') is None:
|
||||
self.service().unreserve(self._machine_id)
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
# Check methods
|
||||
def _create_checker(self) -> str:
|
||||
def _create_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks the state of a deploy for an user or cache
|
||||
"""
|
||||
@ -359,13 +358,13 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
# Alias for poweron check
|
||||
_checkStart = _create_checker
|
||||
|
||||
def _removed_checker(self) -> str:
|
||||
def _removed_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if a machine has been removed
|
||||
"""
|
||||
return State.FINISHED # No check at all, always true
|
||||
return types.states.State.FINISHED # No check at all, always true
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Check what operation is going on, and acts acordly to it
|
||||
"""
|
||||
@ -373,12 +372,12 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: dict[Operation, typing.Optional[collections.abc.Callable[[], str]]] = {
|
||||
fncs: dict[Operation, typing.Optional[collections.abc.Callable[[], types.states.State]]] = {
|
||||
Operation.CREATE: self._create_checker,
|
||||
Operation.RETRY: self._retry,
|
||||
Operation.REMOVE: self._removed_checker,
|
||||
@ -386,13 +385,13 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
}
|
||||
|
||||
try:
|
||||
chkFnc: typing.Optional[typing.Optional[collections.abc.Callable[[], str]]] = fncs.get(op)
|
||||
chkFnc: typing.Optional[typing.Optional[collections.abc.Callable[[], types.states.State]]] = fncs.get(op)
|
||||
|
||||
if chkFnc is None:
|
||||
return self._error(f'Unknown operation found at check queue ({op})')
|
||||
|
||||
state = chkFnc()
|
||||
if state == State.FINISHED:
|
||||
if state == types.states.State.FINISHED:
|
||||
self._pop_current_op() # Remove runing op
|
||||
return self._execute_queue()
|
||||
|
||||
@ -410,7 +409,7 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
"""
|
||||
return self._reason
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
"""
|
||||
Invoked for destroying a deployed service
|
||||
"""
|
||||
@ -420,10 +419,10 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
self._queue = [Operation.REMOVE, Operation.FINISH]
|
||||
return self._execute_queue()
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
types.states.State.values RUNNING, FINISHED or ERROR.
|
||||
|
||||
This can be invoked directly by an administration or by the clean up
|
||||
of the deployed service (indirectly).
|
||||
@ -442,9 +441,9 @@ class OpenGnsysUserService(services.UserService, autoserializable.AutoSerializab
|
||||
Operation.RETRY: 'retry',
|
||||
}.get(op, '????')
|
||||
|
||||
def _debug(self, txt) -> None:
|
||||
def _debug(self, txt: str) -> None:
|
||||
logger.debug(
|
||||
'State at %s: name: %s, ip: %s, mac: %s, machine:%s, queue: %s',
|
||||
'types.states.State.at %s: name: %s, ip: %s, mac: %s, machine:%s, queue: %s',
|
||||
txt,
|
||||
self._name,
|
||||
self._ip,
|
||||
|
@ -34,7 +34,7 @@ import typing
|
||||
import collections.abc
|
||||
|
||||
from uds.core.services import Publication
|
||||
from uds.core.types.states import State
|
||||
from uds.core import types
|
||||
from uds.core.util import autoserializable
|
||||
from uds.core.util.model import sql_datetime
|
||||
|
||||
@ -57,23 +57,23 @@ class OpenGnsysPublication(Publication, autoserializable.AutoSerializable):
|
||||
def service(self) -> 'OGService':
|
||||
return typing.cast('OGService', super().service())
|
||||
|
||||
def publish(self) -> str:
|
||||
def publish(self) -> types.states.State:
|
||||
"""
|
||||
Realizes the publication of the service, on OpenGnsys, does nothing
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Checks state of publication creation
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def error_reason(self) -> str:
|
||||
return 'No error possible :)'
|
||||
|
||||
def destroy(self) -> str:
|
||||
return State.FINISHED
|
||||
def destroy(self) -> types.states.State:
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
return self.destroy()
|
||||
|
@ -38,7 +38,6 @@ import typing
|
||||
import collections.abc
|
||||
|
||||
from uds.core import services, consts, types
|
||||
from uds.core.types.states import State
|
||||
from uds.core.util import log, autoserializable
|
||||
|
||||
from . import on
|
||||
@ -136,9 +135,9 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
def get_ip(self) -> str:
|
||||
return self._ip
|
||||
|
||||
def set_ready(self) -> str:
|
||||
def set_ready(self) -> types.states.State:
|
||||
if self.cache.get('ready') == '1':
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
try:
|
||||
state = self.service().getMachineState(self._vmid)
|
||||
@ -153,7 +152,7 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
self.do_log(log.LogLevel.ERROR, 'Error on setReady: {}'.format(e))
|
||||
# Treat as operation done, maybe the machine is ready and we can continue
|
||||
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def reset(self) -> None:
|
||||
if self._vmid != '':
|
||||
@ -165,7 +164,7 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
def desktop_login(self, username: str, password: str, domain: str = ''):
|
||||
return self.service().desktop_login(self._vmid, username, password, domain)
|
||||
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> str:
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> types.states.State:
|
||||
# Here we will check for suspending the VM (when full ready)
|
||||
logger.debug('Checking if cache 2 for %s', self._name)
|
||||
if self._get_current_op() == Operation.WAIT:
|
||||
@ -173,9 +172,9 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
self._get_and_pop_current_op() # Remove current state
|
||||
return self._execute_queue()
|
||||
# Do not need to go to level 2 (opWait is in fact "waiting for moving machine to cache level 2)
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def deploy_for_user(self, user: 'models.User') -> str:
|
||||
def deploy_for_user(self, user: 'models.User') -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for an user.
|
||||
"""
|
||||
@ -183,15 +182,15 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
self._init_queue_for_deploy(False)
|
||||
return self._execute_queue()
|
||||
|
||||
def deploy_for_cache(self, cacheLevel: int) -> str:
|
||||
def deploy_for_cache(self, level: int) -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for cache
|
||||
"""
|
||||
self._init_queue_for_deploy(cacheLevel == self.L2_CACHE)
|
||||
self._init_queue_for_deploy(level == self.L2_CACHE)
|
||||
return self._execute_queue()
|
||||
|
||||
def _init_queue_for_deploy(self, forLevel2: bool = False) -> None:
|
||||
if forLevel2 is False:
|
||||
def _init_queue_for_deploy(self, for_level_2: bool = False) -> None:
|
||||
if for_level_2 is False:
|
||||
self._queue = [Operation.CREATE, Operation.START, Operation.FINISH]
|
||||
else:
|
||||
self._queue = [
|
||||
@ -202,7 +201,7 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
Operation.FINISH,
|
||||
]
|
||||
|
||||
def _check_machine_state(self, state: on.types.VmState) -> str:
|
||||
def _check_machine_state(self, state: on.types.VmState) -> types.states.State:
|
||||
logger.debug(
|
||||
'Checking that state of machine %s (%s) is %s',
|
||||
self._vmid,
|
||||
@ -218,14 +217,14 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
]: # @UndefinedVariable
|
||||
return self._error('Machine not found')
|
||||
|
||||
ret = State.RUNNING
|
||||
ret = types.states.State.RUNNING
|
||||
|
||||
if isinstance(state, (list, tuple)):
|
||||
if state in state:
|
||||
ret = State.FINISHED
|
||||
ret = types.states.State.FINISHED
|
||||
else:
|
||||
if state == state:
|
||||
ret = State.FINISHED
|
||||
ret = types.states.State.FINISHED
|
||||
|
||||
return ret
|
||||
|
||||
@ -244,12 +243,12 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
def _push_front_op(self, op: Operation) -> None:
|
||||
self._queue.insert(0, op)
|
||||
|
||||
def _error(self, reason: typing.Any) -> str:
|
||||
def _error(self, reason: typing.Any) -> types.states.State:
|
||||
"""
|
||||
Internal method to set object as error state
|
||||
|
||||
Returns:
|
||||
State.ERROR, so we can do "return self.__error(reason)"
|
||||
types.states.State.ERROR, so we can do "return self.__error(reason)"
|
||||
"""
|
||||
reason = str(reason)
|
||||
logger.debug('Setting error state, reason: %s', reason)
|
||||
@ -263,17 +262,17 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
|
||||
self._queue = [Operation.ERROR]
|
||||
self._reason = str(reason)
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
def _execute_queue(self) -> str:
|
||||
def _execute_queue(self) -> types.states.State:
|
||||
self.__debug('executeQueue')
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: dict[Operation, typing.Optional[collections.abc.Callable[[], str]]] = {
|
||||
Operation.CREATE: self._create,
|
||||
@ -292,27 +291,27 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
|
||||
operation_executor()
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
except Exception as e:
|
||||
logger.exception('Got Exception')
|
||||
return self._error(e)
|
||||
|
||||
# Queue execution methods
|
||||
def _retry(self) -> str:
|
||||
def _retry(self) -> types.states.State:
|
||||
"""
|
||||
Used to retry an operation
|
||||
In fact, this will not be never invoked, unless we push it twice, because
|
||||
check_state method will "pop" first item when a check operation returns State.FINISHED
|
||||
check_state method will "pop" first item when a check operation returns types.states.State.FINISHED
|
||||
|
||||
At executeQueue this return value will be ignored, and it will only be used at check_state
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _wait(self) -> str:
|
||||
def _wait(self) -> types.states.State:
|
||||
"""
|
||||
Executes opWait, it simply waits something "external" to end
|
||||
"""
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _create(self) -> str:
|
||||
"""
|
||||
@ -330,13 +329,13 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
) # OpenNebula don't let us to create machines with more than 15 chars!!!
|
||||
|
||||
self._vmid = self.service().deploy_from_template(name, templateId)
|
||||
if self._vmid is None:
|
||||
if not self._vmid:
|
||||
raise Exception('Can\'t create machine')
|
||||
|
||||
# Get IP & MAC (early stage)
|
||||
# self._mac, self._ip = self.service().getNetInfo(self._vmid)
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _remove(self) -> str:
|
||||
"""
|
||||
@ -352,11 +351,11 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
if subState < 3: # Less than running
|
||||
logger.info('Must wait before remove: %s', subState)
|
||||
self._push_front_op(Operation.RETRY)
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
self.service().removeMachine(self._vmid)
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _start_machine(self) -> str:
|
||||
"""
|
||||
@ -367,41 +366,41 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
# Get IP & MAC (later stage, after "powering on")
|
||||
self._mac, self._ip = self.service().getNetInfo(self._vmid)
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _shutdown_machine(self) -> str:
|
||||
"""
|
||||
Suspends the machine
|
||||
"""
|
||||
self.service().shutdownMachine(self._vmid)
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
# Check methods
|
||||
def _create_checker(self) -> str:
|
||||
def _create_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks the state of a deploy for an user or cache
|
||||
"""
|
||||
return self._check_machine_state(on.types.VmState.ACTIVE) # @UndefinedVariable
|
||||
|
||||
def _start_checker(self) -> str:
|
||||
def _start_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if machine has started
|
||||
"""
|
||||
return self._check_machine_state(on.types.VmState.ACTIVE) # @UndefinedVariable
|
||||
|
||||
def _shutdown_checker(self) -> str:
|
||||
def _shutdown_checker(self) -> types.states.State:
|
||||
"""
|
||||
Check if the machine has suspended
|
||||
"""
|
||||
return self._check_machine_state(on.types.VmState.POWEROFF) # @UndefinedVariable
|
||||
|
||||
def _remove_checker(self) -> str:
|
||||
def _remove_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if a machine has been removed
|
||||
"""
|
||||
return State.FINISHED # No check at all, always true
|
||||
return types.states.State.FINISHED # No check at all, always true
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Check what operation is going on, and acts based on it
|
||||
"""
|
||||
@ -409,12 +408,12 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: dict[Operation, typing.Optional[collections.abc.Callable[[], str]]] = {
|
||||
fncs: dict[Operation, typing.Optional[collections.abc.Callable[[], types.states.State]]] = {
|
||||
Operation.CREATE: self._create_checker,
|
||||
Operation.RETRY: self._retry,
|
||||
Operation.WAIT: self._wait,
|
||||
@ -424,13 +423,13 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
}
|
||||
|
||||
try:
|
||||
chkFnc: typing.Optional[collections.abc.Callable[[], str]] = fncs.get(op, None)
|
||||
chkFnc: typing.Optional[collections.abc.Callable[[], types.states.State]] = fncs.get(op, None)
|
||||
|
||||
if chkFnc is None:
|
||||
return self._error('Unknown operation found at check queue ({0})'.format(op))
|
||||
|
||||
state = chkFnc()
|
||||
if state == State.FINISHED:
|
||||
if state == types.states.State.FINISHED:
|
||||
self._get_and_pop_current_op() # Remove runing op
|
||||
return self._execute_queue()
|
||||
|
||||
@ -438,14 +437,14 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
except Exception as e:
|
||||
return self._error(e)
|
||||
|
||||
def move_to_cache(self, newLevel: int) -> str:
|
||||
def move_to_cache(self, level: int) -> types.states.State:
|
||||
"""
|
||||
Moves machines between cache levels
|
||||
"""
|
||||
if Operation.REMOVE in self._queue:
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
if newLevel == self.L1_CACHE:
|
||||
if level == self.L1_CACHE:
|
||||
self._queue = [Operation.START, Operation.FINISH]
|
||||
else:
|
||||
self._queue = [Operation.START, Operation.SHUTDOWN, Operation.FINISH]
|
||||
@ -462,7 +461,7 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
"""
|
||||
return self._reason
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
"""
|
||||
Invoked for destroying a deployed service
|
||||
"""
|
||||
@ -480,12 +479,12 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
|
||||
self._queue = [op, Operation.REMOVE, Operation.FINISH]
|
||||
# Do not execute anything.here, just continue normally
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
types.states.State.values RUNNING, FINISHED or ERROR.
|
||||
|
||||
This can be invoked directly by an administration or by the clean up
|
||||
of the deployed service (indirectly).
|
||||
@ -509,7 +508,7 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
|
||||
|
||||
def __debug(self, txt: str) -> None:
|
||||
logger.debug(
|
||||
'State at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s',
|
||||
'types.states.State.at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s',
|
||||
txt,
|
||||
self._name,
|
||||
self._ip,
|
||||
|
@ -35,7 +35,7 @@ import typing
|
||||
import collections.abc
|
||||
|
||||
from uds.core.services import Publication
|
||||
from uds.core.types.states import State
|
||||
from uds.core import types
|
||||
from uds.core.util import autoserializable
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
@ -81,7 +81,7 @@ class OpenNebulaLivePublication(Publication, autoserializable.AutoSerializable):
|
||||
|
||||
self.mark_for_upgrade() # Flag so manager can save it again with new format
|
||||
|
||||
def publish(self) -> str:
|
||||
def publish(self) -> types.states.State:
|
||||
"""
|
||||
Realizes the publication of the service
|
||||
"""
|
||||
@ -96,47 +96,47 @@ class OpenNebulaLivePublication(Publication, autoserializable.AutoSerializable):
|
||||
except Exception as e:
|
||||
self._state = 'error'
|
||||
self._reason = str(e)
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Checks state of publication creation
|
||||
"""
|
||||
if self._state == 'running':
|
||||
try:
|
||||
if self.service().check_template_published(self._template_id) is False:
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
self._state = 'ok'
|
||||
except Exception as e:
|
||||
self._state = 'error'
|
||||
self._reason = str(e)
|
||||
|
||||
if self._state == 'error':
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if self._state == 'ok':
|
||||
if self._destroy_after: # If we must destroy after publication, do it now
|
||||
self._destroy_after = False
|
||||
return self.destroy()
|
||||
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
self._state = 'ok'
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def error_reason(self) -> str:
|
||||
"""
|
||||
If a publication produces an error, here we must notify the reason why
|
||||
it happened. This will be called just after publish or check_state
|
||||
if they return State.ERROR
|
||||
if they return types.states.State.ERROR
|
||||
|
||||
Returns an string, in our case, set at check_state
|
||||
"""
|
||||
return self._reason
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
"""
|
||||
This is called once a publication is no more needed.
|
||||
|
||||
@ -144,15 +144,15 @@ class OpenNebulaLivePublication(Publication, autoserializable.AutoSerializable):
|
||||
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.
|
||||
The retunred value is the same as when publishing, types.states.State.RUNNING,
|
||||
types.states.State.FINISHED or types.states.State.ERROR.
|
||||
"""
|
||||
if self._state == 'error':
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if self._state == 'running':
|
||||
self._destroy_after = True
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
# We do not do anything else to destroy this instance of publication
|
||||
try:
|
||||
@ -160,11 +160,11 @@ class OpenNebulaLivePublication(Publication, autoserializable.AutoSerializable):
|
||||
except Exception as e:
|
||||
self._state = 'error'
|
||||
self._reason = str(e)
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
"""
|
||||
Do same thing as destroy
|
||||
"""
|
||||
|
@ -161,12 +161,10 @@ class OpenNebulaLiveService(services.Service):
|
||||
Loads required values inside
|
||||
"""
|
||||
|
||||
t: 'on.types.TemplateType'
|
||||
self.template.set_choices(
|
||||
[gui.choice_item(t.id, t.name) for t in self.provider().getTemplates()]
|
||||
)
|
||||
|
||||
d: 'on.types.StorageType'
|
||||
self.datastore.set_choices(
|
||||
[gui.choice_item(d.id, d.name) for d in self.provider().getDatastores()]
|
||||
)
|
||||
|
@ -36,8 +36,7 @@ import logging
|
||||
import pickle # nosec: not insecure, we are loading our own data
|
||||
import typing
|
||||
|
||||
from uds.core import consts, services
|
||||
from uds.core.types.states import State
|
||||
from uds.core import consts, services, types
|
||||
from uds.core.util import autoserializable, log
|
||||
|
||||
from . import openstack
|
||||
@ -145,13 +144,13 @@ class OpenStackLiveDeployment(
|
||||
def get_ip(self) -> str:
|
||||
return self._ip
|
||||
|
||||
def set_ready(self) -> str:
|
||||
def set_ready(self) -> types.states.State:
|
||||
"""
|
||||
The method is invoked whenever a machine is provided to an user, right
|
||||
before presenting it (via transport rendering) to the user.
|
||||
"""
|
||||
if self.cache.get('ready') == '1':
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
try:
|
||||
status = self.service().get_machine_state(self._vmid)
|
||||
@ -171,13 +170,13 @@ class OpenStackLiveDeployment(
|
||||
self.do_log(log.LogLevel.ERROR, 'Error on setReady: {}'.format(e))
|
||||
# Treat as operation done, maybe the machine is ready and we can continue
|
||||
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def reset(self) -> None:
|
||||
if self._vmid != '':
|
||||
self.service().reset_machine(self._vmid)
|
||||
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> str:
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> types.states.State:
|
||||
# Here we will check for suspending the VM (when full ready)
|
||||
logger.debug('Checking if cache 2 for %s', self._name)
|
||||
if self._get_current_op() == Operation.WAIT:
|
||||
@ -185,9 +184,9 @@ class OpenStackLiveDeployment(
|
||||
self._pop_current_op() # Remove current state
|
||||
return self._execute_queue()
|
||||
# Do not need to go to level 2 (opWait is in fact "waiting for moving machine to cache level 2)
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def deploy_for_user(self, user: 'models.User') -> str:
|
||||
def deploy_for_user(self, user: 'models.User') -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for an user.
|
||||
"""
|
||||
@ -195,11 +194,11 @@ class OpenStackLiveDeployment(
|
||||
self._init_queue_for_deploy(False)
|
||||
return self._execute_queue()
|
||||
|
||||
def deploy_for_cache(self, cacheLevel: int) -> str:
|
||||
def deploy_for_cache(self, level: int) -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for cache
|
||||
"""
|
||||
self._init_queue_for_deploy(cacheLevel == self.L2_CACHE)
|
||||
self._init_queue_for_deploy(level == self.L2_CACHE)
|
||||
return self._execute_queue()
|
||||
|
||||
def _init_queue_for_deploy(self, forLevel2: bool = False) -> None:
|
||||
@ -208,7 +207,7 @@ class OpenStackLiveDeployment(
|
||||
else:
|
||||
self._queue = [Operation.CREATE, Operation.WAIT, Operation.SUSPEND, Operation.FINISH]
|
||||
|
||||
def _check_machine_state(self, chkState: str) -> str:
|
||||
def _check_machine_state(self, chkState: str) -> types.states.State:
|
||||
logger.debug(
|
||||
'Checking that state of machine %s (%s) is %s',
|
||||
self._vmid,
|
||||
@ -221,10 +220,10 @@ class OpenStackLiveDeployment(
|
||||
if openstack.status_is_lost(status):
|
||||
return self._error('Machine not available. ({})'.format(status))
|
||||
|
||||
ret = State.RUNNING
|
||||
ret = types.states.State.RUNNING
|
||||
chkStates = [chkState] if not isinstance(chkState, (list, tuple)) else chkState
|
||||
if status in chkStates:
|
||||
ret = State.FINISHED
|
||||
ret = types.states.State.FINISHED
|
||||
|
||||
return ret
|
||||
|
||||
@ -240,12 +239,12 @@ class OpenStackLiveDeployment(
|
||||
|
||||
return self._queue.pop(0)
|
||||
|
||||
def _error(self, reason: typing.Any) -> str:
|
||||
def _error(self, reason: typing.Any) -> types.states.State:
|
||||
"""
|
||||
Internal method to set object as error state
|
||||
|
||||
Returns:
|
||||
State.ERROR, so we can do "return self.__error(reason)"
|
||||
types.states.State.ERROR, so we can do "return self.__error(reason)"
|
||||
"""
|
||||
logger.debug('Setting error state, reason: %s', reason)
|
||||
self._queue = [Operation.ERROR]
|
||||
@ -259,25 +258,25 @@ class OpenStackLiveDeployment(
|
||||
except Exception:
|
||||
logger.warning('Can\t set machine %s state to stopped', self._vmid)
|
||||
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
def _execute_queue(self) -> str:
|
||||
self.__debug('executeQueue')
|
||||
def _execute_queue(self) -> types.states.State:
|
||||
self._debug('executeQueue')
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: dict[int, collections.abc.Callable[[], str]] = {
|
||||
Operation.CREATE: self.__create,
|
||||
Operation.RETRY: self.__retry,
|
||||
Operation.START: self.__startMachine,
|
||||
Operation.SUSPEND: self.__suspendMachine,
|
||||
Operation.WAIT: self.__wait,
|
||||
Operation.REMOVE: self.__remove,
|
||||
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:
|
||||
@ -286,32 +285,32 @@ class OpenStackLiveDeployment(
|
||||
|
||||
fncs[op]()
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
except Exception as e:
|
||||
return self._error(e)
|
||||
|
||||
# Queue execution methods
|
||||
def __retry(self) -> str:
|
||||
def _retry(self) -> types.states.State:
|
||||
"""
|
||||
Used to retry an operation
|
||||
In fact, this will not be never invoked, unless we push it twice, because
|
||||
check_state method will "pop" first item when a check operation returns State.FINISHED
|
||||
check_state method will "pop" first item when a check operation returns types.states.State.FINISHED
|
||||
|
||||
At executeQueue this return value will be ignored, and it will only be used at check_state
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def __wait(self) -> str:
|
||||
def _wait(self) -> types.states.State:
|
||||
"""
|
||||
Executes opWait, it simply waits something "external" to end
|
||||
"""
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def __create(self) -> str:
|
||||
def _create(self) -> str:
|
||||
"""
|
||||
Deploys a machine from template for user/cache
|
||||
"""
|
||||
templateId = self.publication().getTemplateId()
|
||||
templateId = self.publication().get_template_id()
|
||||
name = self.get_name()
|
||||
if name == consts.NO_MORE_NAMES:
|
||||
raise Exception(
|
||||
@ -326,9 +325,9 @@ class OpenStackLiveDeployment(
|
||||
if not self._vmid:
|
||||
raise Exception('Can\'t create machine')
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def __remove(self) -> str:
|
||||
def _remove(self) -> str:
|
||||
"""
|
||||
Removes a machine from system
|
||||
"""
|
||||
@ -339,74 +338,74 @@ class OpenStackLiveDeployment(
|
||||
|
||||
self.service().remove_machine(self._vmid)
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def __startMachine(self) -> str:
|
||||
def _start_machine(self) -> str:
|
||||
"""
|
||||
Powers on the machine
|
||||
"""
|
||||
self.service().start_machine(self._vmid)
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def __suspendMachine(self) -> str:
|
||||
def _suspend_machine(self) -> str:
|
||||
"""
|
||||
Suspends the machine
|
||||
"""
|
||||
self.service().suspend_machine(self._vmid)
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
# Check methods
|
||||
def __checkCreate(self) -> str:
|
||||
def _check_create(self) -> types.states.State:
|
||||
"""
|
||||
Checks the state of a deploy for an user or cache
|
||||
"""
|
||||
ret = self._check_machine_state(openstack.ACTIVE)
|
||||
if ret == State.FINISHED:
|
||||
if ret == types.states.State.FINISHED:
|
||||
# Get IP & MAC (early stage)
|
||||
self._mac, self._ip = self.service().get_network_info(self._vmid)
|
||||
|
||||
return ret
|
||||
|
||||
def __checkStart(self) -> str:
|
||||
def _check_start(self) -> types.states.State:
|
||||
"""
|
||||
Checks if machine has started
|
||||
"""
|
||||
return self._check_machine_state(openstack.ACTIVE)
|
||||
|
||||
def __checkSuspend(self) -> str:
|
||||
def _check_suspend(self) -> types.states.State:
|
||||
"""
|
||||
Check if the machine has suspended
|
||||
"""
|
||||
return self._check_machine_state(openstack.SUSPENDED)
|
||||
|
||||
def __checkRemoved(self) -> str:
|
||||
def _check_removed(self) -> types.states.State:
|
||||
"""
|
||||
Checks if a machine has been removed
|
||||
"""
|
||||
return State.FINISHED # No check at all, always true
|
||||
return types.states.State.FINISHED # No check at all, always true
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Check what operation is going on, and acts acordly to it
|
||||
"""
|
||||
self.__debug('check_state')
|
||||
self._debug('check_state')
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: dict[int, collections.abc.Callable[[], str]] = {
|
||||
Operation.CREATE: self.__checkCreate,
|
||||
Operation.RETRY: self.__retry,
|
||||
Operation.WAIT: self.__wait,
|
||||
Operation.START: self.__checkStart,
|
||||
Operation.SUSPEND: self.__checkSuspend,
|
||||
Operation.REMOVE: self.__checkRemoved,
|
||||
fncs: dict[int, collections.abc.Callable[[], types.states.State]] = {
|
||||
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:
|
||||
@ -414,7 +413,7 @@ class OpenStackLiveDeployment(
|
||||
return self._error('Unknown operation found at execution queue ({0})'.format(op))
|
||||
|
||||
state = fncs[op]()
|
||||
if state == State.FINISHED:
|
||||
if state == types.states.State.FINISHED:
|
||||
self._pop_current_op() # Remove runing op
|
||||
return self._execute_queue()
|
||||
|
||||
@ -422,14 +421,14 @@ class OpenStackLiveDeployment(
|
||||
except Exception as e:
|
||||
return self._error(e)
|
||||
|
||||
def move_to_cache(self, newLevel: int) -> str:
|
||||
def move_to_cache(self, level: int) -> types.states.State:
|
||||
"""
|
||||
Moves machines between cache levels
|
||||
"""
|
||||
if Operation.REMOVE in self._queue:
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
if newLevel == self.L1_CACHE:
|
||||
if level == self.L1_CACHE:
|
||||
self._queue = [Operation.START, Operation.FINISH]
|
||||
else:
|
||||
self._queue = [Operation.START, Operation.SUSPEND, Operation.FINISH]
|
||||
@ -439,11 +438,11 @@ class OpenStackLiveDeployment(
|
||||
def error_reason(self) -> str:
|
||||
return self._reason
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
"""
|
||||
Invoked for destroying a deployed service
|
||||
"""
|
||||
self.__debug('destroy')
|
||||
self._debug('destroy')
|
||||
# If executing something, wait until finished to remove it
|
||||
# We simply replace the execution queue
|
||||
op = self._get_current_op()
|
||||
@ -457,12 +456,12 @@ class OpenStackLiveDeployment(
|
||||
|
||||
self._queue = [op, Operation.REMOVE, Operation.FINISH]
|
||||
# Do not execute anything.here, just continue normally
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
types.states.State.values RUNNING, FINISHED or ERROR.
|
||||
|
||||
This can be invoked directly by an administration or by the clean up
|
||||
of the deployed service (indirectly).
|
||||
@ -472,7 +471,7 @@ class OpenStackLiveDeployment(
|
||||
return self.destroy()
|
||||
|
||||
@staticmethod
|
||||
def __op2str(op: Operation) -> str:
|
||||
def _op2str(op: Operation) -> str:
|
||||
return {
|
||||
Operation.CREATE: 'create',
|
||||
Operation.START: 'start',
|
||||
@ -484,13 +483,13 @@ class OpenStackLiveDeployment(
|
||||
Operation.RETRY: 'retry',
|
||||
}.get(op, '????')
|
||||
|
||||
def __debug(self, txt: str) -> None:
|
||||
def _debug(self, txt: str) -> None:
|
||||
logger.debug(
|
||||
'State at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s',
|
||||
'types.states.State.at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s',
|
||||
txt,
|
||||
self._name,
|
||||
self._ip,
|
||||
self._mac,
|
||||
self._vmid,
|
||||
[OpenStackLiveDeployment.__op2str(op) for op in self._queue],
|
||||
[OpenStackLiveDeployment._op2str(op) for op in self._queue],
|
||||
)
|
||||
|
@ -35,7 +35,7 @@ import typing
|
||||
import collections.abc
|
||||
|
||||
from uds.core.services import Publication
|
||||
from uds.core.types.states import State
|
||||
from uds.core import types
|
||||
from uds.core.util import autoserializable
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
@ -84,7 +84,7 @@ class OpenStackLivePublication(Publication, autoserializable.AutoSerializable):
|
||||
|
||||
self.mark_for_upgrade() # This will force remarshalling
|
||||
|
||||
def publish(self) -> str:
|
||||
def publish(self) -> types.states.State:
|
||||
"""
|
||||
Realizes the publication of the service
|
||||
"""
|
||||
@ -102,19 +102,19 @@ class OpenStackLivePublication(Publication, autoserializable.AutoSerializable):
|
||||
except Exception as e:
|
||||
self._state = 'error'
|
||||
self._reason = 'Got error {}'.format(e)
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Checks state of publication creation
|
||||
"""
|
||||
if self._state == 'error':
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if self._state == 'available':
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
self._state = self.service().get_template(self._template_id)['status'] # For next check
|
||||
|
||||
@ -122,37 +122,37 @@ class OpenStackLivePublication(Publication, autoserializable.AutoSerializable):
|
||||
self._destroy_after = False
|
||||
return self.destroy()
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def error_reason(self) -> str:
|
||||
return self._reason
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
# We do not do anything else to destroy this instance of publication
|
||||
if self._state == 'error':
|
||||
return State.ERROR # Nothing to cancel
|
||||
return types.states.State.ERROR # Nothing to cancel
|
||||
|
||||
if self._state == 'creating':
|
||||
self._destroy_after = True
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
try:
|
||||
self.service().remove_template(self._template_id)
|
||||
except Exception as e:
|
||||
self._state = 'error'
|
||||
self._reason = str(e)
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
return self.destroy()
|
||||
|
||||
# Here ends the publication needed methods.
|
||||
# Methods provided below are specific for this publication
|
||||
# and will be used by user deployments that uses this kind of publication
|
||||
|
||||
def getTemplateId(self) -> str:
|
||||
def get_template_id(self) -> str:
|
||||
"""
|
||||
Returns the template id associated with the publication
|
||||
"""
|
||||
|
@ -36,8 +36,7 @@ import collections.abc
|
||||
|
||||
import dns.resolver
|
||||
|
||||
from uds.core import services
|
||||
from uds.core.types.states import State
|
||||
from uds.core import services, types
|
||||
from uds.core.util import net
|
||||
from uds.core.util import log, autoserializable, auto_attributes
|
||||
|
||||
@ -60,14 +59,14 @@ class OldIPSerialData(auto_attributes.AutoAttributes):
|
||||
auto_attributes.AutoAttributes.__init__(self, ip=str, reason=str, state=str)
|
||||
self._ip = ''
|
||||
self._reason = ''
|
||||
self._state = State.FINISHED
|
||||
self._state = types.states.State.FINISHED
|
||||
|
||||
class IPMachineUserService(services.UserService, autoserializable.AutoSerializable):
|
||||
suggested_delay = 10
|
||||
|
||||
_ip = autoserializable.StringField(default='')
|
||||
_reason = autoserializable.StringField(default='')
|
||||
_state = autoserializable.StringField(default=State.FINISHED)
|
||||
_state = autoserializable.StringField(default=types.states.State.FINISHED)
|
||||
|
||||
# Utility overrides for type checking...
|
||||
def service(self) -> 'IPServiceBase':
|
||||
@ -109,23 +108,23 @@ class IPMachineUserService(services.UserService, autoserializable.AutoSerializab
|
||||
# If multiple and has a ';' on IP, the values is IP;MAC
|
||||
return self._ip.replace('~', ':').split(';')[0]
|
||||
|
||||
def set_ready(self) -> str:
|
||||
def set_ready(self) -> types.states.State:
|
||||
# If single machine, ip is IP~counter,
|
||||
# If multiple and has a ';' on IP, the values is IP;MAC
|
||||
host = HostInfo.from_str(self._ip)
|
||||
if host.mac:
|
||||
self.service().wakeup(host)
|
||||
self._state = State.FINISHED
|
||||
self._state = types.states.State.FINISHED
|
||||
return self._state
|
||||
|
||||
def _deploy(self) -> str:
|
||||
def _deploy(self) -> types.states.State:
|
||||
ip = self.service().get_unassigned_host()
|
||||
if ip is None:
|
||||
self._reason = 'No machines left'
|
||||
self._state = State.ERROR
|
||||
self._state = types.states.State.ERROR
|
||||
else:
|
||||
self._ip = ip.as_identifier()
|
||||
self._state = State.FINISHED
|
||||
self._state = types.states.State.FINISHED
|
||||
|
||||
# If not to be managed by a token, autologin user
|
||||
if not self.service().get_token():
|
||||
@ -135,14 +134,14 @@ class IPMachineUserService(services.UserService, autoserializable.AutoSerializab
|
||||
|
||||
return self._state
|
||||
|
||||
def deploy_for_user(self, user: 'models.User') -> str:
|
||||
def deploy_for_user(self, user: 'models.User') -> types.states.State:
|
||||
logger.debug("Starting deploy of %s for user %s", self._ip, user)
|
||||
return self._deploy()
|
||||
|
||||
def assign(self, ip: str) -> str:
|
||||
logger.debug('Assigning from assignable with ip %s', ip)
|
||||
self._ip = ip
|
||||
self._state = State.FINISHED
|
||||
self._state = types.states.State.FINISHED
|
||||
if not self.service().get_token():
|
||||
dbService = self.db_obj()
|
||||
if dbService:
|
||||
@ -151,29 +150,29 @@ class IPMachineUserService(services.UserService, autoserializable.AutoSerializab
|
||||
return self._state
|
||||
|
||||
def error(self, reason: str) -> str:
|
||||
self._state = State.ERROR
|
||||
self._state = types.states.State.ERROR
|
||||
self._ip = ''
|
||||
self._reason = reason
|
||||
return self._state
|
||||
|
||||
def check_state(self) -> str:
|
||||
return self._state
|
||||
def check_state(self) -> types.states.State:
|
||||
return types.states.State.from_str(self._state)
|
||||
|
||||
def error_reason(self) -> str:
|
||||
"""
|
||||
If a publication produces an error, here we must notify the reason why it happened. This will be called just after
|
||||
publish or checkPublishingState if they return State.ERROR
|
||||
publish or checkPublishingState if they return types.states.State.ERROR
|
||||
"""
|
||||
return self._reason
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
host = HostInfo.from_str(self._ip)
|
||||
if host.host:
|
||||
self.service().unassign_host(host)
|
||||
self._state = State.FINISHED
|
||||
self._state = types.states.State.FINISHED
|
||||
return self._state
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
return self.destroy()
|
||||
|
||||
def unmarshal(self, data: bytes) -> None:
|
||||
|
@ -60,13 +60,10 @@ class IPMachinesService(IPServiceBase):
|
||||
# Gui
|
||||
token = gui.TextField(
|
||||
order=1,
|
||||
label=typing.cast(str, _('Service Token')),
|
||||
label=_('Service Token'),
|
||||
length=64,
|
||||
tooltip=typing.cast(
|
||||
str,
|
||||
_(
|
||||
'Service token that will be used by actors to communicate with service. Leave empty for persistent assignation.'
|
||||
),
|
||||
tooltip=_(
|
||||
'Service token that will be used by actors to communicate with service. Leave empty for persistent assignation.'
|
||||
),
|
||||
default='',
|
||||
required=False,
|
||||
@ -74,28 +71,26 @@ class IPMachinesService(IPServiceBase):
|
||||
)
|
||||
|
||||
list_of_hosts = gui.EditableListField(
|
||||
label=typing.cast(str, _('List of hosts')),
|
||||
tooltip=typing.cast(str, _('List of hosts available for this service')),
|
||||
label=_('List of hosts'),
|
||||
tooltip=_('List of hosts available for this service'),
|
||||
old_field_name='ipList',
|
||||
)
|
||||
|
||||
port = gui.NumericField(
|
||||
length=5,
|
||||
label=typing.cast(str, _('Check Port')),
|
||||
label=_('Check Port'),
|
||||
default=0,
|
||||
order=2,
|
||||
tooltip=typing.cast(
|
||||
str, _('If non zero, only hosts responding to connection on that port will be served.')
|
||||
),
|
||||
tooltip=_('If non zero, only hosts responding to connection on that port will be served.'),
|
||||
required=True,
|
||||
tab=types.ui.Tab.ADVANCED,
|
||||
)
|
||||
ignore_minutes_on_failure = gui.NumericField(
|
||||
length=6,
|
||||
label=typing.cast(str, _('Ignore minutes on failure')),
|
||||
label=_('Ignore minutes on failure'),
|
||||
default=0,
|
||||
order=2,
|
||||
tooltip=typing.cast(str, _('If a host fails to check, skip it for this time (in minutes).')),
|
||||
tooltip=_('If a host fails to check, skip it for this time (in minutes).'),
|
||||
min_value=0,
|
||||
required=True,
|
||||
tab=types.ui.Tab.ADVANCED,
|
||||
@ -104,33 +99,26 @@ class IPMachinesService(IPServiceBase):
|
||||
|
||||
max_session_hours = gui.NumericField(
|
||||
length=3,
|
||||
label=typing.cast(str, _('Max session duration')),
|
||||
label=_('Max session duration'),
|
||||
default=0,
|
||||
order=3,
|
||||
tooltip=typing.cast(
|
||||
str,
|
||||
_(
|
||||
'Max session duration before UDS releases a presumed locked machine (hours). 0 signifies "never".'
|
||||
),
|
||||
),
|
||||
tooltip=_('Max session duration before UDS releases a presumed locked machine (hours). 0 signifies "never".'),
|
||||
min_value=0,
|
||||
required=True,
|
||||
tab=types.ui.Tab.ADVANCED,
|
||||
old_field_name='maxSessionForMachine',
|
||||
)
|
||||
lock_on_external_access = gui.CheckBoxField(
|
||||
label=typing.cast(str, _('Lock machine by external access')),
|
||||
tooltip=typing.cast(str, _('If checked, UDS will lock the machine if it is accesed from outside UDS.')),
|
||||
label=_('Lock machine by external access'),
|
||||
tooltip=_('If checked, UDS will lock the machine if it is accessed from outside UDS.'),
|
||||
default=False,
|
||||
order=4,
|
||||
tab=types.ui.Tab.ADVANCED,
|
||||
old_field_name='lockByExternalAccess',
|
||||
)
|
||||
randomize_host = gui.CheckBoxField(
|
||||
label=typing.cast(str, _('Use random host')),
|
||||
tooltip=typing.cast(
|
||||
str, _('When enabled, UDS selects a random, rather than sequential, host from the list.')
|
||||
),
|
||||
label=_('Use random host'),
|
||||
tooltip=_('When enabled, UDS selects a random, rather than sequential, host from the list.'),
|
||||
default=False,
|
||||
order=5,
|
||||
tab=types.ui.Tab.ADVANCED,
|
||||
@ -138,9 +126,9 @@ class IPMachinesService(IPServiceBase):
|
||||
)
|
||||
|
||||
# Description of service
|
||||
type_name = typing.cast(str, _('Static Multiple IP'))
|
||||
type_name = _('Static Multiple IP')
|
||||
type_type = 'IPMachinesService'
|
||||
type_description = typing.cast(str, _('This service provides access to POWERED-ON Machines by IP'))
|
||||
type_description = _('This service provides access to POWERED-ON Machines by IP')
|
||||
icon_file = 'machines.png'
|
||||
|
||||
uses_cache = False # Cache are running machine awaiting to be assigned
|
||||
@ -187,7 +175,7 @@ class IPMachinesService(IPServiceBase):
|
||||
def hosts(self) -> typing.List['HostInfo']:
|
||||
if self._cached_hosts is None:
|
||||
d = self.storage.read_from_db('ips')
|
||||
hosts_list = pickle.loads(d) if d and isinstance(d, bytes) else [] # nosec: pickle is safe here
|
||||
hosts_list: list[str] = pickle.loads(d) if d and isinstance(d, bytes) else [] # nosec: pickle is safe here
|
||||
self._cached_hosts = IPMachinesService.compose_hosts_info(hosts_list)
|
||||
return self._cached_hosts
|
||||
|
||||
@ -329,18 +317,18 @@ class IPMachinesService(IPServiceBase):
|
||||
self,
|
||||
assignable_id: str,
|
||||
user: 'models.User',
|
||||
userDeployment: 'services.UserService',
|
||||
userservice_instance: 'services.UserService',
|
||||
) -> str:
|
||||
userservice_instance: IPMachineUserService = typing.cast(IPMachineUserService, userDeployment)
|
||||
ipmachine_instance: IPMachineUserService = typing.cast(IPMachineUserService, userservice_instance)
|
||||
host = HostInfo.from_str(assignable_id)
|
||||
|
||||
now = sql_stamp_seconds()
|
||||
locked = self.storage.get_unpickle(host.host)
|
||||
if self.is_usable(locked, now):
|
||||
self.storage.put_pickle(host.host, now)
|
||||
return userservice_instance.assign(host.as_identifier())
|
||||
return ipmachine_instance.assign(host.as_identifier())
|
||||
|
||||
return userservice_instance.error('IP already assigned')
|
||||
return ipmachine_instance.error('IP already assigned')
|
||||
|
||||
def process_login(self, id: str, remote_login: bool) -> None:
|
||||
'''
|
||||
|
@ -83,9 +83,12 @@ class IPSingleMachineService(IPServiceBase):
|
||||
return
|
||||
|
||||
if ';' in self.host.as_str():
|
||||
ip, mac = self.host.as_str().split(';')
|
||||
host, _mac = self.host.as_str().split(';')
|
||||
else:
|
||||
host = self.host.as_str()
|
||||
_mac = ''
|
||||
|
||||
if not net.is_valid_host(self.host.value):
|
||||
if not net.is_valid_host(host):
|
||||
raise exceptions.ui.ValidationError(
|
||||
gettext('Invalid server used: "{}"'.format(self.host.value))
|
||||
)
|
||||
|
@ -4,21 +4,21 @@ import re
|
||||
import typing
|
||||
import collections.abc
|
||||
|
||||
NETWORK_RE: typing.Final[typing.Pattern] = re.compile(r'([a-zA-Z0-9]+)=([^,]+)') # May have vla id at end
|
||||
NETWORK_RE: typing.Final[typing.Pattern[str]] = re.compile(r'([a-zA-Z0-9]+)=([^,]+)') # May have vla id at end
|
||||
|
||||
# Conversor from dictionary to NamedTuple
|
||||
CONVERSORS: typing.Final[collections.abc.MutableMapping[typing.Any, collections.abc.Callable]] = {
|
||||
CONVERSORS: typing.Final[dict[typing.Any, collections.abc.Callable[[typing.Type[typing.Any]], typing.Any]]] = {
|
||||
str: lambda x: str(x or ''),
|
||||
typing.Optional[str]: lambda x: str(x) if x is not None else None,
|
||||
typing.Optional[str]: lambda x: str(x) if x is not None else None, # pyright: ignore
|
||||
bool: lambda x: bool(x),
|
||||
typing.Optional[bool]: lambda x: bool(x) if x is not None else None,
|
||||
int: lambda x: int(x or '0'),
|
||||
typing.Optional[int]: lambda x: int(x or '0') if x is not None else None,
|
||||
float: lambda x: float(x or '0'),
|
||||
typing.Optional[float]: lambda x: float(x or '0') if x is not None else None,
|
||||
datetime.datetime: lambda x: datetime.datetime.fromtimestamp(int(x)),
|
||||
typing.Optional[bool]: lambda x: bool(x) if x is not None else None, # pyright: ignore
|
||||
int: lambda x: int(x or '0'), # pyright: ignore
|
||||
typing.Optional[int]: lambda x: int(x or '0') if x is not None else None, # pyright: ignore
|
||||
float: lambda x: float(x or '0'), # pyright: ignore
|
||||
typing.Optional[float]: lambda x: float(x or '0') if x is not None else None, # pyright: ignore
|
||||
datetime.datetime: lambda x: datetime.datetime.fromtimestamp(int(x)), # pyright: ignore
|
||||
typing.Optional[datetime.datetime]: lambda x: (
|
||||
datetime.datetime.fromtimestamp(int(x)) if x is not None else None
|
||||
datetime.datetime.fromtimestamp(int(x)) if x is not None else None # pyright: ignore
|
||||
),
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,6 @@ import collections.abc
|
||||
|
||||
from uds.core import services, consts, types
|
||||
from uds.core.managers.userservice import UserServiceManager
|
||||
from uds.core.types.states import State
|
||||
from uds.core.util import log, autoserializable
|
||||
from uds.core.util.model import sql_stamp_seconds
|
||||
|
||||
@ -183,9 +182,9 @@ class ProxmoxUserserviceLinked(services.UserService, autoserializable.AutoSerial
|
||||
def get_ip(self) -> str:
|
||||
return self._ip
|
||||
|
||||
def set_ready(self) -> str:
|
||||
def set_ready(self) -> types.states.State:
|
||||
if self.cache.get('ready') == '1':
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
try:
|
||||
vmInfo = self.service().get_machine_info(int(self._vmid))
|
||||
@ -199,7 +198,7 @@ class ProxmoxUserserviceLinked(services.UserService, autoserializable.AutoSerial
|
||||
return self._execute_queue()
|
||||
|
||||
self.cache.put('ready', '1')
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def reset(self) -> None:
|
||||
"""
|
||||
@ -236,7 +235,7 @@ if sys.platform == 'win32':
|
||||
except Exception as e:
|
||||
logger.info('Exception sending loggin to %s: %s', dbService, e)
|
||||
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> str:
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> types.states.State:
|
||||
# Here we will check for suspending the VM (when full ready)
|
||||
logger.debug('Checking if cache 2 for %s', self._name)
|
||||
if self._get_current_op() == Operation.WAIT:
|
||||
@ -244,9 +243,9 @@ if sys.platform == 'win32':
|
||||
self._pop_current_op() # Remove current state
|
||||
return self._execute_queue()
|
||||
# Do not need to go to level 2 (opWait is in fact "waiting for moving machine to cache level 2)
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def deploy_for_user(self, user: 'models.User') -> str:
|
||||
def deploy_for_user(self, user: 'models.User') -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for an user.
|
||||
"""
|
||||
@ -254,11 +253,11 @@ if sys.platform == 'win32':
|
||||
self._init_queue_for_deploy(False)
|
||||
return self._execute_queue()
|
||||
|
||||
def deploy_for_cache(self, cacheLevel: int) -> str:
|
||||
def deploy_for_cache(self, level: int) -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for cache
|
||||
"""
|
||||
self._init_queue_for_deploy(cacheLevel == self.L2_CACHE)
|
||||
self._init_queue_for_deploy(level == self.L2_CACHE)
|
||||
return self._execute_queue()
|
||||
|
||||
def _init_queue_for_deploy(self, forLevel2: bool = False) -> None:
|
||||
@ -299,14 +298,14 @@ if sys.platform == 'win32':
|
||||
|
||||
def _retry_later(self) -> str:
|
||||
self._push_front_op(Operation.RETRY)
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _error(self, reason: typing.Union[str, Exception]) -> str:
|
||||
def _error(self, reason: typing.Union[str, Exception]) -> types.states.State:
|
||||
"""
|
||||
Internal method to set object as error state
|
||||
|
||||
Returns:
|
||||
State.ERROR, so we can do "return self.__error(reason)"
|
||||
types.states.State.ERROR, so we can do "return self.__error(reason)"
|
||||
"""
|
||||
reason = str(reason)
|
||||
logger.debug('Setting error state, reason: %s', reason)
|
||||
@ -317,17 +316,17 @@ if sys.platform == 'win32':
|
||||
|
||||
self._queue = [Operation.ERROR]
|
||||
self._reason = reason
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
def _execute_queue(self) -> str:
|
||||
def _execute_queue(self) -> types.states.State:
|
||||
self._debug('executeQueue')
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: collections.abc.Mapping[Operation, typing.Optional[collections.abc.Callable[[], None]]] = {
|
||||
Operation.CREATE: self._create,
|
||||
@ -349,7 +348,7 @@ if sys.platform == 'win32':
|
||||
|
||||
operation_executor()
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
except Exception as e:
|
||||
return self._error(e)
|
||||
|
||||
@ -358,17 +357,17 @@ if sys.platform == 'win32':
|
||||
"""
|
||||
Used to retry an operation
|
||||
In fact, this will not be never invoked, unless we push it twice, because
|
||||
check_state method will "pop" first item when a check operation returns State.FINISHED
|
||||
check_state method will "pop" first item when a check operation returns types.states.State.FINISHED
|
||||
|
||||
At executeQueue this return value will be ignored, and it will only be used at check_state
|
||||
"""
|
||||
pass
|
||||
|
||||
def _retry_checker(self) -> str:
|
||||
def _retry_checker(self) -> types.states.State:
|
||||
"""
|
||||
This method is not used, because retry operation is never used
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _wait(self) -> None:
|
||||
"""
|
||||
@ -376,11 +375,11 @@ if sys.platform == 'win32':
|
||||
"""
|
||||
pass
|
||||
|
||||
def _wait_checker(self) -> str:
|
||||
def _wait_checker(self) -> types.states.State:
|
||||
"""
|
||||
This method is not used, because wait operation is never used
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _create(self) -> None:
|
||||
"""
|
||||
@ -489,50 +488,50 @@ if sys.platform == 'win32':
|
||||
raise Exception(f'Error setting MAC and HA on proxmox: {e}') from e
|
||||
|
||||
# Check methods
|
||||
def _check_task_finished(self) -> str:
|
||||
def _check_task_finished(self) -> types.states.State:
|
||||
if self._task == '':
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
node, upid = self._retrieve_task()
|
||||
|
||||
try:
|
||||
task = self.service().provider().get_task_info(node, upid)
|
||||
except client.ProxmoxConnectionError:
|
||||
return State.RUNNING # Try again later
|
||||
return types.states.State.RUNNING # Try again later
|
||||
|
||||
if task.is_errored():
|
||||
return self._error(task.exitstatus)
|
||||
|
||||
if task.is_completed():
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _create_checker(self) -> str:
|
||||
def _create_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks the state of a deploy for an user or cache
|
||||
"""
|
||||
return self._check_task_finished()
|
||||
|
||||
def _start_checker(self) -> str:
|
||||
def _start_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if machine has started
|
||||
"""
|
||||
return self._check_task_finished()
|
||||
|
||||
def _stop_checker(self) -> str:
|
||||
def _stop_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if machine has stoped
|
||||
"""
|
||||
return self._check_task_finished()
|
||||
|
||||
def _shutdown_checker(self) -> str:
|
||||
def _shutdown_checker(self) -> types.states.State:
|
||||
"""
|
||||
Check if the machine has suspended
|
||||
"""
|
||||
return self._check_task_finished()
|
||||
|
||||
def _graceful_stop_checker(self) -> str:
|
||||
def _graceful_stop_checker(self) -> types.states.State:
|
||||
"""
|
||||
Check if the machine has gracely stopped (timed shutdown)
|
||||
"""
|
||||
@ -541,7 +540,7 @@ if sys.platform == 'win32':
|
||||
if shutdown_start < 0: # Was already stopped
|
||||
# Machine is already stop
|
||||
logger.debug('Machine WAS stopped')
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
if shutdown_start == 0: # Was shut down a las bravas
|
||||
logger.debug('Macine DO NOT HAVE guest tools')
|
||||
@ -550,7 +549,7 @@ if sys.platform == 'win32':
|
||||
logger.debug('Checking State')
|
||||
# Check if machine is already stopped
|
||||
if self.service().get_machine_info(int(self._vmid)).status == 'stopped':
|
||||
return State.FINISHED # It's stopped
|
||||
return types.states.State.FINISHED # It's stopped
|
||||
|
||||
logger.debug('State is running')
|
||||
if sql_stamp_seconds() - shutdown_start > consts.os.MAX_GUEST_SHUTDOWN_WAIT:
|
||||
@ -563,23 +562,23 @@ if sys.platform == 'win32':
|
||||
self.storage.put_pickle('shutdown', 0)
|
||||
self._stop_machine() # Launch "hard" stop
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _remove_checker(self) -> str:
|
||||
def _remove_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if a machine has been removed
|
||||
"""
|
||||
return self._check_task_finished()
|
||||
|
||||
def _mac_checker(self) -> str:
|
||||
def _mac_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if change mac operation has finished.
|
||||
|
||||
Changing nic configuration is 1-step operation, so when we check it here, it is already done
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Check what operation is going on, and acts acordly to it
|
||||
"""
|
||||
@ -587,12 +586,12 @@ if sys.platform == 'win32':
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: dict[Operation, typing.Optional[collections.abc.Callable[[], str]]] = {
|
||||
fncs: dict[Operation, typing.Optional[collections.abc.Callable[[], types.states.State]]] = {
|
||||
Operation.CREATE: self._create_checker,
|
||||
Operation.RETRY: self._retry_checker,
|
||||
Operation.WAIT: self._wait_checker,
|
||||
@ -605,7 +604,7 @@ if sys.platform == 'win32':
|
||||
}
|
||||
|
||||
try:
|
||||
operation_checker: typing.Optional[typing.Optional[collections.abc.Callable[[], str]]] = fncs.get(
|
||||
operation_checker: typing.Optional[typing.Optional[collections.abc.Callable[[], types.states.State]]] = fncs.get(
|
||||
op, None
|
||||
)
|
||||
|
||||
@ -613,7 +612,7 @@ if sys.platform == 'win32':
|
||||
return self._error(f'Unknown operation found at check queue ({op})')
|
||||
|
||||
state = operation_checker()
|
||||
if state == State.FINISHED:
|
||||
if state == types.states.State.FINISHED:
|
||||
self._pop_current_op() # Remove runing op
|
||||
return self._execute_queue()
|
||||
|
||||
@ -621,14 +620,14 @@ if sys.platform == 'win32':
|
||||
except Exception as e:
|
||||
return self._error(e)
|
||||
|
||||
def move_to_cache(self, newLevel: int) -> str:
|
||||
def move_to_cache(self, level: int) -> types.states.State:
|
||||
"""
|
||||
Moves machines between cache levels
|
||||
"""
|
||||
if Operation.REMOVE in self._queue:
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
if newLevel == self.L1_CACHE:
|
||||
if level == self.L1_CACHE:
|
||||
self._queue = [Operation.START, Operation.FINISH]
|
||||
else:
|
||||
self._queue = [Operation.START, Operation.SHUTDOWN, Operation.FINISH]
|
||||
@ -645,7 +644,7 @@ if sys.platform == 'win32':
|
||||
"""
|
||||
return self._reason
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
"""
|
||||
Invoked for destroying a deployed service
|
||||
"""
|
||||
@ -653,7 +652,7 @@ if sys.platform == 'win32':
|
||||
if self._vmid == '':
|
||||
self._queue = []
|
||||
self._reason = "canceled"
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
# If executing something, wait until finished to remove it
|
||||
# We simply replace the execution queue
|
||||
@ -671,9 +670,9 @@ if sys.platform == 'win32':
|
||||
|
||||
self._queue = [op] + queue
|
||||
# Do not execute anything.here, just continue normally
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
@ -701,7 +700,7 @@ if sys.platform == 'win32':
|
||||
Operation.GET_MAC: 'getting mac',
|
||||
}.get(op, '????')
|
||||
|
||||
def _debug(self, txt):
|
||||
def _debug(self, txt: str) -> None:
|
||||
logger.debug(
|
||||
'State at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s',
|
||||
txt,
|
||||
|
@ -35,9 +35,8 @@ import typing
|
||||
import collections.abc
|
||||
|
||||
from django.utils.translation import gettext as _
|
||||
from uds.core import services
|
||||
from uds.core import services, types
|
||||
from uds.core.util import autoserializable
|
||||
from uds.core.types.states import State
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
@ -48,9 +47,9 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ProxmoxPublication(services.Publication, autoserializable.AutoSerializable):
|
||||
|
||||
|
||||
suggested_delay = 20
|
||||
|
||||
|
||||
_name = autoserializable.StringField(default='')
|
||||
_vmid = autoserializable.StringField(default='')
|
||||
_task = autoserializable.StringField(default='')
|
||||
@ -69,7 +68,7 @@ class ProxmoxPublication(services.Publication, autoserializable.AutoSerializable
|
||||
"""
|
||||
if not data.startswith(b'v'):
|
||||
return super().unmarshal(data)
|
||||
|
||||
|
||||
logger.debug('Data: %s', data)
|
||||
vals = data.decode('utf8').split('\t')
|
||||
if vals[0] == 'v1':
|
||||
@ -84,69 +83,58 @@ class ProxmoxPublication(services.Publication, autoserializable.AutoSerializable
|
||||
) = vals[1:]
|
||||
else:
|
||||
raise ValueError('Invalid data format')
|
||||
|
||||
|
||||
self._destroy_after = destroy_after != ''
|
||||
|
||||
|
||||
self.mark_for_upgrade() # Flag so manager can save it again with new format
|
||||
|
||||
def publish(self) -> str:
|
||||
def publish(self) -> types.states.State:
|
||||
"""
|
||||
If no space is available, publication will fail with an error
|
||||
"""
|
||||
try:
|
||||
# First we should create a full clone, so base machine do not get fullfilled with "garbage" delta disks...
|
||||
self._name = (
|
||||
'UDS '
|
||||
+ _('Publication')
|
||||
+ ' '
|
||||
+ self.servicepool_name()
|
||||
+ "-"
|
||||
+ str(self.revision())
|
||||
)
|
||||
self._name = 'UDS ' + _('Publication') + ' ' + self.servicepool_name() + "-" + str(self.revision())
|
||||
comments = _('UDS Publication for {0} created at {1}').format(
|
||||
self.servicepool_name(), str(datetime.now()).split('.')[0]
|
||||
)
|
||||
task = self.service().clone_machine(self._name, comments)
|
||||
self._vmid = str(task.vmid)
|
||||
self._task = ','.join((task.upid.node, task.upid.upid))
|
||||
self._state = State.RUNNING
|
||||
self._state = types.states.State.RUNNING
|
||||
self._operation = 'p' # Publishing
|
||||
self._destroy_after = False
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
except Exception as e:
|
||||
logger.exception('Caught exception %s', e)
|
||||
self._reason = str(e)
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
def check_state(
|
||||
self,
|
||||
) -> str: # pylint: disable = too-many-branches,too-many-return-statements
|
||||
if self._state != State.RUNNING:
|
||||
return self._state
|
||||
def check_state(self) -> types.states.State:
|
||||
if self._state != types.states.State.RUNNING:
|
||||
return types.states.State.from_str(self._state)
|
||||
node, upid = self._task.split(',')
|
||||
try:
|
||||
task = self.service().provider().get_task_info(node, upid)
|
||||
if task.is_running():
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
except Exception as e:
|
||||
logger.exception('Proxmox publication')
|
||||
self._state = State.ERROR
|
||||
self._state = types.states.State.ERROR
|
||||
self._reason = str(e)
|
||||
return self._state
|
||||
|
||||
if task.is_errored():
|
||||
self._reason = task.exitstatus
|
||||
self._state = State.ERROR
|
||||
self._state = types.states.State.ERROR
|
||||
else: # Finished
|
||||
if self._destroy_after:
|
||||
return self.destroy()
|
||||
self._state = State.FINISHED
|
||||
self._state = types.states.State.FINISHED
|
||||
if self._operation == 'p': # not Destroying
|
||||
# Disable Protection (removal)
|
||||
self.service().provider().set_protection(int(self._vmid), protection=False)
|
||||
time.sleep(
|
||||
0.5
|
||||
) # Give some tome to proxmox. We have observed some concurrency issues
|
||||
time.sleep(0.5) # Give some tome to proxmox. We have observed some concurrency issues
|
||||
# And add it to HA if
|
||||
self.service().enable_machine_ha(int(self._vmid))
|
||||
time.sleep(0.5)
|
||||
@ -163,26 +151,30 @@ class ProxmoxPublication(services.Publication, autoserializable.AutoSerializable
|
||||
self._task = ''
|
||||
self._destroy_after = False
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
if (
|
||||
self._state == State.RUNNING and self._destroy_after is False
|
||||
self._state == types.states.State.RUNNING and self._destroy_after is False
|
||||
): # If called destroy twice, will BREAK STOP publication
|
||||
self._destroy_after = True
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
self._state = State.RUNNING
|
||||
self._state = types.states.State.RUNNING
|
||||
self._operation = 'd'
|
||||
self._destroy_after = False
|
||||
try:
|
||||
task = self.service().remove_machine(self.machine())
|
||||
self._task = ','.join((task.node, task.upid))
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
except Exception as e:
|
||||
self._reason = str(e) # Store reason of error
|
||||
logger.warning('Problem destroying publication %s: %s. Please, check machine state On Proxmox', self.machine(), e)
|
||||
return State.ERROR
|
||||
logger.warning(
|
||||
'Problem destroying publication %s: %s. Please, check machine state On Proxmox',
|
||||
self.machine(),
|
||||
e,
|
||||
)
|
||||
return types.states.State.ERROR
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
return self.destroy()
|
||||
|
||||
def error_reason(self) -> str:
|
||||
|
@ -34,8 +34,7 @@ import logging
|
||||
import typing
|
||||
import collections.abc
|
||||
|
||||
from uds.core import services
|
||||
from uds.core.types.states import State
|
||||
from uds.core import services, types
|
||||
|
||||
from . import service as sample_service
|
||||
|
||||
@ -118,7 +117,7 @@ class SampleUserServiceOne(services.UserService):
|
||||
generate more names. (Generator are simple utility classes)
|
||||
"""
|
||||
name: str = typing.cast(str, self.storage.read_from_db('name'))
|
||||
if name is None:
|
||||
if not name:
|
||||
name = self.name_generator().get(
|
||||
self.service().get_basename() + '-' + self.service().getColour(), 3
|
||||
)
|
||||
@ -152,7 +151,7 @@ class SampleUserServiceOne(services.UserService):
|
||||
to use to get an unused mac.
|
||||
"""
|
||||
mac = typing.cast(str, self.storage.read_from_db('mac'))
|
||||
if mac is None:
|
||||
if not mac:
|
||||
mac = self.mac_generator().get('00:00:00:00:00:00-00:FF:FF:FF:FF:FF')
|
||||
self.storage.save_to_db('mac', mac)
|
||||
return mac
|
||||
@ -176,11 +175,11 @@ class SampleUserServiceOne(services.UserService):
|
||||
|
||||
"""
|
||||
ip = typing.cast(str, self.storage.read_from_db('ip'))
|
||||
if ip is None:
|
||||
if not ip:
|
||||
ip = '192.168.0.34' # Sample IP for testing purposses only
|
||||
return ip
|
||||
|
||||
def set_ready(self) -> str:
|
||||
def set_ready(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the expected return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
@ -198,22 +197,22 @@ class SampleUserServiceOne(services.UserService):
|
||||
|
||||
This method, in this case, will check the state of the machine, and if
|
||||
it is "ready", that is, powered on and accesible, it will return
|
||||
"State.FINISHED". If the machine is not accesible (has ben erased, for
|
||||
example), it will return "State.ERROR" and store a reason of error so UDS
|
||||
"types.states.State.FINISHED". If the machine is not accesible (has ben erased, for
|
||||
example), it will return "types.states.State.ERROR" and store a reason of error so UDS
|
||||
can ask for it and present this information to the Administrator.
|
||||
|
||||
If the machine powered off, or suspended, or any other state that is not
|
||||
directly usable but can be put in an usable state, it will return
|
||||
"State.RUNNING", and core will use check_state to see when the operation
|
||||
"types.states.State.RUNNING", and core will use check_state to see when the operation
|
||||
has finished.
|
||||
|
||||
I hope this sample is enough to explain the use of this method..
|
||||
"""
|
||||
|
||||
# In our case, the service is always ready
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def deploy_for_user(self, user: 'models.User') -> str:
|
||||
def deploy_for_user(self, user: 'models.User') -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for an user.
|
||||
|
||||
@ -232,9 +231,9 @@ class SampleUserServiceOne(services.UserService):
|
||||
|
||||
If the service gets created in "one step", that is, before the return
|
||||
of this method, the consumable service for the user gets created, it
|
||||
will return "State.FINISH".
|
||||
will return "types.states.State.FINISH".
|
||||
If the service needs more steps (as in this case), we will return
|
||||
"State.RUNNING", and if it has an error, it wil return "State.ERROR" and
|
||||
"types.states.State.RUNNING", and if it has an error, it wil return "types.states.State.ERROR" and
|
||||
store an error string so administration interface can show it.
|
||||
|
||||
We do not use user for anything, as in most cases will be.
|
||||
@ -246,11 +245,11 @@ class SampleUserServiceOne(services.UserService):
|
||||
# random fail
|
||||
if random.randint(0, 9) == 9: # nosec: just testing values
|
||||
self.storage.save_to_db('error', 'Random error at deployForUser :-)')
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Our deployForUser method will initiate the consumable service deployment,
|
||||
but will not finish it.
|
||||
@ -259,8 +258,8 @@ class SampleUserServiceOne(services.UserService):
|
||||
return that we have finished, else we will return that we are working
|
||||
on it.
|
||||
|
||||
One deployForUser returns State.RUNNING, this task will get called until
|
||||
check_state returns State.FINISHED.
|
||||
One deployForUser returns types.states.State.RUNNING, this task will get called until
|
||||
check_state returns types.states.State.FINISHED.
|
||||
|
||||
Also, we will make the publication fail one of every 10 calls to this
|
||||
method.
|
||||
@ -284,15 +283,15 @@ class SampleUserServiceOne(services.UserService):
|
||||
# In our sample, we only use check_state in case of deployForUser,
|
||||
# so at first call count will be 0.
|
||||
if count >= 5:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
# random fail
|
||||
if random.randint(0, 9) == 9: # nosec: just testing values
|
||||
self.storage.save_to_db('error', 'Random error at check_state :-)')
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
self.storage.save_to_db('count', str(count))
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def finish(self) -> None:
|
||||
"""
|
||||
@ -324,7 +323,7 @@ class SampleUserServiceOne(services.UserService):
|
||||
# We store the value at storage, but never get used, just an example
|
||||
self.storage.save_to_db('user', username)
|
||||
|
||||
def user_logged_out(self, username) -> None:
|
||||
def user_logged_out(self, username: str) -> None:
|
||||
"""
|
||||
This method must be available so os managers can invoke it whenever
|
||||
an user get logged out if a service.
|
||||
@ -351,18 +350,18 @@ class SampleUserServiceOne(services.UserService):
|
||||
"""
|
||||
return typing.cast(str, self.storage.read_from_db('error')) or 'No error'
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
|
||||
Invoked for destroying a deployed service
|
||||
Do whatever needed here, as deleting associated data if needed (i.e. a copy of the machine, snapshots, etc...)
|
||||
@return: State.FINISHED if no more checks/steps for deployment are needed, State.RUNNING if more steps are needed (steps checked using check_state)
|
||||
@return: types.states.State.FINISHED if no more checks/steps for deployment are needed, types.states.State.RUNNING if more steps are needed (steps checked using check_state)
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
@ -372,4 +371,4 @@ class SampleUserServiceOne(services.UserService):
|
||||
When administrator requests it, the cancel is "delayed" and not
|
||||
invoked directly.
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
@ -35,8 +35,7 @@ import logging
|
||||
import typing
|
||||
import collections.abc
|
||||
|
||||
from uds.core import services
|
||||
from uds.core.types.states import State
|
||||
from uds.core import services, types
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
@ -227,7 +226,7 @@ class SampleUserServiceTwo(services.UserService):
|
||||
return '192.168.0.34' # Sample IP for testing purposes only
|
||||
return self._ip
|
||||
|
||||
def set_ready(self) -> str:
|
||||
def set_ready(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
@ -245,22 +244,22 @@ class SampleUserServiceTwo(services.UserService):
|
||||
|
||||
This method, in this case, will check the state of the machine, and if
|
||||
it is "ready", that is, powered on and accessible, it will return
|
||||
"State.FINISHED". If the machine is not accessible (has been erased, for
|
||||
example), it will return "State.ERROR" and store a reason of error so UDS
|
||||
"types.states.State.FINISHED". If the machine is not accessible (has been erased, for
|
||||
example), it will return "types.states.State.ERROR" and store a reason of error so UDS
|
||||
can ask for it and present this information to the Administrator.
|
||||
|
||||
If the machine powered off, or suspended, or any other state that is not
|
||||
directly usable but can be put in an usable state, it will return
|
||||
"State.RUNNING", and core will use check_state to see when the operation
|
||||
"types.states.State.RUNNING", and core will use check_state to see when the operation
|
||||
has finished.
|
||||
|
||||
I hope this sample is enough to explain the use of this method..
|
||||
"""
|
||||
|
||||
# In our case, the service is always ready
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def deploy_for_user(self, user: 'models.User') -> str:
|
||||
def deploy_for_user(self, user: 'models.User') -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for an user.
|
||||
|
||||
@ -279,9 +278,9 @@ class SampleUserServiceTwo(services.UserService):
|
||||
|
||||
If the service gets created in "one step", that is, before the return
|
||||
of this method, the consumable service for the user gets created, it
|
||||
will return "State.FINISH".
|
||||
will return "types.states.State.FINISH".
|
||||
If the service needs more steps (as in this case), we will return
|
||||
"State.RUNNING", and if it has an error, it wil return "State.ERROR" and
|
||||
"types.states.State.RUNNING", and if it has an error, it wil return "types.states.State.ERROR" and
|
||||
store an error string so administration interface can show it.
|
||||
|
||||
We do not use user for anything, as in most cases will be.
|
||||
@ -295,11 +294,11 @@ class SampleUserServiceTwo(services.UserService):
|
||||
# Note that we can mark this string as translatable, and return
|
||||
# it translated at error_reason method
|
||||
self._error = 'Random error at deployForUser :-)'
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def deploy_for_cache(self, cacheLevel: int) -> str:
|
||||
def deploy_for_cache(self, level: int) -> types.states.State:
|
||||
"""
|
||||
Deploys a user deployment as cache.
|
||||
|
||||
@ -316,9 +315,9 @@ class SampleUserServiceTwo(services.UserService):
|
||||
cache level (L1, L2) is the deployment
|
||||
"""
|
||||
self._count = 0
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Our deployForUser method will initiate the consumable service deployment,
|
||||
but will not finish it.
|
||||
@ -327,8 +326,8 @@ class SampleUserServiceTwo(services.UserService):
|
||||
return that we have finished, else we will return that we are working
|
||||
on it.
|
||||
|
||||
One deployForUser returns State.RUNNING, this task will get called until
|
||||
check_state returns State.FINISHED.
|
||||
One deployForUser returns types.states.State.RUNNING, this task will get called until
|
||||
check_state returns types.states.State.FINISHED.
|
||||
|
||||
Also, we will make the user deployment fail one of every 10 calls to this
|
||||
method.
|
||||
@ -350,14 +349,14 @@ class SampleUserServiceTwo(services.UserService):
|
||||
# In our sample, we only use check_state in case of deployForUser,
|
||||
# so at first call count will be 0.
|
||||
if self._count >= 5:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
# random fail
|
||||
if random.randint(0, 9) == 9: # nosec: just testing values
|
||||
self._error = 'Random error at check_state :-)'
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def finish(self):
|
||||
"""
|
||||
@ -390,7 +389,7 @@ class SampleUserServiceTwo(services.UserService):
|
||||
# We store the value at storage, but never get used, just an example
|
||||
self.storage.save_to_db('user', username)
|
||||
|
||||
def user_logged_out(self, username) -> None:
|
||||
def user_logged_out(self, username: str) -> None:
|
||||
"""
|
||||
This method must be available so os managers can invoke it whenever
|
||||
an user get logged out if a service.
|
||||
@ -422,18 +421,18 @@ class SampleUserServiceTwo(services.UserService):
|
||||
"""
|
||||
return self._error
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
|
||||
Invoked for destroying a deployed service
|
||||
Do whatever needed here, as deleting associated data if needed (i.e. a copy of the machine, snapshots, etc...)
|
||||
@return: State.FINISHED if no more checks/steps for deployment are needed, State.RUNNING if more steps are needed (steps checked using check_state)
|
||||
@return: types.states.State.FINISHED if no more checks/steps for deployment are needed, types.states.State.RUNNING if more steps are needed (steps checked using check_state)
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
"""
|
||||
This is a task method. As that, the excepted return values are
|
||||
State values RUNNING, FINISHED or ERROR.
|
||||
@ -443,4 +442,4 @@ class SampleUserServiceTwo(services.UserService):
|
||||
When administrator requests it, the cancel is "delayed" and not
|
||||
invoked directly.
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
@ -37,8 +37,7 @@ import typing
|
||||
import collections.abc
|
||||
|
||||
from django.utils.translation import gettext as _
|
||||
from uds.core import services
|
||||
from uds.core.types.states import State
|
||||
from uds.core import services, types
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -123,7 +122,7 @@ class SamplePublication(services.Publication):
|
||||
self._reason = vals[1]
|
||||
self._number = int(vals[2])
|
||||
|
||||
def publish(self) -> str:
|
||||
def publish(self) -> types.states.State:
|
||||
"""
|
||||
This method is invoked whenever the administrator requests a new publication.
|
||||
|
||||
@ -135,7 +134,7 @@ class SamplePublication(services.Publication):
|
||||
All publications can be synchronous or asynchronous.
|
||||
|
||||
The main difference between both is that first do whatever needed, (the
|
||||
action must be fast enough to do not block core), returning State.FINISHED.
|
||||
action must be fast enough to do not block core), returning types.states.State.FINISHED.
|
||||
|
||||
The second (asynchronous) are publications that could block the core, so
|
||||
it have to be done in more than one step.
|
||||
@ -144,7 +143,7 @@ class SamplePublication(services.Publication):
|
||||
* First we invoke the copy operation to virtualization provider
|
||||
* Second, we kept needed values inside instance so we can serialize
|
||||
them whenever requested
|
||||
* Returns an State.RUNNING, indicating the core that the publication
|
||||
* Returns an types.states.State.RUNNING, indicating the core that the publication
|
||||
has started but has to finish sometime later. (We do no check
|
||||
again the state and keep waiting here, because we will block the
|
||||
core untill this operation is finished).
|
||||
@ -157,7 +156,7 @@ class SamplePublication(services.Publication):
|
||||
finish at publish call but a later check_state call
|
||||
|
||||
Take care with instantiating threads from here. Whenever a publish returns
|
||||
"State.RUNNING", the core will recheck it later, but not using this instance
|
||||
"types.states.State.RUNNING", the core will recheck it later, but not using this instance
|
||||
and maybe that even do not use this server.
|
||||
|
||||
If you want to use threadings or somethin likt it, use DelayedTasks and
|
||||
@ -178,17 +177,17 @@ class SamplePublication(services.Publication):
|
||||
"""
|
||||
self._number = 5
|
||||
self._reason = ''
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Our publish method will initiate publication, but will not finish it.
|
||||
So in our sample, wi will only check if _number reaches 0, and if so
|
||||
return that we have finished, else we will return that we are working
|
||||
on it.
|
||||
|
||||
One publish returns State.RUNNING, this task will get called untill
|
||||
check_state returns State.FINISHED.
|
||||
One publish returns types.states.State.RUNNING, this task will get called untill
|
||||
check_state returns types.states.State.FINISHED.
|
||||
|
||||
Also, wi will make the publication fail one of every 10 calls to this
|
||||
method.
|
||||
@ -204,11 +203,11 @@ class SamplePublication(services.Publication):
|
||||
# One of every 10 calls
|
||||
if random.randint(0, 9) == 9:
|
||||
self._reason = _('Random integer was 9!!! :-)')
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if self._number <= 0:
|
||||
return State.FINISHED
|
||||
return State.RUNNING
|
||||
return types.states.State.FINISHED
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def finish(self) -> None:
|
||||
"""
|
||||
@ -228,13 +227,13 @@ class SamplePublication(services.Publication):
|
||||
"""
|
||||
If a publication produces an error, here we must notify the reason why
|
||||
it happened. This will be called just after publish or check_state
|
||||
if they return State.ERROR
|
||||
if they return types.states.State.ERROR
|
||||
|
||||
Returns an string, in our case, set at check_state
|
||||
"""
|
||||
return self._reason
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
"""
|
||||
This is called once a publication is no more needed.
|
||||
|
||||
@ -242,16 +241,16 @@ class SamplePublication(services.Publication):
|
||||
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.
|
||||
The retunred value is the same as when publishing, types.states.State.RUNNING,
|
||||
types.states.State.FINISHED or types.states.State.ERROR.
|
||||
"""
|
||||
self._name = ''
|
||||
self._reason = '' # In fact, this is not needed, but cleaning up things... :-)
|
||||
|
||||
# We do not do anything else to destroy this instance of publication
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
"""
|
||||
Invoked for canceling the current operation.
|
||||
This can be invoked directly by an administration or by the clean up
|
||||
|
@ -35,7 +35,7 @@ import typing
|
||||
import collections.abc
|
||||
|
||||
from django.utils.translation import gettext_noop as _
|
||||
from uds.core import services, exceptions
|
||||
from uds.core import services, exceptions, types
|
||||
from uds.core.ui import gui
|
||||
|
||||
from .publication import SamplePublication
|
||||
@ -226,16 +226,6 @@ class ServiceTwo(services.Service):
|
||||
# Gui, we will use here the EditableList field
|
||||
names = gui.EditableListField(label=_('List of names'))
|
||||
|
||||
def __init__(self, environment, parent, values=None):
|
||||
"""
|
||||
We here can get a HUGE list from client.
|
||||
Right now, this is treated same as other fields, in a near
|
||||
future we will se how to handle this better
|
||||
"""
|
||||
super(ServiceTwo, self).__init__(environment, parent, values)
|
||||
|
||||
# No checks here
|
||||
|
||||
def get_names(self) -> list[str]:
|
||||
"""
|
||||
For using at deployed services, really nothing
|
||||
|
@ -35,8 +35,7 @@ import dataclasses
|
||||
import typing
|
||||
import collections.abc
|
||||
|
||||
from uds.core import services
|
||||
from uds.core.types.states import State
|
||||
from uds.core import services, types
|
||||
|
||||
from . import service
|
||||
|
||||
@ -59,6 +58,7 @@ class TestUserService(services.UserService):
|
||||
"""
|
||||
This is the data we will store in the storage
|
||||
"""
|
||||
|
||||
count: int = -1
|
||||
ready: bool = False
|
||||
name: str = ''
|
||||
@ -92,35 +92,33 @@ class TestUserService(services.UserService):
|
||||
def get_unique_id(self) -> str:
|
||||
logger.info('Getting unique id of deployment %s', self.data)
|
||||
if not self.data.mac:
|
||||
self.data.mac = self.mac_generator().get(
|
||||
'00:00:00:00:00:00-00:FF:FF:FF:FF:FF'
|
||||
)
|
||||
self.data.mac = self.mac_generator().get('00:00:00:00:00:00-00:FF:FF:FF:FF:FF')
|
||||
return self.data.mac
|
||||
|
||||
def get_ip(self) -> str:
|
||||
logger.info('Getting ip of deployment %s', self.data)
|
||||
ip = typing.cast(str, self.storage.read_from_db('ip'))
|
||||
if ip is None:
|
||||
if not ip:
|
||||
ip = '8.6.4.2' # Sample IP for testing purposses only
|
||||
return ip
|
||||
|
||||
def set_ready(self) -> str:
|
||||
def set_ready(self) -> types.states.State:
|
||||
logger.info('Setting ready %s', self.data)
|
||||
self.data.ready = True
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def deploy_for_user(self, user: 'models.User') -> str:
|
||||
def deploy_for_user(self, user: 'models.User') -> types.states.State:
|
||||
logger.info('Deploying for user %s %s', user, self.data)
|
||||
self.data.count = 3
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
logger.info('Checking state of deployment %s', self.data)
|
||||
if self.data.count <= 0:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
self.data.count -= 1
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def finish(self) -> None:
|
||||
logger.info('Finishing deployment %s', self.data)
|
||||
@ -129,17 +127,16 @@ class TestUserService(services.UserService):
|
||||
def user_logged_in(self, username: str) -> None:
|
||||
logger.info('User %s has logged in', username)
|
||||
|
||||
def user_logged_out(self, username) -> None:
|
||||
def user_logged_out(self, username: str) -> None:
|
||||
logger.info('User %s has logged out', username)
|
||||
|
||||
def error_reason(self) -> str:
|
||||
return 'No error'
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
logger.info('Destroying deployment %s', self.data)
|
||||
self.data.count = -1
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
return self.destroy()
|
||||
|
||||
|
@ -38,8 +38,7 @@ import typing
|
||||
import collections.abc
|
||||
|
||||
from django.utils.translation import gettext as _
|
||||
from uds.core import services
|
||||
from uds.core.types.states import State
|
||||
from uds.core import services, types
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -80,32 +79,32 @@ class TestPublication(services.Publication):
|
||||
# We do not check anything at marshal method, so we ensure that
|
||||
# default values are correctly handled by marshal.
|
||||
self.data.name = ''.join(random.choices(string.ascii_letters, k=8))
|
||||
self.data.state = State.RUNNING
|
||||
self.data.state = types.states.State.RUNNING
|
||||
self.data.reason = 'none'
|
||||
self.data.number = 10
|
||||
|
||||
def publish(self) -> str:
|
||||
def publish(self) -> types.states.State:
|
||||
logger.info('Publishing publication %s: %s remaining',self.data.name, self.data.number)
|
||||
self.data.number -= 1
|
||||
|
||||
if self.data.number <= 0:
|
||||
self.data.state = State.FINISHED
|
||||
return self.data.state
|
||||
self.data.state = types.states.State.FINISHED
|
||||
return types.states.State.from_str(self.data.state)
|
||||
|
||||
def finish(self) -> None:
|
||||
# Make simply a random string
|
||||
logger.info('Finishing publication %s', self.data.name)
|
||||
self.data.number = 0
|
||||
self.data.state = State.FINISHED
|
||||
self.data.state = types.states.State.FINISHED
|
||||
|
||||
def error_reason(self) -> str:
|
||||
return self.data.reason
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
logger.info('Destroying publication %s', self.data.name)
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
logger.info('Canceling publication %s', self.data.name)
|
||||
return self.destroy()
|
||||
|
||||
|
@ -35,7 +35,7 @@ import logging
|
||||
import typing
|
||||
import collections.abc
|
||||
|
||||
from uds.core import services, consts
|
||||
from uds.core import services, consts, types
|
||||
from uds.core.types.states import State
|
||||
from uds.core.util import autoserializable, log
|
||||
|
||||
@ -142,9 +142,9 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
def get_ip(self) -> str:
|
||||
return self._ip
|
||||
|
||||
def set_ready(self) -> str:
|
||||
def set_ready(self) -> types.states.State:
|
||||
if self.cache.get('ready') == '1':
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
try:
|
||||
state = self.service().get_machine_power_state(self._vmid)
|
||||
@ -159,13 +159,13 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
self.do_log(log.LogLevel.ERROR, 'Error setting machine state: {}'.format(e))
|
||||
# return self.__error('Machine is not available anymore')
|
||||
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def reset(self) -> None:
|
||||
if self._vmid:
|
||||
self.service().reset_machine(self._vmid) # Reset in sync
|
||||
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> str:
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> types.states.State:
|
||||
# Here we will check for suspending the VM (when full ready)
|
||||
logger.debug('Checking if cache 2 for %s', self._name)
|
||||
if self._get_current_op() == Operation.WAIT:
|
||||
@ -173,9 +173,9 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
self._pop_current_op() # Remove current state
|
||||
return self._execute_queue()
|
||||
# Do not need to go to level 2 (opWait is in fact "waiting for moving machine to cache level 2)
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def deploy_for_user(self, user: 'models.User') -> str:
|
||||
def deploy_for_user(self, user: 'models.User') -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for an user.
|
||||
"""
|
||||
@ -183,11 +183,11 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
self._init_queue_for_deployment(False)
|
||||
return self._execute_queue()
|
||||
|
||||
def deploy_for_cache(self, cacheLevel: int) -> str:
|
||||
def deploy_for_cache(self, level: int) -> types.states.State:
|
||||
"""
|
||||
Deploys an service instance for cache
|
||||
"""
|
||||
self._init_queue_for_deployment(cacheLevel == self.L2_CACHE)
|
||||
self._init_queue_for_deployment(level == self.L2_CACHE)
|
||||
return self._execute_queue()
|
||||
|
||||
def _init_queue_for_deployment(self, forLevel2: bool = False) -> None:
|
||||
@ -229,7 +229,7 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
def _push_back_op(self, op: Operation) -> None:
|
||||
self._queue.append(op)
|
||||
|
||||
def _error(self, reason: typing.Any) -> str:
|
||||
def _error(self, reason: typing.Any) -> types.states.State:
|
||||
logger.debug('Setting error state, reason: %s', reason)
|
||||
self.do_log(log.LogLevel.ERROR, reason)
|
||||
|
||||
@ -248,17 +248,17 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
|
||||
self._queue = [Operation.ERROR]
|
||||
self._reason = str(reason)
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
def _execute_queue(self) -> str:
|
||||
def _execute_queue(self) -> types.states.State:
|
||||
self.__debug('executeQueue')
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: dict[Operation, typing.Optional[collections.abc.Callable[[], str]]] = {
|
||||
Operation.CREATE: self._create,
|
||||
@ -281,32 +281,32 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
|
||||
operation()
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
except Exception as e:
|
||||
return self._error(e)
|
||||
|
||||
# Queue execution methods
|
||||
def _retry(self) -> str:
|
||||
def _retry(self) -> types.states.State:
|
||||
"""
|
||||
Used to retry an operation
|
||||
In fact, this will not be never invoked, unless we push it twice, because
|
||||
check_state method will "pop" first item when a check operation returns State.FINISHED
|
||||
check_state method will "pop" first item when a check operation returns types.states.State.FINISHED
|
||||
|
||||
At executeQueue this return value will be ignored, and it will only be used at check_state
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _wait(self) -> str:
|
||||
def _wait(self) -> types.states.State:
|
||||
"""
|
||||
Executes opWait, it simply waits something "external" to end
|
||||
"""
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _create(self) -> str:
|
||||
"""
|
||||
Deploys a machine from template for user/cache
|
||||
"""
|
||||
templateId = self.publication().getTemplateId()
|
||||
template_id = self.publication().getTemplateId()
|
||||
name = self.get_name()
|
||||
if name == consts.NO_MORE_NAMES:
|
||||
raise Exception(
|
||||
@ -318,11 +318,11 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
) # oVirt don't let us to create machines with more than 15 chars!!!
|
||||
comments = 'UDS Linked clone'
|
||||
|
||||
self._task = self.service().start_deploy_from_template(name, comments, templateId)
|
||||
if self._task is None:
|
||||
self._task = self.service().start_deploy_from_template(name, comments, template_id)
|
||||
if not self._task:
|
||||
raise Exception('Can\'t create machine')
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _remove(self) -> str:
|
||||
"""
|
||||
@ -336,7 +336,7 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
else:
|
||||
self.service().remove_machine(self._vmid)
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _start_machine(self) -> str:
|
||||
"""
|
||||
@ -349,7 +349,7 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
else:
|
||||
self._task = ''
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _stop_machine(self) -> str:
|
||||
"""
|
||||
@ -362,14 +362,14 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
else:
|
||||
self._task = ''
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _wait_suspend(self) -> str:
|
||||
"""
|
||||
Before suspending, wait for machine to have the SUSPEND feature
|
||||
"""
|
||||
self._task = ''
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _suspend_machine(self) -> str:
|
||||
"""
|
||||
@ -382,7 +382,7 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
else:
|
||||
self._task = ''
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _configure(self):
|
||||
"""
|
||||
@ -390,7 +390,7 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
"""
|
||||
self.service().configure_machine(self._vmid, self.get_unique_id())
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _provision(self):
|
||||
"""
|
||||
@ -398,68 +398,68 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
"""
|
||||
self.service().provision_machine(self._vmid, False) # Let's try this in "sync" mode, this must be fast enough
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
# Check methods
|
||||
def _create_checker(self):
|
||||
def _create_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks the state of a deploy for an user or cache
|
||||
"""
|
||||
state = self.service().check_task_finished(self._task)
|
||||
if state[0]: # Finished
|
||||
self._vmid = state[1]
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _start_checker(self):
|
||||
def _start_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if machine has started
|
||||
"""
|
||||
if self.service().check_task_finished(self._task)[0]:
|
||||
return State.FINISHED
|
||||
return State.RUNNING
|
||||
return types.states.State.FINISHED
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _stop_checker(self):
|
||||
def _stop_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if machine has stoped
|
||||
"""
|
||||
if self.service().check_task_finished(self._task)[0]:
|
||||
return State.FINISHED
|
||||
return State.RUNNING
|
||||
return types.states.State.FINISHED
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _wait_suspend_checker(self):
|
||||
def _wait_suspend_checker(self) -> types.states.State:
|
||||
if self.service().can_suspend_machine(self._vmid) is True:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def _suspend_checker(self):
|
||||
def _suspend_checker(self) -> types.states.State:
|
||||
"""
|
||||
Check if the machine has suspended
|
||||
"""
|
||||
if self.service().check_task_finished(self._task)[0]:
|
||||
return State.FINISHED
|
||||
return State.RUNNING
|
||||
return types.states.State.FINISHED
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def removed_checker(self):
|
||||
def removed_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if a machine has been removed
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _configure_checker(self):
|
||||
def _configure_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if change mac operation has finished.
|
||||
|
||||
Changing nic configuration es 1-step operation, so when we check it here, it is already done
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _provision_checker(self):
|
||||
return State.FINISHED
|
||||
def _provision_checker(self) -> types.states.State:
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def check_state(self) -> str:
|
||||
def check_state(self) -> types.states.State:
|
||||
"""
|
||||
Check what operation is going on, and acts acordly to it
|
||||
"""
|
||||
@ -467,12 +467,12 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.ERROR
|
||||
return types.states.State.ERROR
|
||||
|
||||
if op == Operation.FINISH:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
fncs: dict[int, typing.Optional[collections.abc.Callable[[], str]]] = {
|
||||
fncs: dict[int, typing.Optional[collections.abc.Callable[[], types.states.State]]] = {
|
||||
Operation.CREATE: self._create_checker,
|
||||
Operation.RETRY: self._retry,
|
||||
Operation.WAIT: self._wait,
|
||||
@ -486,13 +486,13 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
}
|
||||
|
||||
try:
|
||||
chkFnc: typing.Optional[collections.abc.Callable[[], str]] = fncs.get(op, None)
|
||||
chkFnc: typing.Optional[collections.abc.Callable[[], types.states.State]] = fncs.get(op, None)
|
||||
|
||||
if chkFnc is None:
|
||||
return self._error('Unknown operation found at check queue ({})'.format(op))
|
||||
|
||||
state = chkFnc()
|
||||
if state == State.FINISHED:
|
||||
if state == types.states.State.FINISHED:
|
||||
self._pop_current_op() # Remove runing op
|
||||
return self._execute_queue()
|
||||
|
||||
@ -500,14 +500,14 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
except Exception as e:
|
||||
return self._error(e)
|
||||
|
||||
def move_to_cache(self, newLevel: int) -> str:
|
||||
def move_to_cache(self, level: int) -> types.states.State:
|
||||
"""
|
||||
Moves machines between cache levels
|
||||
"""
|
||||
if Operation.REMOVE in self._queue:
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
if newLevel == self.L1_CACHE:
|
||||
if level == self.L1_CACHE:
|
||||
self._queue = [Operation.START, Operation.FINISH]
|
||||
else:
|
||||
self._queue = [Operation.START, Operation.SUSPEND, Operation.FINISH]
|
||||
@ -517,14 +517,14 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
def error_reason(self) -> str:
|
||||
return self._reason
|
||||
|
||||
def destroy(self) -> str:
|
||||
def destroy(self) -> types.states.State:
|
||||
self.__debug('destroy')
|
||||
# If executing something, wait until finished to remove it
|
||||
# We simply replace the execution queue
|
||||
op = self._get_current_op()
|
||||
|
||||
if op == Operation.ERROR:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
if op == Operation.FINISH or op == Operation.WAIT:
|
||||
self._queue = [Operation.STOP, Operation.REMOVE, Operation.FINISH]
|
||||
@ -532,9 +532,9 @@ class XenLinkedDeployment(services.UserService, autoserializable.AutoSerializabl
|
||||
|
||||
self._queue = [op, Operation.STOP, Operation.REMOVE, Operation.FINISH]
|
||||
# Do not execute anything.here, just continue normally
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
def cancel(self) -> str:
|
||||
def cancel(self) -> types.states.State:
|
||||
return self.destroy()
|
||||
|
||||
@staticmethod
|
||||
|
@ -30,13 +30,11 @@
|
||||
"""
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import pickle # nosec: controled data
|
||||
import enum
|
||||
import logging
|
||||
import typing
|
||||
import collections.abc
|
||||
|
||||
from uds.core import services
|
||||
from uds.core import types
|
||||
from uds.core.services.specializations.fixed_machine.fixed_userservice import FixedUserService, Operation
|
||||
from uds.core.types.states import State
|
||||
from uds.core.util import log, autoserializable
|
||||
@ -69,9 +67,9 @@ class XenFixedUserService(FixedUserService, autoserializable.AutoSerializable):
|
||||
def service(self) -> 'service_fixed.XenFixedService':
|
||||
return typing.cast('service_fixed.XenFixedService', super().service())
|
||||
|
||||
def set_ready(self) -> str:
|
||||
def set_ready(self) -> types.states.State:
|
||||
if self.cache.get('ready') == '1':
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
try:
|
||||
state = self.service().get_machine_power_state(self._vmid)
|
||||
@ -86,14 +84,14 @@ class XenFixedUserService(FixedUserService, autoserializable.AutoSerializable):
|
||||
self.do_log(log.LogLevel.ERROR, 'Error setting machine state: {}'.format(e))
|
||||
# return self.__error('Machine is not available anymore')
|
||||
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def reset(self) -> None:
|
||||
if self._vmid:
|
||||
self.service().reset_machine(self._vmid) # Reset in sync
|
||||
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> str:
|
||||
return State.FINISHED
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> types.states.State:
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def error(self, reason: str) -> str:
|
||||
return self._error(reason)
|
||||
@ -118,44 +116,44 @@ class XenFixedUserService(FixedUserService, autoserializable.AutoSerializable):
|
||||
self._task = self.service().stop_machine(self._vmid) or ''
|
||||
|
||||
# Check methods
|
||||
def _check_task_finished(self) -> str:
|
||||
def _check_task_finished(self) -> types.states.State:
|
||||
if self._task == '':
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
try:
|
||||
finished, per = self.service().check_task_finished(self._task)
|
||||
finished, _per = self.service().check_task_finished(self._task)
|
||||
except xen_client.XenFailure:
|
||||
return State.RUNNING # Try again later
|
||||
return types.states.State.RUNNING # Try again later
|
||||
except Exception as e: # Failed for some other reason
|
||||
if isinstance(e.args[0], dict) and 'error_connection' in e.args[0]:
|
||||
return State.RUNNING # Try again later
|
||||
return types.states.State.RUNNING # Try again later
|
||||
raise e
|
||||
|
||||
if finished:
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
return State.RUNNING
|
||||
return types.states.State.RUNNING
|
||||
|
||||
# Check methods
|
||||
def _create_checker(self) -> str:
|
||||
def _create_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks the state of a deploy for an user or cache
|
||||
"""
|
||||
return State.FINISHED
|
||||
return types.states.State.FINISHED
|
||||
|
||||
def _start_checker(self) -> str:
|
||||
def _start_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if machine has started
|
||||
"""
|
||||
return self._check_task_finished()
|
||||
|
||||
def _stop_checker(self) -> str:
|
||||
def _stop_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if machine has stoped
|
||||
"""
|
||||
return self._check_task_finished()
|
||||
|
||||
def _removed_checker(self) -> str:
|
||||
def _removed_checker(self) -> types.states.State:
|
||||
"""
|
||||
Checks if a machine has been removed
|
||||
"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user