Пример #1
0
 def buildProtocol(self, addr):
     # construct protocol and wire up io signals
     self.protocol = SpoolingSignalProtocol('stdout', 'stderr', sender=self.host_string)
     return self.protocol
Пример #2
0
 def buildProtocol(self, addr):
     # construct protocol and wire up io signals
     self.protocol = SpoolingSignalProtocol('stdout',
                                            'stderr',
                                            sender=self.host_string)
     return self.protocol
Пример #3
0
class PlaitWorker(Factory):
    """
    Executes a sequence of tasks against a remote host.

    When run, an initial SSH connection is established to the remote host.
    For efficiency's sake, all subsequent remote operations reuse the
    same connection and execute over a new channel.

    Each task is executed in a daemon thread which will be killed when the
    main thread exits. When the task runs a remote operation it blocks on
    a call on the worker inside the main reactor thread where the network
    operations are negotiated. The result is then returned to the thread
    and it resumes execution.

    There are a number of signals emitted for workers:

      - timeout        : seconds
      - fail           : failure
      - connect        : user, host, port
      - task_start     : task
      - task_end       : result
      - stdout         : line
      - stderr         : line
      - complete       :

    """

    def __init__(self, tasks, keys, agent, known_hosts, timeout, all_tasks=False):
        self.proto = None
        self.host_string = None
        self.user = None
        self.host = None
        self.port = None
        self.tasks = tasks
        self.keys = keys
        self.agent = None
        self.known_hosts = None
        self.timeout = timeout
        self.all_tasks = all_tasks
        self.lines = 0
        self.tasks_by_uid = dict()

    def __str__(self):
        return self.host_string

    def buildProtocol(self, addr):
        # construct protocol and wire up io signals
        self.protocol = SpoolingSignalProtocol('stdout', 'stderr', sender=self.host_string)
        return self.protocol

    def makeConnectEndpoint(self):
        """
        Endpoint for initial SSH host connection.
        """
        return WorkerEndpoint.newConnection(
            reactor, b"cat",
            self.user, self.host, self.port,
            keys=self.keys, agentEndpoint=None,
            knownHosts=None, ui=QuietConsoleUI())

    def makeCommandEndpoint(self, command):
        """
        Endpoint for remotely executing operations.
        """
        return WorkerEndpoint.existingConnection(
            self.protocol.transport.conn, command.encode('utf8'))

    @defer.inlineCallbacks
    def connect(self, host_string):
        """
        Establish initial SSH connection to remote host.
        """
        self.parse_host_string(host_string)
        endpoint = self.makeConnectEndpoint()
        yield timeout(self.timeout, endpoint.connect(self))
        signal('worker_connect').send(self)

    def parse_host_string(self, host_string):
        self.host_string = host_string
        self.user, self.host, self.port = parse_host_string(host_string)

    @property
    def label(self):
        return "{}@{}".format(self.user, self.host)

    def stdout(self, thread_name, data=None):
        task = self.tasks_by_uid[thread_name]
        task.has_output = True
        signal('worker_stdout').send(self, data=data)

    def stderr(self, thread_name, data=None):
        task = self.tasks_by_uid[thread_name]
        task.has_output = True
        signal('worker_stderr').send(self, data=data)

    def runTask(self, task):
        # listen to the output of this task
        signal('stdout').connect(self.stdout, sender=task.uid)
        signal('stderr').connect(self.stderr, sender=task.uid)
        # signal that the task has begun
        signal('task_start').send(self, task=task)
        return task.run()

    @defer.inlineCallbacks
    def run(self):
        """
        Execute each task in a Task thread.
        """
        # execute each task in sequence
        for name, func, args, kwargs in self.tasks:
            task = Task(self, name, func, args, kwargs)
            self.tasks_by_uid[task.uid] = task
            result = yield self.runTask(task)
            # tasks will return an Exception is there was a failure
            if isinstance(result, BaseException):
                # wrap it so the runner recognizes this as an expected exception
                # and doesn't emit generic worker exception signals
                e = TaskError("Task `{}` failed.".format(name))
                e.task = task
                e.failure = result
                raise e
            # otherwise it may optionally return a completion value
            elif self.all_tasks and not (result or task.has_output):
                e = TaskError("Task returned empty result.")
                e.task = task
                e.failure = e
                raise e
            else:
                signal('task_finish').send(self, task=task, result=result)

    @defer.inlineCallbacks
    def execFromThread(self, command):
        """
        API for tasks to execute ssh commands.
        """
        ep = self.makeCommandEndpoint(command)
        yield ep.connect(self)
        failed = False
        try:
            yield self.protocol.finished
        except error.ProcessTerminated as e:
            failed = True
        # flush output from proto accumulated during execution
        stdout, stderr = self.protocol.flush()
        result = AttributeString(stdout)
        result.stderr = stderr
        result.failed = failed
        result.succeeded = not failed
        result.command = command
        defer.returnValue(result)
Пример #4
0
class PlaitWorker(Factory):
    """
    Executes a sequence of tasks against a remote host.

    When run, an initial SSH connection is established to the remote host.
    For efficiency's sake, all subsequent remote operations reuse the
    same connection and execute over a new channel.

    Each task is executed in a daemon thread which will be killed when the
    main thread exits. When the task runs a remote operation it blocks on
    a call on the worker inside the main reactor thread where the network
    operations are negotiated. The result is then returned to the thread
    and it resumes execution.

    There are a number of signals emitted for workers:

      - timeout        : seconds
      - fail           : failure
      - connect        : user, host, port
      - task_start     : task
      - task_end       : result
      - stdout         : line
      - stderr         : line
      - complete       :

    """
    def __init__(self,
                 tasks,
                 keys,
                 agent,
                 known_hosts,
                 timeout,
                 all_tasks=False):
        self.proto = None
        self.host_string = None
        self.user = None
        self.host = None
        self.port = None
        self.tasks = tasks
        self.keys = keys
        self.agent = None
        self.known_hosts = None
        self.timeout = timeout
        self.all_tasks = all_tasks
        self.lines = 0
        self.tasks_by_uid = dict()

    def __str__(self):
        return self.host_string

    def buildProtocol(self, addr):
        # construct protocol and wire up io signals
        self.protocol = SpoolingSignalProtocol('stdout',
                                               'stderr',
                                               sender=self.host_string)
        return self.protocol

    def makeConnectEndpoint(self):
        """
        Endpoint for initial SSH host connection.
        """
        return WorkerEndpoint.newConnection(reactor,
                                            b"cat",
                                            self.user,
                                            self.host,
                                            self.port,
                                            keys=self.keys,
                                            agentEndpoint=None,
                                            knownHosts=None,
                                            ui=QuietConsoleUI())

    def makeCommandEndpoint(self, command):
        """
        Endpoint for remotely executing operations.
        """
        return WorkerEndpoint.existingConnection(self.protocol.transport.conn,
                                                 command.encode('utf8'))

    @defer.inlineCallbacks
    def connect(self, host_string):
        """
        Establish initial SSH connection to remote host.
        """
        self.parse_host_string(host_string)
        endpoint = self.makeConnectEndpoint()
        yield timeout(self.timeout, endpoint.connect(self))
        signal('worker_connect').send(self)

    def parse_host_string(self, host_string):
        self.host_string = host_string
        self.user, self.host, self.port = parse_host_string(host_string)

    @property
    def label(self):
        return "{}@{}".format(self.user, self.host)

    def stdout(self, thread_name, data=None):
        task = self.tasks_by_uid[thread_name]
        task.has_output = True
        signal('worker_stdout').send(self, data=data)

    def stderr(self, thread_name, data=None):
        task = self.tasks_by_uid[thread_name]
        task.has_output = True
        signal('worker_stderr').send(self, data=data)

    def runTask(self, task):
        # listen to the output of this task
        signal('stdout').connect(self.stdout, sender=task.uid)
        signal('stderr').connect(self.stderr, sender=task.uid)
        # signal that the task has begun
        signal('task_start').send(self, task=task)
        return task.run()

    @defer.inlineCallbacks
    def run(self):
        """
        Execute each task in a Task thread.
        """
        # execute each task in sequence
        for name, func, args, kwargs in self.tasks:
            task = Task(self, name, func, args, kwargs)
            self.tasks_by_uid[task.uid] = task
            result = yield self.runTask(task)
            # tasks will return an Exception is there was a failure
            if isinstance(result, BaseException):
                # wrap it so the runner recognizes this as an expected exception
                # and doesn't emit generic worker exception signals
                e = TaskError("Task `{}` failed.".format(name))
                e.task = task
                e.failure = result
                raise e
            # otherwise it may optionally return a completion value
            elif self.all_tasks and not (result or task.has_output):
                e = TaskError("Task returned empty result.")
                e.task = task
                e.failure = e
                raise e
            else:
                signal('task_finish').send(self, task=task, result=result)

    @defer.inlineCallbacks
    def execFromThread(self, command):
        """
        API for tasks to execute ssh commands.
        """
        ep = self.makeCommandEndpoint(command)
        yield ep.connect(self)
        failed = False
        try:
            yield self.protocol.finished
        except error.ProcessTerminated as e:
            failed = True
        # flush output from proto accumulated during execution
        stdout, stderr = self.protocol.flush()
        result = AttributeString(stdout)
        result.stderr = stderr
        result.failed = failed
        result.succeeded = not failed
        result.command = command
        defer.returnValue(result)