def checkOsManagerRelated(self): osManager = self.userServiceInstance.osmanager() state = State.USABLE # and make this usable if os manager says that it is usable, else it pass to configuring state # This is an "early check" for os manager, so if we do not have os manager, or os manager # already notifies "ready" for this, we 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) else: stateOs = State.FINISHED logger.debug('State {}, StateOS {} for {}'.format( State.toString(state), State.toString(stateOs), self.userService.friendly_name)) if stateOs == State.RUNNING: self.userService.setOsState(State.PREPARING) else: # If state is finish, we need to notify the userService again that os has finished # This will return a new task state, and that one will be the one taken into account self.userService.setOsState(State.USABLE) rs = self.userServiceInstance.notifyReadyFromOsManager('') if rs != State.FINISHED: self.checkLater() state = self.userService.state # No not alter current state if after notifying os manager the user service keeps working else: self.logIp() return state
def growL1Cache(self, sp, cacheL1, cacheL2, assigned): ''' This method tries to enlarge L1 cache. If for some reason the number of deployed services (Counting all, ACTIVE and PREPARING, assigned, L1 and L2) is over max allowed service deployments, this method will not grow the L1 cache ''' logger.debug("Growing L1 cache creating a new service for {0}".format(sp)) # First, we try to assign from L2 cache if cacheL2 > 0: valid = None with transaction.atomic(): for n in sp.cachedUserServices().select_for_update().filter(UserServiceManager.getCacheStateFilter(services.UserDeployment.L2_CACHE)).order_by('creation_date'): if n.needsOsManager(): if State.isUsable(n.state) is False or State.isUsable(n.os_state): valid = n break else: valid = n break if valid is not None: valid.moveToLevel(services.UserDeployment.L1_CACHE) return try: UserServiceManager.manager().createCacheFor(sp.activePublication(), services.UserDeployment.L1_CACHE) except MaxServicesReachedException as e: log.doLog(sp, log.ERROR, 'Max number of services reached for this service', log.INTERNAL) logger.error(str(e)) except: logger.exception('Exception')
def checkOsManagerRelated(self): osManager = self.userServiceInstance.osmanager() state = State.USABLE # and make this usable if os manager says that it is usable, else it pass to configuring state # This is an "early check" for os manager, so if we do not have os manager, or os manager # already notifies "ready" for this, we 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) else: stateOs = State.FINISHED logger.debug('State {}, StateOS {} for {}'.format(State.toString(state), State.toString(stateOs), self.userService.friendly_name)) if stateOs == State.RUNNING: self.userService.setOsState(State.PREPARING) else: # If state is finish, we need to notify the userService again that os has finished # This will return a new task state, and that one will be the one taken into account self.userService.setOsState(State.USABLE) rs = self.userServiceInstance.notifyReadyFromOsManager('') if rs != State.FINISHED: self.checkLater() state = self.userService.state # No not alter current state if after notifying os manager the user service keeps working else: self.logIp() return state
def reduceL1Cache(self, sp, cacheL1, cacheL2, assigned): logger.debug("Reducing L1 cache erasing a service in cache for {0}".format(sp)) # We will try to destroy the newest cacheL1 element that is USABLE if the deployer can't cancel a new service creation cacheItems = sp.cachedUserServices().filter(UserServiceManager.getCacheStateFilter(services.UserDeployment.L1_CACHE)).order_by('-creation_date') if len(cacheItems) == 0: logger.debug('There is more services than configured, but could not reduce cache cause its already empty') return if cacheL2 < sp.cache_l2_srvs: valid = None for n in cacheItems: if n.needsOsManager(): if State.isUsable(n.state) is False or State.isUsable(n.os_state): valid = n break else: valid = n break if valid is not None: valid.moveToLevel(services.UserDeployment.L2_CACHE) return cache = cacheItems[0] cache.removeOrCancel()
def remove(self, uService): """ Removes a uService element @return: the uService removed (marked for removal) """ with transaction.atomic(): uService = UserService.objects.select_for_update().get( id=uService.id) logger.debug('Removing uService {0}'.format(uService)) if uService.isUsable() is False and State.isRemovable( uService.state) is False: raise OperationException( _('Can\'t remove a non active element')) uService.setState(State.REMOVING) logger.debug("***** The state now is {}".format( State.toString(uService.state))) uService.setInUse( False ) # For accounting, ensure that it is not in use right now uService.save() ci = uService.getInstance() state = ci.destroy() UserServiceOpChecker.makeUnique(uService, ci, state)
def checkAndUpdateState(userService, userServiceInstance, 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 """ try: if State.isFinished(state): checkLater = False userServiceInstance.finish() userService.updateData(userServiceInstance) userService.setState(State.USABLE) # Wi will only migrate fully functional services 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: ClusterMigrationTask.checkLater(userService, userServiceInstance) except Exception as e: logger.exception('Migrating service') log.doLog(userService, log.ERROR, 'Exception: {0}'.format(e), log.INTERNAL) userService.setState(State.ERROR) userService.save()
def reduceL1Cache(self, sp, cacheL1, cacheL2, assigned): logger.debug( "Reducing L1 cache erasing a service in cache for {0}".format(sp)) # We will try to destroy the newest cacheL1 element that is USABLE if the deployer can't cancel a new service creation cacheItems = sp.cachedUserServices().filter( UserServiceManager.getCacheStateFilter( services.UserDeployment.L1_CACHE)).order_by('-creation_date') if len(cacheItems) == 0: logger.debug( 'There is more services than configured, but could not reduce cache cause its already empty' ) return if cacheL2 < sp.cache_l2_srvs: valid = None for n in cacheItems: if n.needsOsManager(): if State.isUsable(n.state) is False or State.isUsable( n.os_state): valid = n break else: valid = n break if valid is not None: valid.moveToLevel(services.UserDeployment.L2_CACHE) return cache = cacheItems[0] cache.removeOrCancel()
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), )
def checkAndUpdateState(servicePoolPub, pi, 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 """ try: prevState = servicePoolPub.state checkLater = False if State.isFinished(state): # Now we mark, if it exists, the previous usable publication as "Removable" if State.isPreparing(prevState): for old in servicePoolPub.deployed_service.publications.filter( state=State.USABLE): old.setstate(State.REMOVABLE) osm = servicePoolPub.deployed_service.osmanager # If os manager says "machine is persistent", do not tray to delete "previous version" assigned machines doPublicationCleanup = True if osm is None else not osm.getInstance( ).isPersistent() if doPublicationCleanup: pc = PublicationOldMachinesCleaner(old.id) pc.register( GlobalConfig.SESSION_EXPIRE_TIME.getInt(True) * 3600, 'pclean-' + str(old.id), True) servicePoolPub.setState(State.USABLE) servicePoolPub.deployed_service.markOldUserServicesAsRemovables( servicePoolPub) elif State.isRemoving(prevState): servicePoolPub.setState(State.REMOVED) else: # State is canceling servicePoolPub.setState(State.CANCELED) # Mark all previous publications deployed services as removables # and make this usable pi.finish() servicePoolPub.updateData(pi) elif State.isErrored(state): servicePoolPub.updateData(pi) servicePoolPub.setState(State.ERROR) else: checkLater = True # The task is running servicePoolPub.updateData(pi) if checkLater: PublicationFinishChecker.checkLater(servicePoolPub, pi) except Exception: logger.exception('At checkAndUpdate for publication') PublicationFinishChecker.checkLater(servicePoolPub, pi)
def moveToLevel(self, cache, cacheLevel): """ Moves a cache element from one level to another @return: cache element """ cache = UserService.objects.get(id=cache.id) logger.debug('Moving cache {0} to level {1}'.format(cache, cacheLevel)) ci = cache.getInstance() state = ci.moveToCache(cacheLevel) cache.cache_level = cacheLevel logger.debug('Service State: {0} {1} {2}'.format(State.toString(state), State.toString(cache.state), State.toString(cache.os_state))) if State.isRuning(state) and cache.isUsable(): cache.setState(State.PREPARING) UserServiceOpChecker.makeUnique(cache, ci, state)
def moveToLevel(self, cache, cacheLevel): ''' Moves a cache element from one level to another @return: cache element ''' cache = UserService.objects.get(id=cache.id) logger.debug('Moving cache {0} to level {1}'.format(cache, cacheLevel)) ci = cache.getInstance() state = ci.moveToCache(cacheLevel) cache.cache_level = cacheLevel logger.debug('Service State: {0} {1} {2}'.format(State.toString(state), State.toString(cache.state), State.toString(cache.os_state))) if State.isRuning(state) and cache.isUsable(): cache.setState(State.PREPARING) UserServiceOpChecker.makeUnique(cache, ci, state)
def removeOrCancel(self, uService): if uService.isUsable() or State.isRemovable(uService.state): return self.remove(uService) elif uService.isPreparing(): return self.cancel(uService) else: raise OperationException(_('Can\'t remove nor cancel {0} cause its states don\'t allow it'))
def checkAndUpdateState(userService, userServiceInstance, 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 """ try: # 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 userService.save(update_fields=['unique_id', 'friendly_name']) updater = { State.PREPARING: UpdateFromPreparing, State.REMOVING: UpdateFromRemoving, State.CANCELING: UpdateFromCanceling }.get(userService.state, UpdateFromOther) logger.debug('Updating from {} with updater {} and state {}'.format(State.toString(userService.state), updater, state)) 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) userService.setState(State.ERROR) userService.save(update_fields=['data'])
def setState(self, state): ''' Updates the state of this object and, optionally, saves it Args: state: new State to store at record save: Defaults to true. If false, record will not be saved to db, just modified ''' logger.debug(' *** Setting state to {} from {} for {}'.format( State.toString(state), State.toString(self.state), self)) if state != self.state: self.state_date = getSqlDatetime() self.state = state
def getFields(self, parent): return [ # Note that this field is "self generated" on client table { 'group_name': { 'title': _('Name'), 'type': 'icon_dict', 'icon_dict': { 'group': 'fa fa-group text-success', 'meta': 'fa fa-gears text-info' } } }, { 'comments': { 'title': _('comments') } }, { 'state': { 'title': _('State'), 'type': 'dict', 'dict': State.dictionary() } }, ]
def getFields(self, parent): return [ {'revision': {'title': _('Revision'), 'type': 'numeric', 'width': '6em'}}, {'publish_date': {'title': _('Publish date'), 'type': 'datetime'}}, {'state': {'title': _('State'), 'type': 'dict', 'dict': State.dictionary()}}, {'reason': {'title': _('Reason')}}, ]
def __registerUser(authenticator, authInstance, username): """ Check if this user already exists on database with this authenticator, if don't, create it with defaults This will work correctly with both internal or externals cause we first authenticate the user, if internal and user do not exists in database authenticate will return false, if external and return true, will create a reference in database """ from uds.core.util.request import getRequest username = authInstance.transformUsername(username) logger.debug('Transformed username: {0}'.format(username)) request = getRequest() usr = authenticator.getOrCreateUser(username, username) usr.real_name = authInstance.getRealName(username) usr.save() if usr is not None and State.isActive(usr.state): # Now we update database groups for this user usr.getManager().recreateGroups(usr) # And add an login event events.addEvent(authenticator, events.ET_LOGIN, username=username, srcip=request.ip) # pylint: disable=maybe-no-member events.addEvent(authenticator, events.ET_PLATFORM, platform=request.os.OS, browser=request.os.Browser, version=request.os.Version) # pylint: disable=maybe-no-member return usr return None
def checkAndUpdateState(userService, userServiceInstance, 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 ''' try: # 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 updater = { State.PREPARING: UpdateFromPreparing, State.REMOVING: UpdateFromRemoving, State.CANCELING: UpdateFromCanceling }.get(userService.state, UpdateFromOther) logger.debug( 'Updating from {} with updater {} and state {}'.format( State.toString(userService.state), updater, state)) 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) userService.setState(State.ERROR) userService.save()
def getFields(self, parent): return [ { 'name': { 'title': _('Username'), 'visible': True, 'type': 'icon', 'icon': 'fa fa-user text-success' } }, { 'real_name': { 'title': _('Name') } }, { 'comments': { 'title': _('Comments') } }, { 'state': { 'title': _('state'), 'type': 'dict', 'dict': State.dictionary() } }, { 'last_access': { 'title': _('Last access'), 'type': 'datetime' } }, ]
def getFields(self, parent): return [ {"revision": {"title": _("Revision"), "type": "numeric", "width": "6em"}}, {"publish_date": {"title": _("Publish date"), "type": "datetime"}}, {"state": {"title": _("State"), "type": "dict", "dict": State.dictionary()}}, {"reason": {"title": _("Reason")}}, ]
def getFields(self, parent): return [ { 'revision': { 'title': _('Revision'), 'type': 'numeric', 'width': '6em' } }, { 'publish_date': { 'title': _('Publish date'), 'type': 'datetime' } }, { 'state': { 'title': _('State'), 'type': 'dict', 'dict': State.dictionary() } }, { 'reason': { 'title': _('Reason') } }, ]
def getFields(self, parent): return [ { 'name': { 'title': _('Group'), 'visible': True, 'type': 'icon_dict', 'icon_dict': { 'group': 'fa fa-group text-success', 'meta': 'fa fa-gears text-info' } } }, { 'comments': { 'title': _('Comments') } }, { 'state': { 'title': _('state'), 'type': 'dict', 'dict': State.dictionary() } }, ]
def getFields(self, parent): return [ # Note that this field is "self generated" on client table {'group_name': {'title': _('Name'), 'type': 'icon_dict', 'icon_dict': {'group': 'fa fa-group text-success', 'meta': 'fa fa-gears text-info'}}}, {'comments': {'title': _('comments')}}, {'state': {'title': _('State'), 'type': 'dict', 'dict': State.dictionary()}}, ]
def getFields(self, parent): return [ {'name': {'title': _('Username'), 'visible': True, 'type': 'icon', 'icon': 'fa fa-user text-success'}}, {'real_name': {'title': _('Name')}}, {'comments': {'title': _('Comments')}}, {'state': {'title': _('state'), 'type': 'dict', 'dict': State.dictionary()}}, {'last_access': {'title': _('Last access'), 'type': 'datetime'}}, ]
def checkAndUpdateState(servicePoolPub, pi, 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 """ try: prevState = servicePoolPub.state checkLater = False if State.isFinished(state): # Now we mark, if it exists, the previous usable publication as "Removable" if State.isPreparing(prevState): for old in servicePoolPub.deployed_service.publications.filter(state=State.USABLE): old.state = State.REMOVABLE old.save() osm = servicePoolPub.deployed_service.osmanager # If os manager says "machine is persistent", do not tray to delete "previous version" assigned machines doPublicationCleanup = True if osm is None else not osm.getInstance().isPersistent() if doPublicationCleanup: pc = PublicationOldMachinesCleaner(old.id) pc.register(GlobalConfig.SESSION_EXPIRE_TIME.getInt(True) * 3600, 'pclean-' + str(old.id), True) servicePoolPub.setState(State.USABLE) servicePoolPub.deployed_service.markOldUserServicesAsRemovables(servicePoolPub) elif State.isRemoving(prevState): servicePoolPub.setState(State.REMOVED) else: # State is canceling servicePoolPub.setState(State.CANCELED) # Mark all previous publications deployed services as removables # and make this usable pi.finish() servicePoolPub.updateData(pi) elif State.isErrored(state): servicePoolPub.updateData(pi) servicePoolPub.state = State.ERROR else: checkLater = True # The task is running servicePoolPub.updateData(pi) servicePoolPub.save() if checkLater: PublicationFinishChecker.checkLater(servicePoolPub, pi) except Exception: logger.exception('At checkAndUpdate for publication') PublicationFinishChecker.checkLater(servicePoolPub, pi)
def unpublish(self, servicePoolPub): # pylint: disable=no-self-use ''' Unpublishes an active (usable) or removable publication :param servicePoolPub: Publication to unpublish ''' if State.isUsable(servicePoolPub.state) is False and State.isRemovable(servicePoolPub.state) is False: raise PublishException(_('Can\'t unpublish non usable publication') ) if servicePoolPub.userServices.exclude(state__in=State.INFO_STATES).count() > 0: raise PublishException(_('Can\'t unpublish publications with services in process')) try: pubInstance = servicePoolPub.getInstance() state = pubInstance.destroy() servicePoolPub.setState(State.REMOVING) PublicationFinishChecker.checkAndUpdateState(servicePoolPub, pubInstance, state) except Exception as e: raise PublishException(str(e))
def unpublish(self, servicePoolPub): # pylint: disable=no-self-use ''' Unpublishes an active (usable) or removable publication :param servicePoolPub: Publication to unpublish ''' if State.isUsable(servicePoolPub.state) is False and State.isRemovable(servicePoolPub.state) is False: raise PublishException(_('Can\'t unpublish non usable publication') ) if servicePoolPub.userServices.exclude(state__in=State.INFO_STATES).count() > 0: raise PublishException(_('Can\'t unpublish publications with services in process')) try: pubInstance = servicePoolPub.getInstance() state = pubInstance.destroy() servicePoolPub.setState(State.REMOVING) PublicationFinishChecker.checkAndUpdateState(servicePoolPub, pubInstance, state) except Exception, e: raise PublishException(str(e))
def getItems(self, parent, item): return [{ 'id': i.uuid, 'revision': i.revision, 'publish_date': i.publish_date, 'state': i.state, 'reason': State.isErrored(i.state) and i.getInstance().reasonOfError() or '', 'state_date': i.state_date, } for i in parent.publications.all()]
def remove(self, uService): ''' Removes a uService element @return: the uService removed (marked for removal) ''' with transaction.atomic(): uService = UserService.objects.select_for_update().get(id=uService.id) logger.debug('Removing uService {0}'.format(uService)) if uService.isUsable() is False and State.isRemovable(uService.state) is False: raise OperationException(_('Can\'t remove a non active element')) uService.setState(State.REMOVING) logger.debug("***** The state now is {}".format(State.toString(uService.state))) uService.setInUse(False) # For accounting, ensure that it is not in use right now uService.save() ci = uService.getInstance() state = ci.destroy() UserServiceOpChecker.makeUnique(uService, ci, state)
def checkAndUpdateState(userService, userServiceInstance, 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 ''' try: prevState = userService.state 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) except Exception as e: logger.exception('Checking service state') log.doLog(userService, log.ERROR, 'Exception: {0}'.format(e), log.INTERNAL) userService.setState(State.ERROR) userService.save()
def getFields(self, parent): return [{ 'creation_date': { 'title': _('Creation date'), 'type': 'datetime' } }, { 'revision': { 'title': _('Revision') } }, { 'unique_id': { 'title': 'Unique ID' } }, { 'ip': { 'title': _('IP') } }, { 'friendly_name': { 'title': _('Friendly name') } }, { 'state': { 'title': _('status'), 'type': 'dict', 'dict': State.dictionary() } }, { 'state_date': { 'title': _('Status date'), 'type': 'datetime' } }, { 'in_use': { 'title': _('In Use') } }, { 'source_host': { 'title': _('Src Host') } }, { 'source_ip': { 'title': _('Src Ip') } }, { 'owner': { 'title': _('Owner') } }, { 'actor_version': { 'title': _('Actor version') } }]
def getFields(self, parent): return [ {'creation_date': {'title': _('Creation date'), 'type': 'datetime'}}, {'revision': {'title': _('Revision')}}, {'unique_id': {'title': 'Unique ID'}}, {'ip': {'title': _('IP')}}, {'friendly_name': {'title': _('Friendly name')}}, {'state': {'title': _('State'), 'type': 'dict', 'dict': State.dictionary()}}, {'cache_level': {'title': _('Cache level')}}, {'actor_version': {'title': _('Actor version')}} ]
def getFields(self, parent): return [ {"creation_date": {"title": _("Creation date"), "type": "datetime"}}, {"revision": {"title": _("Revision")}}, {"unique_id": {"title": "Unique ID"}}, {"ip": {"title": _("IP")}}, {"friendly_name": {"title": _("Friendly name")}}, {"state": {"title": _("State"), "type": "dict", "dict": State.dictionary()}}, {"cache_level": {"title": _("Cache level")}}, {"actor_version": {"title": _("Actor version")}}, ]
def getItems(self, parent, item): return [ { "id": i.uuid, "revision": i.revision, "publish_date": i.publish_date, "state": i.state, "reason": State.isErrored(i.state) and i.getInstance().reasonOfError() or "", "state_date": i.state_date, } for i in parent.publications.all() ]
def growL1Cache(self, sp, cacheL1, cacheL2, assigned): """ This method tries to enlarge L1 cache. If for some reason the number of deployed services (Counting all, ACTIVE and PREPARING, assigned, L1 and L2) is over max allowed service deployments, this method will not grow the L1 cache """ logger.debug( "Growing L1 cache creating a new service for {0}".format(sp)) # First, we try to assign from L2 cache if cacheL2 > 0: valid = None with transaction.atomic(): for n in sp.cachedUserServices().select_for_update().filter( UserServiceManager.getCacheStateFilter( services.UserDeployment.L2_CACHE)).order_by( 'creation_date'): if n.needsOsManager(): if State.isUsable(n.state) is False or State.isUsable( n.os_state): valid = n break else: valid = n break if valid is not None: valid.moveToLevel(services.UserDeployment.L1_CACHE) return try: UserServiceManager.manager().createCacheFor( sp.activePublication(), services.UserDeployment.L1_CACHE) except MaxServicesReachedError as e: log.doLog(sp, log.ERROR, 'Max number of services reached for this service', log.INTERNAL) logger.error(str(e)) except: logger.exception('Exception')
def run(self, state): executor = { State.RUNNING: self.running, State.ERROR: self.error, State.FINISHED: self.finish }.get(state, self.error) logger.debug('Running updater with state {} and executor {}'.format(State.toString(state), executor)) try: executor() except Exception as e: self.setError('Exception: {}'.format(e))
def getFields(self, parent): return [ # Note that this field is "self generated" on client table { "group_name": { "title": _("Name"), "type": "icon_dict", "icon_dict": {"group": "fa fa-group text-success", "meta": "fa fa-gears text-info"}, } }, {"comments": {"title": _("comments")}}, {"state": {"title": _("State"), "type": "dict", "dict": State.dictionary()}}, ]
def remove(self, uService): ''' Removes a uService element @return: the uService removed (marked for removal) ''' uService = UserService.objects.get(id=uService.id) logger.debug('Removing uService {0}'.format(uService)) if uService.isUsable() is False and State.isRemovable(uService.state) is False: raise OperationException(_('Can\'t remove a non active element')) ci = uService.getInstance() state = ci.destroy() uService.setState(State.REMOVING) UserServiceOpChecker.makeUnique(uService, ci, state)
def getFields(self, parent): return [ {'creation_date': {'title': _('Creation date'), 'type': 'datetime'}}, {'pool_name': {'title': _('Pool')}}, {'unique_id': {'title': 'Unique ID'}}, {'ip': {'title': _('IP')}}, {'friendly_name': {'title': _('Friendly name')}}, {'state': {'title': _('status'), 'type': 'dict', 'dict': State.dictionary()}}, {'in_use': {'title': _('In Use')}}, {'source_host': {'title': _('Src Host')}}, {'source_ip': {'title': _('Src Ip')}}, {'owner': {'title': _('Owner')}}, {'actor_version': {'title': _('Actor version')}} ]
def getFields(self, parent): return [ {"creation_date": {"title": _("Creation date"), "type": "datetime"}}, {"revision": {"title": _("Revision")}}, {"unique_id": {"title": "Unique ID"}}, {"ip": {"title": _("IP")}}, {"friendly_name": {"title": _("Friendly name")}}, {"state": {"title": _("status"), "type": "dict", "dict": State.dictionary()}}, {"state_date": {"title": _("Status date"), "type": "datetime"}}, {"in_use": {"title": _("In Use")}}, {"source_host": {"title": _("Src Host")}}, {"source_ip": {"title": _("Src Ip")}}, {"owner": {"title": _("Owner")}}, {"actor_version": {"title": _("Actor version")}}, ]
def remove(self, uService): ''' Removes a uService element @return: the uService removed (marked for removal) ''' uService = UserService.objects.get(id=uService.id) logger.debug('Removing uService {0}'.format(uService)) if uService.isUsable() is False and State.isRemovable( uService.state) is False: raise OperationException(_('Can\'t remove a non active element')) ci = uService.getInstance() state = ci.destroy() uService.setState(State.REMOVING) UserServiceOpChecker.makeUnique(uService, ci, state)
def setState(self, state): ''' Updates the state of this object and, optionally, saves it Args: state: new State to store at record save: Defaults to true. If false, record will not be saved to db, just modified ''' logger.debug(' *** Setting state to {} from {} for {}'.format(State.toString(state), State.toString(self.state), self)) if state != self.state: self.state_date = getSqlDatetime() self.state = state
def isValidUser(self, username, falseIfNotExists=True): """ Checks the validity of an user Args: username: Name of the user to check falseIfNotExists: Defaults to True. It is used so we can return a value defined by caller. One example of falseIfNotExists using as True is for checking that the user is active or it doesn't exists. Returns: True if it exists and is active, falseIfNotExists (param) if it doesn't exists This is done so we can check non existing or non blocked users (state != Active, or do not exists) """ try: u = self.users.get(name=username) return State.isActive(u.state) except Exception: return falseIfNotExists
def isValidUser(self, username, falseIfNotExists=True): ''' Checks the validity of an user Args: username: Name of the user to check falseIfNotExists: Defaults to True. It is used so we can return a value defined by caller. One example of falseIfNotExists using as True is for checking that the user is active or it doesn't exists. Returns: True if it exists and is active, falseIfNotExists (param) if it doesn't exists This is done so we can check non existing or non blocked users (state != Active, or do not exists) ''' try: u = self.users.get(name=username) return State.isActive(u.state) except Exception: return falseIfNotExists
def running(self): self.setError('Unknown running transition from {}'.format(State.toString(self.userService.state)))
def isUsable(self): ''' Returns if this service is usable ''' return State.isUsable(self.state)
def isUsable(self): """ Returns if this service is usable """ return State.isUsable(self.state)
def getFields(self, parent): return [ {'name': {'title': _('Group'), 'visible': True, 'type': 'icon_dict', 'icon_dict': {'group': 'fa fa-group text-success', 'meta': 'fa fa-gears text-info'}}}, {'comments': {'title': _('Comments')}}, {'state': {'title': _('state'), 'type': 'dict', 'dict': State.dictionary()}}, ]
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))
def __str__(self): return 'Publication {0}, rev {1}, state {2}'.format( self.deployed_service.name, self.revision, State.toString(self.state))
def ticketAuth(request, ticketId): ''' Used to authenticate an user via a ticket ''' ticket = Ticket(ticketId) logger.debug('Ticket: {}'.format(ticket)) try: try: # Extract ticket.data from ticket.data storage, and remove it if success username = ticket.data['username'] groups = ticket.data['groups'] auth = ticket.data['auth'] realname = ticket.data['realname'] servicePool = ticket.data['servicePool'] password = ticket.data['password'] transport = ticket.data['transport'] except: logger.error('Ticket stored is not valid') raise InvalidUserException() # Remove ticket ticket.delete() auth = Authenticator.objects.get(uuid=auth) # If user does not exists in DB, create it right now # Add user to groups, if they exists... grps = [] for g in groups: try: grps.append(auth.groups.get(uuid=g)) except Exception: logger.debug('Group list has changed since ticket assignement') if len(grps) == 0: logger.error('Ticket has no valid groups') raise Exception('Invalid ticket authentication') usr = auth.getOrCreateUser(username, realname) if usr is None or State.isActive( usr.state) is False: # If user is inactive, raise an exception raise InvalidUserException() # Add groups to user (replace existing groups) usr.groups = grps # Right now, we assume that user supports java, let's see how this works # Force cookie generation webLogin(request, None, usr, password) request.user = usr # Temporarily store this user as "authenticated" user, next requests will be done using session # Check if servicePool is part of the ticket if servicePool is not None: servicePool = DeployedService.objects.get(uuid=servicePool) # Check if service pool can't be accessed by groups servicePool.validateUser(usr) if servicePool.isInMaintenance(): raise ServiceInMaintenanceMode() transport = Transport.objects.get(uuid=transport) response = service( request, 'F' + servicePool.uuid, transport.uuid) # 'A' Indicates 'assigned service' else: response = HttpResponsePermanentRedirect( reverse('uds.web.views.index')) # Now ensure uds cookie is at response getUDSCookie(request, response, True) return response except Authenticator.DoesNotExist: logger.error('Ticket has an non existing authenticator') return errors.error(request, InvalidUserException()) except DeployedService.DoesNotExist: logger.error('Ticket has an invalid Service Pool') return errors.error(request, InvalidServiceException()) except Exception as e: logger.exception('Exception') return errors.exceptionView(request, e)
def ticketAuth(request, ticketId): """ Used to authenticate an user via a ticket """ try: data = TicketStore.get(ticketId, invalidate=True) try: # Extract ticket.data from ticket.data storage, and remove it if success username = data['username'] groups = data['groups'] auth = data['auth'] realname = data['realname'] servicePool = data['servicePool'] password = cryptoManager().decrypt(data['password']) transport = data['transport'] except Exception: logger.error('Ticket stored is not valid') raise InvalidUserException() auth = Authenticator.objects.get(uuid=auth) # If user does not exists in DB, create it right now # Add user to groups, if they exists... grps = [] for g in groups: try: grps.append(auth.groups.get(uuid=g)) except Exception: logger.debug('Group list has changed since ticket assignment') if len(grps) == 0: logger.error('Ticket has no valid groups') raise Exception('Invalid ticket authentication') usr = auth.getOrCreateUser(username, realname) if usr is None or State.isActive(usr.state) is False: # If user is inactive, raise an exception raise InvalidUserException() # Add groups to user (replace existing groups) usr.groups.set(grps) # Force cookie generation webLogin(request, None, usr, password) request.user = usr # Temporarily store this user as "authenticated" user, next requests will be done using session request.session['ticket'] = '1' # Store that user access is done using ticket logger.debug("Service & transport: {}, {}".format(servicePool, transport)) for v in DeployedService.objects.all(): logger.debug("{} {}".format(v.uuid, v.name)) # Check if servicePool is part of the ticket if servicePool is not None: # If service pool is in there, also is transport res = userServiceManager().getService(request.user, request.os, request.ip, 'F' + servicePool, transport, False) _x, userService, _x, transport, _x = res transportInstance = transport.getInstance() if transportInstance.ownLink is True: link = reverse('TransportOwnLink', args=('A' + userService.uuid, transport.uuid)) else: link = html.udsAccessLink(request, 'A' + userService.uuid, transport.uuid) request.session['data'] = link; response = HttpResponseRedirect(reverse('page.index')) else: response = HttpResponsePermanentRedirect(reverse('page.index')) # Now ensure uds cookie is at response getUDSCookie(request, response, True) return response except ServiceNotReadyError as e: return errors.errorView(request, errors.SERVICE_NOT_READY) except TicketStore.InvalidTicket: return errors.errorView(request, errors.RELOAD_NOT_SUPPORTED) except Authenticator.DoesNotExist: logger.error('Ticket has an non existing authenticator') return errors.errorView(request, errors.ACCESS_DENIED) except DeployedService.DoesNotExist: logger.error('Ticket has an invalid Service Pool') return errors.errorView(request, errors.SERVICE_NOT_FOUND) except Exception as e: logger.exception('Exception') return errors.exceptionView(request, e)
def isPreparing(self): """ Returns if this service is in preparation (not ready to use, but in its way to be so...) """ return State.isPreparing(self.state)
class ServicesPools(ModelHandler): """ Handles Services Pools REST requests """ model = DeployedService detail = { 'services': AssignedService, 'cache': CachedService, 'groups': Groups, 'transports': Transports, 'publications': Publications, 'changelog': Changelog, 'access': AccessCalendars, 'actions': ActionsCalendars } save_fields = [ 'name', 'short_name', 'comments', 'tags', 'service_id', 'osmanager_id', 'image_id', 'servicesPoolGroup_id', 'initial_srvs', 'cache_l1_srvs', 'cache_l2_srvs', 'max_srvs', 'show_transports', 'visible', 'allow_users_remove', 'allow_users_reset', 'ignores_unused', 'account_id' ] remove_fields = ['osmanager_id', 'service_id'] table_title = _('Service Pools') table_fields = [ { 'name': { 'title': _('Name') } }, { 'state': { 'title': _('Status'), 'type': 'dict', 'dict': State.dictionary() } }, { 'user_services_count': { 'title': _('User services'), 'type': 'number' } }, { 'user_services_in_preparation': { 'title': _('In Preparation') } }, { 'usage': { 'title': _('Usage') } }, { 'visible': { 'title': _('Visible'), 'type': 'callback' } }, { 'show_transports': { 'title': _('Shows transports'), 'type': 'callback' } }, { 'pool_group_name': { 'title': _('Pool group') } }, { 'parent': { 'title': _('Parent service') } }, { 'tags': { 'title': _('tags'), 'visible': False } }, ] # Field from where to get "class" and prefix for that class, so this will generate "row-state-A, row-state-X, .... table_row_style = {'field': 'state', 'prefix': 'row-state-'} custom_methods = [('setFallbackAccess', True), ('getFallbackAccess', True), ('actionsList', True)] def item_as_dict(self, item): summary = 'summarize' in self._params # if item does not have an associated service, hide it (the case, for example, for a removed service) # Access from dict will raise an exception, and item will be skipped poolGroupId = None poolGroupName = _('Default') poolGroupThumb = DEFAULT_THUMB_BASE64 if item.servicesPoolGroup is not None: poolGroupId = item.servicesPoolGroup.uuid poolGroupName = item.servicesPoolGroup.name if item.servicesPoolGroup.image is not None: poolGroupThumb = item.servicesPoolGroup.image.thumb64 state = item.state if item.isInMaintenance(): state = State.MAINTENANCE elif userServiceManager().canInitiateServiceFromDeployedService( item) is False: state = State.SLOWED_DOWN val = { 'id': item.uuid, 'name': item.name, 'short_name': item.short_name, 'tags': [tag.tag for tag in item.tags.all()], 'parent': item.service.name, 'parent_type': item.service.data_type, 'comments': item.comments, 'state': state, 'thumb': item.image.thumb64 if item.image is not None else DEFAULT_THUMB_BASE64, 'account': item.account.name if item.account is not None else '', 'account_id': item.account.uuid if item.account is not None else None, 'service_id': item.service.uuid, 'provider_id': item.service.provider.uuid, 'image_id': item.image.uuid if item.image is not None else None, 'initial_srvs': item.initial_srvs, 'cache_l1_srvs': item.cache_l1_srvs, 'cache_l2_srvs': item.cache_l2_srvs, 'max_srvs': item.max_srvs, 'show_transports': item.show_transports, 'visible': item.visible, 'allow_users_remove': item.allow_users_remove, 'allow_users_reset': item.allow_users_reset, 'ignores_unused': item.ignores_unused, 'fallbackAccess': item.fallbackAccess, 'meta_member': [{ 'id': i.uuid, 'name': i.name } for i in item.meta.all()], } # Extended info if not summary: state = item.state if item.isInMaintenance(): state = State.MAINTENANCE elif userServiceManager().canInitiateServiceFromDeployedService( item) is False: state = State.SLOWED_DOWN poolGroupId = None poolGroupName = _('Default') poolGroupThumb = DEFAULT_THUMB_BASE64 if item.servicesPoolGroup is not None: poolGroupId = item.servicesPoolGroup.uuid poolGroupName = item.servicesPoolGroup.name if item.servicesPoolGroup.image is not None: poolGroupThumb = item.servicesPoolGroup.image.thumb64 val['state'] = state val['thumb'] = item.image.thumb64 if item.image is not None else DEFAULT_THUMB_BASE64 val['user_services_count'] = item.userServices.exclude( state__in=State.INFO_STATES).count() val['user_services_in_preparation'] = item.userServices.filter( state=State.PREPARING).count() val['tags'] = [tag.tag for tag in item.tags.all()] val['restrained'] = item.isRestrained() val['permission'] = permissions.getEffectivePermission( self._user, item) val['info'] = Services.serviceInfo(item.service) val['pool_group_id'] = poolGroupId val['pool_group_name'] = poolGroupName val['pool_group_thumb'] = poolGroupThumb val['usage'] = item.usage() if item.osmanager is not None: val['osmanager_id'] = item.osmanager.uuid return val # Gui related def getGui(self, type_): if OSManager.objects.count() < 1: # No os managers, can't create db raise ResponseError( ugettext( 'Create at least one OS Manager before creating a new service pool' )) if Service.objects.count() < 1: raise ResponseError( ugettext( 'Create at least a service before creating a new service pool' )) g = self.addDefaultFields([], ['name', 'short_name', 'comments', 'tags']) for f in [ { 'name': 'service_id', 'values': [gui.choiceItem('', '')] + gui.sortedChoices([ gui.choiceItem(v.uuid, v.provider.name + '\\' + v.name) for v in Service.objects.all() ]), 'label': ugettext('Base service'), 'tooltip': ugettext('Service used as base of this service pool'), 'type': gui.InputField.CHOICE_TYPE, 'rdonly': True, 'order': 100, # Ensueres is At end }, { 'name': 'osmanager_id', 'values': [gui.choiceItem(-1, '')] + gui.sortedChoices([ gui.choiceItem(v.uuid, v.name) for v in OSManager.objects.all() ]), 'label': ugettext('OS Manager'), 'tooltip': ugettext('OS Manager used as base of this service pool'), 'type': gui.InputField.CHOICE_TYPE, 'rdonly': True, 'order': 101, }, { 'name': 'show_transports', 'value': True, 'label': ugettext('Show transports'), 'tooltip': ugettext( 'If active, alternative transports for user will be shown' ), 'type': gui.InputField.CHECKBOX_TYPE, 'order': 110, 'tab': ugettext('Advanced'), }, { 'name': 'allow_users_remove', 'value': False, 'label': ugettext('Allow removal by users'), 'tooltip': ugettext( 'If active, the user will be allowed to remove the service "manually". Be careful with this, because the user will have the "power" to delete it\'s own service' ), 'type': gui.InputField.CHECKBOX_TYPE, 'order': 111, 'tab': ugettext('Advanced'), }, { 'name': 'allow_users_reset', 'value': False, 'label': ugettext('Allow reset by users'), 'tooltip': ugettext( 'If active, the user will be allowed to reset the service' ), 'type': gui.InputField.CHECKBOX_TYPE, 'order': 112, 'tab': ugettext('Advanced'), }, { 'name': 'ignores_unused', 'value': False, 'label': ugettext('Ignores unused'), 'tooltip': ugettext( 'If the option is enabled, UDS will not attempt to detect and remove the user services assigned but not in use.' ), 'type': gui.InputField.CHECKBOX_TYPE, 'order': 113, 'tab': ugettext('Advanced'), }, { 'name': 'image_id', 'values': [gui.choiceImage(-1, '--------', DEFAULT_THUMB_BASE64)] + gui.sortedChoices([ gui.choiceImage(v.uuid, v.name, v.thumb64) for v in Image.objects.all() ]), 'label': ugettext('Associated Image'), 'tooltip': ugettext('Image assocciated with this service'), 'type': gui.InputField.IMAGECHOICE_TYPE, 'order': 120, 'tab': ugettext('Display'), }, { 'name': 'servicesPoolGroup_id', 'values': [gui.choiceImage(-1, _('Default'), DEFAULT_THUMB_BASE64)] + gui.sortedChoices([ gui.choiceImage(v.uuid, v.name, v.thumb64) for v in ServicesPoolGroup.objects.all() ]), 'label': ugettext('Pool group'), 'tooltip': ugettext( 'Pool group for this pool (for pool classify on display)'), 'type': gui.InputField.IMAGECHOICE_TYPE, 'order': 121, 'tab': ugettext('Display'), }, { 'name': 'visible', 'value': True, 'label': ugettext('Visible'), 'tooltip': ugettext('If active, transport will be visible for users'), 'type': gui.InputField.CHECKBOX_TYPE, 'order': 107, 'tab': ugettext('Display'), }, { 'name': 'initial_srvs', 'value': '0', 'minValue': '0', 'label': ugettext('Initial available services'), 'tooltip': ugettext('Services created initially for this service pool'), 'type': gui.InputField.NUMERIC_TYPE, 'order': 130, 'tab': ugettext('Availability'), }, { 'name': 'cache_l1_srvs', 'value': '0', 'minValue': '0', 'label': ugettext('Services to keep in cache'), 'tooltip': ugettext( 'Services kept in cache for improved user service assignation' ), 'type': gui.InputField.NUMERIC_TYPE, 'order': 131, 'tab': ugettext('Availability'), }, { 'name': 'cache_l2_srvs', 'value': '0', 'minValue': '0', 'label': ugettext('Services to keep in L2 cache'), 'tooltip': ugettext( 'Services kept in cache of level2 for improved service generation' ), 'type': gui.InputField.NUMERIC_TYPE, 'order': 132, 'tab': ugettext('Availability'), }, { 'name': 'max_srvs', 'value': '0', 'minValue': '1', 'label': ugettext('Maximum number of services to provide'), 'tooltip': ugettext( 'Maximum number of service (assigned and L1 cache) that can be created for this service' ), 'type': gui.InputField.NUMERIC_TYPE, 'order': 133, 'tab': ugettext('Availability'), }, { 'name': 'show_transports', 'value': True, 'label': ugettext('Show transports'), 'tooltip': ugettext( 'If active, alternative transports for user will be shown' ), 'type': gui.InputField.CHECKBOX_TYPE, 'tab': ugettext('Advanced'), 'order': 130, }, { 'name': 'account_id', 'values': [gui.choiceItem(-1, '')] + gui.sortedChoices([ gui.choiceItem(v.uuid, v.name) for v in Account.objects.all() ]), 'label': ugettext('Accounting'), 'tooltip': ugettext('Account associated to this service pool'), 'type': gui.InputField.CHOICE_TYPE, 'tab': ugettext('Advanced'), 'order': 131, } ]: self.addField(g, f) return g def beforeSave(self, fields): # logger.debug(self._params) try: try: service = Service.objects.get( uuid=processUuid(fields['service_id'])) fields['service_id'] = service.id except: raise RequestError( ugettext('Base service does not exist anymore')) try: serviceType = service.getType() if serviceType.publicationType is None: self._params['publish_on_save'] = False if serviceType.canReset is False: self._params['allow_users_reset'] = False if serviceType.needsManager is True: osmanager = OSManager.objects.get( uuid=processUuid(fields['osmanager_id'])) fields['osmanager_id'] = osmanager.id else: del fields['osmanager_id'] if serviceType.cacheConstrains is not None: for k, v in serviceType.cacheConstrains.items(): fields[k] = v if serviceType.maxDeployed != -1: fields['max_srvs'] = min( (int(fields['max_srvs']), serviceType.maxDeployed)) fields['initial_srvs'] = min(int(fields['initial_srvs']), serviceType.maxDeployed) fields['cache_l1_srvs'] = min(int(fields['cache_l1_srvs']), serviceType.maxDeployed) if serviceType.usesCache is False: for k in ('initial_srvs', 'cache_l1_srvs', 'cache_l2_srvs', 'max_srvs'): fields[k] = 0 except Exception: raise RequestError( ugettext('This service requires an OS Manager')) # If max < initial or cache_1 or cache_l2 fields['max_srvs'] = max( (int(fields['initial_srvs']), int(fields['cache_l1_srvs']), int(fields['max_srvs']))) # *** ACCOUNT *** accountId = fields['account_id'] fields['account_id'] = None logger.debug('Account id: %s', accountId) if accountId != '-1': try: fields['account_id'] = Account.objects.get( uuid=processUuid(accountId)).id except Exception: logger.exception('Getting account ID') # **** IMAGE *** imgId = fields['image_id'] fields['image_id'] = None logger.debug('Image id: %s', imgId) try: if imgId != '-1': image = Image.objects.get(uuid=processUuid(imgId)) fields['image_id'] = image.id except Exception: logger.exception('At image recovering') # Servicepool Group spgrpId = fields['servicesPoolGroup_id'] fields['servicesPoolGroup_id'] = None logger.debug('servicesPoolGroup_id: %s', spgrpId) try: if spgrpId != '-1': spgrp = ServicesPoolGroup.objects.get( uuid=processUuid(spgrpId)) fields['servicesPoolGroup_id'] = spgrp.id except Exception: logger.exception('At service pool group recovering') except (RequestError, ResponseError): raise except Exception as e: raise RequestError(str(e)) def afterSave(self, item): if self._params.get('publish_on_save', False) is True: try: item.publish() except Exception: pass def deleteItem(self, item): try: logger.debug('Deleting %s', item) item.remove( ) # This will mark it for deletion, but in fact will not delete it directly except Exception: # Eat it and logit logger.exception('deleting service pool') # Logs def getLogs(self, item): try: return log.getLogs(item) except Exception: return [] # Set fallback status def setFallbackAccess(self, item): self.ensureAccess(item, permissions.PERMISSION_MANAGEMENT) fallback = self._params.get('fallbackAccess') if fallback != '': logger.debug('Setting fallback of %s to %s', item.name, fallback) item.fallbackAccess = fallback item.save() return item.fallbackAccess def getFallbackAccess(self, item): return item.fallbackAccess # Returns the action list based on current element, for calendar def actionsList(self, item): validActions = () itemInfo = item.service.getType() if itemInfo.usesCache is True: validActions += (CALENDAR_ACTION_INITIAL, CALENDAR_ACTION_CACHE_L1, CALENDAR_ACTION_MAX) if itemInfo.usesCache_L2 is True: validActions += (CALENDAR_ACTION_CACHE_L2, ) if itemInfo.publicationType is not None: validActions += (CALENDAR_ACTION_PUBLISH, ) # Transport & groups actions validActions += (CALENDAR_ACTION_ADD_TRANSPORT, CALENDAR_ACTION_DEL_TRANSPORT, CALENDAR_ACTION_ADD_GROUP, CALENDAR_ACTION_DEL_GROUP) return validActions