Exemple #1
0
class AgentProtocol(basic.Int32StringReceiver):
    MAX_LENGTH = 10000000
    def __init__(self):
        self.userInfo = UserInfo()
    
    def connectionMade(self):
        config.reload()
        # Send hello (info) message
        self._sendInfo(hello=True)
        
        self.update_loop = task.LoopingCall(self._sendUpdatedUserInfo)
        self.update_info_loop = task.LoopingCall(self._sendInfo)        
        def startLooping():
            # Check periodically if anything has changed, and send updated
            # information if necessary
            self.update_loop.start(8.0, True)
            # Send info, e.g. uptime and load every 10 seconds
            self.update_info_loop.start(10.0, False)
        # To avoid PotentialZombieWarning due to the reactor not running,
        # only start looping after the reactor has been started.
        reactor.callWhenRunning(startLooping)
    
    def requestReceived(self, data):
        log.debug('got request: %s' % data)
        req = 'error'
        reqid = 0
        try:
            request = json.loads(data)
            req = request['request']
            args = request['args']
            reqid = request['requestID']
            
            def sendResponse(data):
                self.sendResponse(req, reqid, data)
            
            handler = getattr(self, '_handle' + req.capitalize())
            deferred = defer.Deferred()
            deferred.addCallback(sendResponse)
        except (KeyError, ValueError, AttributeError):
            log.err()
            self.sendResponse(req, reqid, '', 'invalid request')
            return
        handler(deferred, args)
    
    stringReceived = requestReceived
    
    def sendResponse(self, request, requestID, data, error=''):
        response = json.dumps(
                              {'requestID': requestID,
                               'response': request,
                               'data': data,
                               'error': error
                              }
        )
        log.debug('sending response %s' % response)
        self.sendString(response)
    
    def _sendUpdatedUserInfo(self):
        """
        Called by a LoopingCall.
        Check if anything has changed, send updated list of users if necessary.
        """
        def cb(changed):
            if not changed:
                return
            def cbUsers(data):
                self.sendResponse('users', -1, data)
            d = defer.Deferred()
            d.addCallback(cbUsers)
            self._handleUsers(d, [])
            
        self.userInfo.updateUsersProcesses(cb)
    
    def _sendInfo(self, hello=False):
        """
        Called when agent is started and by a LoopingCall.
        Sends updated system information (see _handleInfo).
        """
        if hello:
            reqid = 0
        else:
            reqid = -2
        def cbInfo(data):
            self.sendResponse('info', reqid, data)
        
        d = defer.Deferred()
        d.addCallback(cbInfo)
        self._handleInfo(d, [])
        
    def _dictToUkey(self, udict):
        d = udict
        return (d['username'], d['client'], d['display'])
    
    def _genHandleArgs(self, args):
        """
        Generator which returns a tuple of ukey_dict and corresponding user object or None
        for each ukey in args.
        """
        for ukey_dict in args:
            try:
                ukey = self._dictToUkey(ukey_dict)
                user = self.userInfo.users[ukey]
                yield (ukey_dict, user)
            except KeyError:
                ukey_dict['error'] = 'notfound'
                yield (ukey_dict, None)
    
    def _genericUserRequestHandler(self, deferred, args, cbUser):
        """
        Generic request handler for user requests.
        cbUser is called with the user object and user data (user dict + optional fields)
        for each user, and should return user data or a deferred. 
        """
        def ebFailed(failure, udict):
            log.msg('unhandled failure: %s' % failure)
            udict['error'] = 'failed'
            return udict
        
        deferreds = []
        for udict, user in self._genHandleArgs(args):
            # if user is not found, user is None and udict['error'] is set
            d = defer.maybeDeferred(cbUser, user, udict)
            d.addErrback(ebFailed, udict)
            deferreds.append(d)
        
        def cbSuccess(result):
            deferred.callback([t[1] for t in result])
        
        dl = defer.DeferredList(deferreds)
        dl.addCallback(cbSuccess)
    
    # Request handlers 
    
    def _handleUsers(self, deferred, args):
        """
        Handle request for list of users.
        """
        def genData():
            for user in self.userInfo.users.itervalues():
                yield {'username': user.username, 'client': user.client,
                       'display': user.display, 'name': user.name,
                       'groups': user.groups, 'time': user.logintime,
                       'hwaddr': user.clientHWAddr}

        deferred.callback(list(genData()))
    
    def _handleInfo(self, deferred, args):
        """
        Handle info request, currently this returns uptime and load and OS.
        """
        def reply(bootTime):
            deferred.callback({'uptime': bootTime,
                               'load': self.userInfo.getLoad(),
                               'os': self.userInfo.getOS()})
        db = defer.maybeDeferred(self.userInfo.getBootTime)
        db.addCallback(reply)
        
    def _handleProcesses(self, deferred, args):
        def get(user, udata):
            if user:
                udata['processes'] = self.userInfo.processes[user.username]
            else:
                udata['processes'] = []
            return udata
        
        def cbChanged(changed):
            self._genericUserRequestHandler(deferred, args, get)
        
        self.userInfo.updateUsersProcesses(cbChanged)
    
    def _handleKillprocesses(self, deferred, args):
        """
        Handle request to kill processes.
        """
        user_pid = {}
        def get(user, udata):
            if user:
                pid = int(udata['pid'])
                if pid in user_pid[user.username]:
                    self.userInfo.killProcess(user, pid)
            del udata['pid']
            return udata
        
        def cbChanged(changed):
            for user in self.userInfo.users.itervalues():
                for pid, cmd in self.userInfo.processes[user.username]:
                    user_pid.setdefault(user.username, {})[pid] = 0
            self._genericUserRequestHandler(deferred, args, get)
        self.userInfo.updateUsersProcesses(cbChanged)
    
    def _handleThumbnails(self, deferred, args):
        """
        Handle thumbnails request.
        """
        def get(user, udata):
            def cbSuccess(thumbnail):
                from base64 import b64encode
                udata['thumbnail'] = b64encode(thumbnail)
                return udata
            
            if user:
                d = self.userInfo.getThumbnail(user)
                d.addCallback(cbSuccess)
                return d
            else:
                return udata
        
        self._genericUserRequestHandler(deferred, args, get)
    
    def _handleVnc(self, deferred, args):
        """
        Handle VNC request.
        """
        
        def get(user, udata):
            def errorHandler(failure):
                log.msg('getVNC failed: %s' % failure)
                udata['error'] = 'failed'
                return udata
            def cbSuccess(result):
                port, password = result
                udata['port'] = port
                udata['password'] = password
                return udata
            if not user:
                return udata
            d = self.userInfo.getVNC(user)
            d.addCallback(cbSuccess)
            d.addErrback(errorHandler)
            return d
        
        self._genericUserRequestHandler(deferred, args, get)
    
    def _handleLogin(self, deferred, args):
        """
        Handle login request (e.g. VNC + XDMCP on linux or RDP on windows)
        """
        port, protocol = self.userInfo.getLogin()
        deferred.callback({'port': port, 'protocol': protocol})
    
    def _handleMessage(self, deferred, args):
        def get(user, udata):
            if user:
                self.userInfo.sendMessage(user, udata['message'])
            return udata
        
        self._genericUserRequestHandler(deferred, args, get)
    
    def _handleLogout(self, deferred, args):
        def get(user, udata):
            if user:
                self.userInfo.logoutUser(user)
            return udata
        
        self._genericUserRequestHandler(deferred, args, get)
    
    def _handleLock(self, deferred, args):
        def get(user, udata):
            if user:
                self.userInfo.lockUser(user)
            return udata
        
        self._genericUserRequestHandler(deferred, args, get)
    
    def _handleOpenurl(self, deferred, args):
        def get(user, udata):
            if user:
                try:
                    self.userInfo.openURL(user, udata['url'])
                except NotImplementedError:
                    udata['error'] = 'notimplemented'
            return udata
        
        self._genericUserRequestHandler(deferred, args, get)
    
    def _handleShutdown(self, deferred, args):
        """
        Handle shutdown request
        """
        action = args['action']
        try:
            if action == 'poweroff':
                self.userInfo.shutdown()
            else:
                self.userInfo.reboot()
        except NotImplementedError:
            deferred.callback({'error': 'notimplemented'})
        else:                   
            deferred.callback({})