Exemplo n.º 1
0
 def validatePublication(self) -> None:
     """
     Ensures that, if this service has publications, that a publication is active
     raises an IvalidServiceException if check fails
     """
     if self.activePublication() is None and self.service.getType().publicationType is not None:
         raise InvalidServiceException()
Exemplo n.º 2
0
 def validateTransport(self, transport) -> None:
     try:
         self.transports.get(id=transport.id)
     except:
         raise InvalidServiceException()
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
    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'))
Exemplo n.º 5
0
    def getAssignationForUser(
        self, servicePool: ServicePool, user: User
    ) -> typing.Optional[UserService]:  # pylint: disable=too-many-branches
        if servicePool.service.getInstance().spawnsNew is False:
            assignedUserService = self.getExistingAssignationForUser(
                servicePool, user)
        else:
            assignedUserService = None

        # If has an assigned user service, returns this without any more work
        if assignedUserService:
            return assignedUserService

        if servicePool.isRestrained():
            raise InvalidServiceException(
                _('The requested service is restrained'))

        cache: typing.Optional[UserService] = None
        # Now try to locate 1 from cache already "ready" (must be usable and at level 1)
        with transaction.atomic():
            caches = typing.cast(
                typing.List[UserService],
                servicePool.cachedUserServices().select_for_update().filter(
                    cache_level=services.UserDeployment.L1_CACHE,
                    state=State.USABLE,
                    os_state=State.USABLE)[:1])
            if caches:
                cache = caches[0]
                # Ensure element is reserved correctly on DB
                if servicePool.cachedUserServices().select_for_update().filter(
                        user=None, uuid=typing.cast(
                            UserService, cache).uuid).update(
                                user=user, cache_level=0) != 1:
                    cache = None
            else:
                cache = None

        # Out of previous atomic
        if not cache:
            with transaction.atomic():
                caches = typing.cast(
                    typing.List[UserService],
                    servicePool.cachedUserServices().select_for_update().
                    filter(cache_level=services.UserDeployment.L1_CACHE,
                           state=State.USABLE)[:1])
                if cache:
                    cache = caches[0]
                    if servicePool.cachedUserServices().select_for_update(
                    ).filter(user=None,
                             uuid=typing.cast(UserService, cache).uuid).update(
                                 user=user, cache_level=0) != 1:
                        cache = None
                else:
                    cache = None

        # Out of atomic transaction
        if cache:
            # Early assign
            cache.assignToUser(user)

            logger.debug(
                'Found a cached-ready service from %s for user %s, item %s',
                servicePool, user, cache)
            events.addEvent(servicePool,
                            events.ET_CACHE_HIT,
                            fld1=servicePool.cachedUserServices().filter(
                                cache_level=services.UserDeployment.L1_CACHE,
                                state=State.USABLE).count())
            return cache

        # Cache missed

        # Now find if there is a preparing one
        with transaction.atomic():
            caches = servicePool.cachedUserServices().select_for_update(
            ).filter(cache_level=services.UserDeployment.L1_CACHE,
                     state=State.PREPARING)[:1]
            if caches:
                cache = caches[0]
                if servicePool.cachedUserServices().select_for_update().filter(
                        user=None, uuid=typing.cast(
                            UserService, cache).uuid).update(
                                user=user, cache_level=0) != 1:
                    cache = None
            else:
                cache = None

        # Out of atomic transaction
        if cache:
            cache.assignToUser(user)

            logger.debug(
                'Found a cached-preparing service from %s for user %s, item %s',
                servicePool, user, cache)
            events.addEvent(servicePool,
                            events.ET_CACHE_MISS,
                            fld1=servicePool.cachedUserServices().filter(
                                cache_level=services.UserDeployment.L1_CACHE,
                                state=State.PREPARING).count())
            return cache

        # Can't assign directly from L2 cache... so we check if we can create e new service in the limits requested
        serviceType = servicePool.service.getType()
        if serviceType.usesCache:
            # inCacheL1 = ds.cachedUserServices().filter(UserServiceManager.getCacheStateFilter(services.UserDeployment.L1_CACHE)).count()
            inAssigned = servicePool.assignedUserServices().filter(
                UserServiceManager.getStateFilter()).count()
            # totalL1Assigned = inCacheL1 + inAssigned
            if inAssigned >= servicePool.max_srvs:  # cacheUpdater will drop unnecesary L1 machines, so it's not neccesary to check against inCacheL1
                log.doLog(
                    servicePool, log.WARN,
                    'Max number of services reached: {}'.format(
                        servicePool.max_srvs), log.INTERNAL)
                raise MaxServicesReachedError()

        # Can create new service, create it
        events.addEvent(servicePool, events.ET_CACHE_MISS, fld1=0)
        return self.createAssignedFor(servicePool, user)