# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from threading import Thread from Queue import Queue import Process class SSHConnection(): def __init__(self, host, num = None): self.host = host if num is None: self.hostname = host else: self.hostname = host + '-' + str(num); self.pwd = '/' self.env = {} self.path = [] def cd(self, path): self.pwd = path.format(host = self.hostname) def export(self, env, value): self.env[env] = value.format(host = self.hostname) def add_path(self, path): self.path.append(path.format(host = self.hostname)) def prefix(self, cmd): pre = [] pre.append('cd "{0}"'.format(self.pwd)) for (e, v) in self.env.iteritems(): pre.append('export {0}=\\"{1}\\"'.format(e, v)) for p in self.path: pre.append('export PATH="{0}:${{PATH}}"'.format(p)) pre.append(cmd) return ' && '.join(pre) def run(self, cmd, warn_only = False, quiet = False, vewy_quiet = False, abandon_output = True): # Don't use single quotes in `cmd`, this will break and end badly. cmd = cmd.format(host = self.hostname) cmd = self.prefix(cmd) print(self.hostname + ' =>') if vewy_quiet: # Be vewy, vewy quiet, I'm hunting wabbits. print('[command hidden]\n') quiet = True else: print(cmd + '\n') cmd = '''ssh '{0}' "bash -c '{1}'"'''.format(self.host, cmd) try: return Process.run(cmd, quiet, abandon_output) except Exception as e: if warn_only: print(str(e) + '---------- This was only a warning, ' + 'it won\'t stop the execution --\n') return None else: raise e class SSHSet(): def __init__(self, conn = []): self.conn = conn def __len__(self): return len(self.conn) def add(self, conn): if isinstance(conn, list): self.conn.extend(conn) else: self.conn.append(conn) def cd(self, path): for conn in self.conn: conn.cd(path) def export(self, env, value): for conn in self.conn: conn.export(env, value) def add_path(self, path): for conn in self.conn: conn.add_path(path) def run(self, cmd, parallel = True, quiet = False, vewy_quiet = False, abandon_output = True, warn_only = False): if not parallel: for conn in self.conn: conn.run(cmd, quiet = quiet, vewy_quiet = vewy_quiet, abandon_output = abandon_output, warn_only = warn_only) else: threads = [] queue = Queue() def wrapper(conn, cmd, queue): try: conn.run(cmd, quiet = quiet, vewy_quiet = vewy_quiet, abandon_output = abandon_output, warn_only = warn_only) except Exception as e: queue.put(Exception(conn.hostname + ' => ' + str(e))) for conn in self.conn: thread = Thread(target = wrapper, args = (conn, cmd, queue, )) thread.start() threads.append(thread) for thread in threads: thread.join() if not queue.empty(): l = [] while not queue.empty(): e = queue.get() l.append(str(e)); raise Exception('\n'.join(l))