diff --git a/server/src/uds/core/managers/user_service.py b/server/src/uds/core/managers/user_service.py index 7c6c81b0e..4aa5d1ff6 100644 --- a/server/src/uds/core/managers/user_service.py +++ b/server/src/uds/core/managers/user_service.py @@ -67,6 +67,9 @@ from uds.web.util.errors import MAX_SERVICES_REACHED from .userservice import comms from .userservice.opchecker import UserServiceOpChecker +if typing.TYPE_CHECKING: + from uds import models + logger = logging.getLogger(__name__) traceLogger = logging.getLogger('traceLog') @@ -81,9 +84,8 @@ class UserServiceManager(metaclass=singleton.Singleton): UserServiceManager() ) # Singleton pattern will return always the same instance - @staticmethod - def getCacheStateFilter(servicePool: ServicePool, level: int) -> Q: - return Q(cache_level=level) & UserServiceManager.getStateFilter(servicePool) + def getCacheStateFilter(self, servicePool: ServicePool, level: int) -> Q: + return Q(cache_level=level) & self.getStateFilter(servicePool.service) @staticmethod def getStateFilter(servicePool: ServicePool) -> Q: @@ -96,21 +98,37 @@ class UserServiceManager(metaclass=singleton.Singleton): def _checkMaxDeployedReached(self, servicePool: ServicePool) -> None: """ - Checks if maxDeployed for the service has been reached, and, if so, - raises an exception that no more services of this kind can be reached + Returns the number of running user services for this service """ - serviceInstance = servicePool.service.getInstance() + return UserService.objects.filter( + self.getStateFilter(service) & Q(deployed_service__service=service) + ).count() + + def maximumUserServicesDeployed(self, service: 'models.Service') -> bool: + """ + Checks if the maximum number of user services for this service has been reached + """ + serviceInstance = service.getInstance() # Early return, so no database count is needed if serviceInstance.maxDeployed == services.Service.UNLIMITED: - return + return False numberOfServices = servicePool.userServices.filter( self.getStateFilter(servicePool) ).count() - if serviceInstance.maxDeployed <= numberOfServices: + return False + + def __checkMaxDeployedReached(self, servicePool: ServicePool) -> None: + """ + Checks if maxDeployed for the service has been reached, and, if so, + raises an exception that no more services of this kind can be reached + """ + if self.maximumUserServicesDeployed(servicePool.service): raise MaxServicesReachedError( - 'Max number of allowed deployments for service reached' + _('Maximum number of user services reached for this {}').format( + servicePool + ) ) def _createCacheAtDb( @@ -532,7 +550,7 @@ class UserServiceManager(metaclass=singleton.Singleton): if serviceType.usesCache: inAssigned = ( servicePool.assignedUserServices() - .filter(UserServiceManager.getStateFilter(servicePool)) + .filter(self.getStateFilter(servicePool.service)) .count() ) if ( @@ -550,12 +568,14 @@ class UserServiceManager(metaclass=singleton.Singleton): events.addEvent(servicePool, events.ET_CACHE_MISS, fld1=0) return self.createAssignedFor(servicePool, user) - def getServicesInStateForProvider(self, provider_id: int, state: str) -> int: + def getUserServicesInStatesForProvider( + self, provider: 'models.Provider', states: typing.List[str] + ) -> int: """ Returns the number of services of a service provider in the state indicated """ return UserService.objects.filter( - deployed_service__service__provider__id=provider_id, state=state + deployed_service__service__provider=provider, state__in=states ).count() def canRemoveServiceFromDeployedService(self, servicePool: ServicePool) -> bool: @@ -563,8 +583,8 @@ class UserServiceManager(metaclass=singleton.Singleton): checks if we can do a "remove" from a deployed service serviceIsntance is just a helper, so if we already have unserialized deployedService """ - removing = self.getServicesInStateForProvider( - servicePool.service.provider.id, State.REMOVING + removing = self.getUserServicesInStatesForProvider( + servicePool.service.provider, [State.REMOVING] ) serviceInstance = servicePool.service.getInstance() if ( @@ -579,12 +599,12 @@ class UserServiceManager(metaclass=singleton.Singleton): """ Checks if we can start a new service """ - preparing = self.getServicesInStateForProvider( - servicePool.service.provider.id, State.PREPARING + preparingForProvider = self.getUserServicesInStatesForProvider( + servicePool.service.provider, [State.PREPARING] ) serviceInstance = servicePool.service.getInstance() - if ( - preparing >= serviceInstance.parent().getMaxPreparingServices() + if self.maximumUserServicesDeployed(servicePool.service) or ( + preparingForProvider >= serviceInstance.parent().getMaxPreparingServices() and serviceInstance.parent().getIgnoreLimits() is False ): return False diff --git a/server/src/uds/core/workers/servicepools_cache_updater.py b/server/src/uds/core/workers/servicepools_cache_updater.py index 8a78ee529..4310dae57 100644 --- a/server/src/uds/core/workers/servicepools_cache_updater.py +++ b/server/src/uds/core/workers/servicepools_cache_updater.py @@ -140,7 +140,7 @@ class ServiceCacheUpdater(Job): ) inAssigned: int = ( servicePool.assignedUserServices() - .filter(userServiceManager().getStateFilter(servicePool)) + .filter(userServiceManager().getStateFilter(servicePool.service)) .count() ) # if we bypasses max cache, we will reduce it in first place. This is so because this will free resources on service provider