From 19fdcadbcdf4fa8960aacf2f2c16cce7dd2ba4e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Fri, 6 Nov 2015 12:04:44 +0100 Subject: [PATCH] Upgrading the UserServiceOpChecker to a new & clearer one --- .../uds/core/managers/UserServiceManager.py | 150 ++++++++++++------ server/src/uds/models/UserService.py | 8 +- 2 files changed, 112 insertions(+), 46 deletions(-) diff --git a/server/src/uds/core/managers/UserServiceManager.py b/server/src/uds/core/managers/UserServiceManager.py index 0cb925ee..3831ddbf 100644 --- a/server/src/uds/core/managers/UserServiceManager.py +++ b/server/src/uds/core/managers/UserServiceManager.py @@ -57,6 +57,100 @@ logger = logging.getLogger(__name__) USERSERVICE_TAG = 'cm-' +class StateUpdater(object): + def __init__(self, userService, userServiceInstance=None): + self.userService = userService + self.userServiceInstance = userServiceInstance if userServiceInstance is not None else userService.getInstance() + + def setError(self, msg=None): + self.userService.setState(State.ERROR) + self.userService.save() + if msg is not None: + log.doLog(self.userService, log.ERROR, msg, log.INTERNAL) + + def save(self, newState=None): + if newState is not None: + self.userService.setState(newState) + self.userService.updateData(self.userServiceInstance) + self.userService.save() + + def checkLater(self): + UserServiceOpChecker.checkLater(self.userService, self.userServiceInstance) + + def run(self, state): + executor = { + State.RUNNING: self.running, + State.ERROR: self.error, + State.FINISHED: self.finish + }.get(state, self.error) + + try: + executor() + except Exception as e: + self.setError('Exception: {}'.format(e)) + + def finish(self): + raise NotImplementedError('finish method must be overriden') + + def running(self): + raise NotImplementedError('running method must be overriden') + + def error(self): + self.setError(self.userServiceInstance.reasonOfError()) + + +class UpdateFromPreparing(StateUpdater): + def finish(self): + self.userServiceInstance.finish() + + osManager = self.userServiceInstance.osmanager() + + state = State.REMOVABLE # By default, if not valid publication, service will be marked for removal on preparation finished + if self.userService.isValidPublication(): + logger.debug('Publication is valid for {}'.format(self.userService.friendly_name)) + state = State.USABLE + # and make this usable if os manager says that it is usable, else it pass to configuring state + if osManager is not None and State.isPreparing(self.userService.os_state): + logger.debug('Has valid osmanager for {}'.format(self.userService.friendly_name)) + stateOs = osManager.checkState(self.userService) + # If state is finish, we need to notify the userService again that os has finished + if State.isFinished(stateOs): + state = self.userServiceInstance.notifyReadyFromOsManager('') + else: + stateOs = State.FINISHED + + logger.debug('State {}, StateOS {} for {}'.format(State.toString(state), State.toString(stateOs), self.userService.friendly_name)) + if State.isRuning(stateOs): + self.userService.setOsState(State.PREPARING) + else: + self.userService.setOsState(State.USABLE) + + self.save(state) + + def running(self): + self.checkLater() + +class UpdateFromRemoving(StateUpdater): + def finish(self): + pass + + def running(self): + pass + +class UpdateFromCanceling(StateUpdater): + def finish(self): + self.save(State.CANCELED) + + def running(self): + self.checkLater() + +class UpdateFromOther(StateUpdater): + def finish(self): + self.setError('Unknown running transition from {}'.format(State.toString(self.userService.state))) + + def running(self): + self.setError('Unknown running transition from {}'.format(State.toString(self.userService.state))) + class UserServiceOpChecker(DelayedTask): def __init__(self, service): @@ -79,52 +173,18 @@ class UserServiceOpChecker(DelayedTask): Return True if it has to continue checking, False if finished ''' try: - prevState = userService.state + # Fills up basic data userService.unique_id = userServiceInstance.getUniqueId() # Updates uniqueId userService.friendly_name = userServiceInstance.getName() # And name, both methods can modify serviceInstance, so we save it later - if State.isFinished(state): - checkLater = False - userServiceInstance.finish() - if State.isPreparing(prevState): - if userServiceInstance.service().publicationType is None or userService.publication == userService.deployed_service.activePublication(): - userService.setState(State.USABLE) - # and make this usable if os manager says that it is usable, else it pass to configuring state - if userServiceInstance.osmanager() is not None and userService.os_state == State.PREPARING: # If state is already "Usable", do not recheck it - stateOs = userServiceInstance.osmanager().checkState(userService) - # If state is finish, we need to notify the userService again that os has finished - if State.isFinished(stateOs): - state = userServiceInstance.notifyReadyFromOsManager('') - userService.updateData(userServiceInstance) - else: - stateOs = State.FINISHED - if State.isRuning(stateOs): - userService.setOsState(State.PREPARING) - else: - userService.setOsState(State.USABLE) - else: - # We ignore OsManager info and if userService don't belong to "current" publication, mark it as removable - userService.setState(State.REMOVABLE) - elif State.isRemoving(prevState): - if userServiceInstance.osmanager() is not None: - userServiceInstance.osmanager().release(userService) - userService.setState(State.REMOVED) - else: - # Canceled, - logger.debug("Canceled us {2}: {0}, {1}".format(prevState, State.toString(state), State.toString(userService))) - userService.setState(State.CANCELED) - userServiceInstance.osmanager().release(userService) - userService.updateData(userServiceInstance) - elif State.isErrored(state): - checkLater = False - userService.updateData(userServiceInstance) - userService.setState(State.ERROR) - else: - checkLater = True # The task is running - userService.updateData(userServiceInstance) - userService.save() - if checkLater: - UserServiceOpChecker.checkLater(userService, userServiceInstance) + updater = { + State.PREPARING: UpdateFromPreparing, + State.REMOVING: UpdateFromRemoving, + State.CANCELING: UpdateFromCanceling + }.get(userService.state, UpdateFromOther) + + updater(userService, userServiceInstance).run(state) + except Exception as e: logger.exception('Checking service state') log.doLog(userService, log.ERROR, 'Exception: {0}'.format(e), log.INTERNAL) @@ -139,9 +199,9 @@ class UserServiceOpChecker(DelayedTask): @param pi: Instance of Publication manager for the object ''' # Do not add task if already exists one that updates this service - if DelayedTaskRunner.runner().checkExists(USERSERVICE_TAG + str(userService.id)): + if DelayedTaskRunner.runner().checkExists(USERSERVICE_TAG + userService.uuid): return - DelayedTaskRunner.runner().insert(UserServiceOpChecker(userService), ci.suggestedTime, USERSERVICE_TAG + str(userService.id)) + DelayedTaskRunner.runner().insert(UserServiceOpChecker(userService), ci.suggestedTime, USERSERVICE_TAG + userService.uuid) def run(self): logger.debug('Checking user service finished {0}'.format(self._svrId)) diff --git a/server/src/uds/models/UserService.py b/server/src/uds/models/UserService.py index 9d753b88..c2f4b09c 100644 --- a/server/src/uds/models/UserService.py +++ b/server/src/uds/models/UserService.py @@ -55,7 +55,7 @@ from uds.models.Util import getSqlDatetime import logging -__updated__ = '2015-10-05' +__updated__ = '2015-11-06' logger = logging.getLogger(__name__) @@ -463,6 +463,12 @@ class UserService(UUIDModel): def getLoggedIP(self): return self.getProperty('ip', '0.0.0.0') + def isValidPublication(self): + ''' + Returns True if this user service does not needs an publication, or if this deployed service publication is the current one + ''' + return self.deployed_service.service.getType().publicationType is None or self.publication == self.deployed_service.activePublication() + def __str__(self): return "User service {0}, cache_level {1}, user {2}, name {3}, state {4}:{5}".format(self.id, self.cache_level, self.user, self.friendly_name, State.toString(self.state), State.toString(self.os_state))