def getMeta( self, user: User, srcIp: str, os: typing.MutableMapping, idMetaPool: str, clientHostName: typing.Optional[str] = None ) -> typing.Tuple[typing.Optional[str], UserService, typing.Optional['services.UserDeployment'], Transport, typing.Optional[transports.Transport]]: logger.debug('This is meta') # We need to locate the service pool related to this meta, and also the transport # First, locate if there is a service in any pool associated with this metapool meta: MetaPool = MetaPool.objects.get(uuid=idMetaPool) # If access is denied by calendar... if meta.isAccessAllowed() is False: raise ServiceAccessDeniedByCalendar() # Sort pools based on meta selection if meta.policy == MetaPool.PRIORITY_POOL: sortPools = [(p.priority, p.pool) for p in meta.members.all()] elif meta.policy == MetaPool.MOST_AVAILABLE_BY_NUMBER: sortPools = [(p.pool.usage(), p.pool) for p in meta.members.all()] else: sortPools = [(random.randint(0, 10000), p.pool) for p in meta.members.all()] # Just shuffle them # Sort pools related to policy now, and xtract only pools, not sort keys # Remove "full" pools (100%) from result and pools in maintenance mode, not ready pools, etc... pools: typing.List[ServicePool] = [ p[1] for p in sorted(sortPools, key=lambda x: x[0]) if p[1].usage() < 100 and p[1].isUsable() ] logger.debug('Pools: %s', pools) usable: typing.Optional[typing.Tuple[ServicePool, Transport]] = None # Now, Lets find first if there is one assigned in ANY pool def ensureTransport( pool: ServicePool ) -> typing.Optional[typing.Tuple[ServicePool, Transport]]: found = None t: Transport for t in pool.transports.all().order_by('priority'): typeTrans = t.getType() if t.getType() and t.validForIp( srcIp) and typeTrans.supportsOs( os['OS']) and t.validForOs(os['OS']): found = (pool, t) break return found try: alreadyAssigned: UserService = UserService.objects.filter( deployed_service__in=pools, state__in=State.VALID_STATES, user=user, cache_level=0).order_by('deployed_service__name')[0] logger.debug('Already assigned %s', alreadyAssigned) # Ensure transport is available for the OS, and store it usable = ensureTransport(alreadyAssigned.deployed_service) # Found already assigned, ensure everythinf is fine if usable: return self.getService(user, os, srcIp, 'F' + usable[0].uuid, usable[1].uuid, doTest=False, clientHostname=clientHostName) except Exception: # No service already assigned, lets find a suitable one for pool in pools: # Pools are already sorted, and "full" pools are filtered out # Ensure transport is available for the OS usable = ensureTransport(pool) # Stop if a pool-transport is found and can be assigned to user if usable: try: usable[0].validateUser(user) return self.getService(user, os, srcIp, 'F' + usable[0].uuid, usable[1].uuid, doTest=False, clientHostname=clientHostName) except Exception as e: logger.info( 'Meta service %s:%s could not be assigned, trying a new one', usable[0].name, e) usable = None log.doLog( meta, log.WARN, "No user service accessible from device (ip {}, os: {})".format( srcIp, os['OS']), log.SERVICE) raise InvalidServiceException( _('The service is not accessible from this device'))
def getService( # pylint: disable=too-many-locals, too-many-branches, too-many-statements self, user: User, os: typing.MutableMapping, srcIp: str, idService: str, idTransport: str, doTest: bool = True, clientHostname: typing.Optional[str] = None ) -> typing.Tuple[typing.Optional[str], UserService, typing.Optional['services.UserDeployment'], Transport, typing.Optional[transports.Transport]]: """ Get service info from user service """ if idService[0] == 'M': # Meta pool return self.getMeta(user, srcIp, os, idService[1:]) userService = self.locateUserService(user, idService, create=True) if not userService: raise InvalidServiceException( _('Invalid service. The service is not available at this moment. Please, try later' )) # Early log of "access try" so we can imagine what is going on userService.setConnectionSource(srcIp, clientHostname or srcIp) if userService.isInMaintenance(): raise ServiceInMaintenanceMode() if not userService.deployed_service.isAccessAllowed(): raise ServiceAccessDeniedByCalendar() if not idTransport: # Find a suitable transport t: Transport for t in userService.deployed_service.transports.order_by( 'priority'): typeTrans = t.getType() if t.validForIp(srcIp) and typeTrans.supportsOs( os['OS']) and t.validForOs(os['OS']): idTransport = t.uuid break try: transport: Transport = Transport.objects.get(uuid=idTransport) except Exception: raise InvalidServiceException() # Ensures that the transport is allowed for this service if userService.deployed_service.transports.filter( id=transport.id).count() == 0: raise InvalidServiceException() # If transport is not available for the request IP... if not transport.validForIp(srcIp): msg = _('The requested transport {} is not valid for {}').format( transport.name, srcIp) logger.error(msg) raise InvalidServiceException(msg) userName = user.name if user else 'unknown' if not doTest: # traceLogger.info('GOT service "{}" for user "{}" with transport "{}" (NOT TESTED)'.format(userService.name, userName, trans.name)) return None, userService, None, transport, None serviceNotReadyCode = 0x0001 ip = 'unknown' # Test if the service is ready if userService.isReady(): serviceNotReadyCode = 0x0002 log.doLog( userService, log.INFO, "User {0} from {1} has initiated access".format( user.name, srcIp), log.WEB) # If ready, show transport for this service, if also ready ofc userServiceInstance = userService.getInstance() ip = userServiceInstance.getIp() userService.logIP(ip) # Update known ip logger.debug('IP: %s', ip) if self.checkUuid( userService ) is False: # The service is not the expected one serviceNotReadyCode = 0x0004 log.doLog( userService, log.WARN, "User service is not accessible due to invalid UUID (ip {0})" .format(ip), log.TRANSPORT) logger.debug('UUID check failed for user service %s', userService) else: events.addEvent(userService.deployed_service, events.ET_ACCESS, username=userName, srcip=srcIp, dstip=ip, uniqueid=userService.unique_id) if ip: serviceNotReadyCode = 0x0003 transportInstance = transport.getInstance() if transportInstance.isAvailableFor(userService, ip): # userService.setConnectionSource(srcIp, 'unknown') log.doLog(userService, log.INFO, "User service ready", log.WEB) self.notifyPreconnect( userService, transportInstance.processedUser(userService, user), transportInstance.protocol) traceLogger.info( 'READY on service "%s" for user "%s" with transport "%s" (ip:%s)', userService.name, userName, transport.name, ip) return ip, userService, userServiceInstance, transport, transportInstance message = transportInstance.getCustomAvailableErrorMsg( userService, ip) log.doLog(userService, log.WARN, message, log.TRANSPORT) logger.debug( 'Transport is not ready for user service %s: %s', userService, message) else: logger.debug('Ip not available from user service %s', userService) else: log.doLog( userService, log.WARN, "User {} from {} tried to access, but service was not ready". format(user.name, srcIp), log.WEB) traceLogger.error( 'ERROR %s on service "%s" for user "%s" with transport "%s" (ip:%s)', serviceNotReadyCode, userService.name, userName, transport.name, ip) raise ServiceNotReadyError(code=serviceNotReadyCode, service=userService, transport=transport)