Skip to content
cli.py 39.2 KiB
Newer Older
    @listed
        """ Start a remote shell on object matching the provided tql.
        :param tql: tql matching only one object on which start the shell
        :return: the label of the created tunnel
        objects = self.client.list(tql, show=('r', 'p'), method='shell')
        if not objects:
            raise NotImplementedError('No objects matched by query')
        elif len(objects) != 1:
            raise NotImplementedError('Shell only support one tunnel at time for now')
        errs = Reporter()
        for obj in objects:
            if obj['r'] in ('host', 'hv'):
                client = self.server.get_client(obj['id'])
                srv_to_host_tun = client.shell()
                cli_tun = self.client.register_tunnel('shell', client, srv_to_host_tun)
                errs.success(obj['id'], 'tunnel started.', output=cli_tun.label)
            else:
                errs.error(obj['id'], 'bad role')
        return errs.get_dict()

    @listed
    def resize(self, label, row, col, xpixel, ypixel):
        """ Send a resize event to the remote shell's tty.

        :param label: label of the tty tunnel to resize
        :param row: number of rows
        :param col: number of columns
        :param xpixel: unused
        :param ypixel: unused
        if label is None:
            tuns = [(c, st.label) for t, c, ct, st
                    in self.client.tunnels.values() if t == 'shell']
        else:
            ttype, client, ctun, stun = self.client.get_tunnel(label)
            if ttype != 'shell':
                raise ValueError('Label does not refers on a shell')
            tuns = [(client, stun.label)]

        for client, label in tuns:
            client.resize(label, row, col, xpixel, ypixel)
    def forward(self, login, port, destination='127.0.0.1'):
        """ Forward a TCP port to the client.

        :param login: login of the remote client on which establish the tunnel
        :param port: port on which establish the tunnel on destination
        :param destination: tunnel destination (from the remote client side)
        self.client.check('forward', query='id=%s' % login)
        # Create the tunnel to the node:
        try:
            host_client = self.server.get_client(login)
        except KeyError:
            raise KeyError('Specified client is not connected')
        s2n_tun = host_client.forward(port, destination)

        # Create tunnel to the CLI
        c2s_tun = self.client.register_tunnel('forward', host_client, s2n_tun)

        return c2s_tun.label
    @listed
    def dbstats(self):
        """ Get statistics about tql database.
        """
        return self.server.db.stats()

    def forward_call(self, login, func, *args, **kwargs):
        """ Forward a call to a connected client and return result.

        :param login: login of the connected client
        :param func: function to execute on the client
        :param \*args, \*\*kwargs: arguments of the call
        """
        self.client.check('forward_call')
        client = self.server.get_client(login)
        return client.conn.call(func, *args, **kwargs)


class CliClient(Client):

    """ A cli client connected to the cc-server.
    """

    ROLE = 'cli'
    RPC_HANDLER = CliHandler
    KILL_ALREADY_CONNECTED = True
    def __init__(self, *args, **kwargs):
        super(CliClient, self).__init__(*args, **kwargs)
        self._tunnels = {}  # Running tunnels for this client (as client)

    @property
    def tunnels(self):
        """ Get active client tunnels by label (a dict).
        """
        return self._tunnels

    def spawn_job(self, job_class, **kwargs):
        self._server.jobs.spawn(job_class, self.login, **kwargs)

    def register_tunnel(self, ttype, client, tun, label=None):
        """ Create and register a tunnel for this client.

        :param ttype: type of tunnel
        :param client: client where the tunnel go
        :param tun: the tunnel of this client
        :param label: label of the tunnel to create
        """
        def cb_on_shutdown(tun):
            # Call the default callback:
            tun.cb_default_on_shutdown(tun)
            # Delete the tunnel from the current running tunnels:
            self.unregister_tunnel(tun.label)

        ctun = self.conn.create_tunnel(label=label, endpoint=tun.socket,
                                       on_shutdown=cb_on_shutdown)
        self._tunnels[ctun.label] = (ttype, client, ctun, tun)
        return ctun

    def get_tunnel(self, label):
        """ Get the tunnel binded to the provided label.

        :return: a tuple (type, remote_client, tunnel, remote_client_tunnel)
            where: **type** is a string provided on tunnel creation,
            **remote_client** the client object of the remote client on which
            the tunnel is established, **tunnel** the cli-to-server tunnel
            object from the sjRpc, **remote_client_tunnel** the
            server-to-remote-client tunnel object from the sjRpc.
        """
        return self._tunnels[label]

    def unregister_tunnel(self, label):
        try:
            del self._tunnels[label]
        except KeyError:
            pass
Antoine Millet's avatar
Antoine Millet committed
    def wall(self, sender, message):
        """ Send a wall to the client.
        """
        self.conn.call('wall', sender, message)

Client.register_client_class(CliClient)