def run_command(command, hosts, max_threads=DFLT_MAX_THREADS, analyse=None): ''' Run a command over a set of hosts using threading. A working SSH agent is needed for authentication. @param command: the command to be executed on the specified hosts. @type command: string @param hosts: the hosts on which the command is to be executed @type hosts: list @param max_threads: the number of concurrent threads used to interact with nodes @type max_threads: int @param analyse: a function which can be called to analyse the results of the execution of the command for each host. Argument of the function should be a L{NodeResult} variable. @type analyse: function @return: a L{NodeResultSet} with results for all hosts @rtype: NodeResultSet ''' agent = Agent() queue = Queue.Queue() threads = {} # fork enough (but not too many) threads for i in range(min(max_threads, len(hosts))): threads[i] = NodeCommandThread(queue, command, agent, analyse=analyse) threads[i].setDaemon(True) threads[i].start() # add all hosts to the work queue for host in hosts: queue.put(host) # wait for threads to be done queue.join() # fix for some threading problems time.sleep(1) # gather results result = NodeResultSet() for i in range(min(max_threads, len(hosts))): result.append(threads[i].get_result()) return result
def __init__(self, queue, command, agent, timeout=DFLT_SSH_TIMEOUT, analyse=None): """ Create a new NodeCommandThread object. @param queue: a list of nodes on which the commands is to be executed @type queue: list of strings @param command: the command to be executed @type command: string @param agent: a I{paramiko.Agent} SSH-agent object. @type agent: I{paramiko.Agent} object @param timeout: the SSH timeout in seconds @type timeout: integer @param analyse: callback analyse function. This function is called after the command has been executed. Argument for the function is a L{NodeResult} object. @type analyse: function """ self.queue = queue self.command = command self.agent = agent self.timeout = timeout self.result = NodeResultSet() self.analyse = analyse threading.Thread.__init__(self)
class NodeCommandThread(threading.Thread): ''' a thread for processing commands to a node via SSH ''' def __init__(self, queue, command, agent, timeout=DFLT_SSH_TIMEOUT, analyse=None): """ Create a new NodeCommandThread object. @param queue: a list of nodes on which the commands is to be executed @type queue: list of strings @param command: the command to be executed @type command: string @param agent: a I{paramiko.Agent} SSH-agent object. @type agent: I{paramiko.Agent} object @param timeout: the SSH timeout in seconds @type timeout: integer @param analyse: callback analyse function. This function is called after the command has been executed. Argument for the function is a L{NodeResult} object. @type analyse: function """ self.queue = queue self.command = command self.agent = agent self.timeout = timeout self.result = NodeResultSet() self.analyse = analyse threading.Thread.__init__(self) def run(self): """ Execution of the thread. """ # read default SSH config ssh = SSHClient() conf = SSHConfig() # use the local SSH configuration for keys, known hosts, etc conf.parse(open(os.path.join(os.environ['HOME'], '.ssh', 'config'), 'r')) ssh.set_missing_host_key_policy(AutoAddPolicy()) ssh.load_system_host_keys() # continue to process hosts until the queue is empty while True: try: starttime = time.time() # pick the next available host host = self.queue.get() result = NodeResult(host) node = RingNode(host) try: # some template replacements cmd = self.command.replace("%%HOST%%", host) result = node.run_command(cmd) except RingException, e: result.set_ssh_result(NodeResult.SSH_ERROR) result.set_ssh_errormsg(e.__str__()) finally: node.close() if self.analyse: self.analyse(result) result.add_value('runtime', time.time() - starttime) self.result.append(result) except Queue.Empty: # we're done! pass finally: