title: Multiple SSH Connections in a Python 2.7 script- Multiprocessing Vs Threading
body: <p>I have a script that gets a list of nodes as an argument (could be 10 or even 50), and connects to each by SSH to run a service restart command.
  At the moment, I'm using multiprocessing in order to parallelize the script (getting the batch size as an argument as well), however I've heard that threading module could help me with performing my tasks in a quicker and easier to manage way (I'm using <code>try..except</code> KeyboardInterrupt with <code>sys.exit()</code> and <code>pool.terminate()</code>, but it won't stop the entire script because it's a different process).
  Since I understand the multithreading is more lightweight and easier to manage for my case, I am trying to convert my script to use threading instead of multiprocessing but it doesn't properly work.</p>
  
  <p>The current code in multiprocessing (works):</p>
  
  <pre><code>def restart_service(node, initd_tup):
      """
      Get a node name as an argument, connect to it via SSH and run the service restart command..
      """
      command = 'service {0} restart'.format(initd_tup[node])
      logger.info('[{0}] Connecting to {0} in order to restart {1} service...'.format(node, initd_tup[node]))
      try:
          ssh.connect(node)
          stdin, stdout, stderr = ssh.exec_command(command)
          result = stdout.read()
          if not result:
              result_err = stderr.read()
              print '{0}{1}[{2}] ERROR: {3}{4}'.format(Color.BOLD, Color.RED, node, result_err, Color.END)
              logger.error('[{0}]  Result of command {1} output: {2}'.format(node, command, result_err))
          else:
              print '{0}{1}{2}[{3}]{4}\n{5}'.format(Color.BOLD, Color.UNDERLINE, Color.GREEN, node, Color.END, result)
              logger.info('[{0}]  Result of command {1} output: {2}'.format(node, command, result.replace("\n", "... ")))
          ssh.close()
      except paramiko.AuthenticationException:
          print "{0}{1}ERROR! SSH failed with Authentication Error. Make sure you run the script as root and try again..{2}".format(Color.BOLD, Color.RED, Color.END)
          logger.error('SSH Authentication failed, thrown error message to the user to make sure script is run with root permissions')
          pool.terminate()
      except socket.error as error:
          print("[{0}]{1}{2} ERROR! SSH failed with error: {3}{4}\n".format(node, Color.RED, Color.BOLD, error, Color.END))
          logger.error("[{0}] SSH failed with error: {1}".format(node, error))
      except KeyboardInterrupt:
          pool.terminate()
          general_utils.terminate(logger)
  
  
  def convert_to_tuple(a_b):
      """Convert 'f([1,2])' to 'f(1,2)' call."""
      return restart_service(*a_b)
  
  
  def iterate_nodes_and_call_exec_func(nodes_list):
      """
      Iterate over the list of nodes to process,
      create a list of nodes that shouldn't exceed the batch size provided (or 1 if not provided).
      Then using the multiprocessing module, call the restart_service func on x nodes in parallel (where x is the batch size).
      If batch_sleep arg was provided, call the sleep func and provide the batch_sleep argument between each batch.
      """
      global pool
      general_utils.banner('Initiating service restart')
      pool = multiprocessing.Pool(10)
      manager = multiprocessing.Manager()
      work = manager.dict()
      for line in nodes_list:
          work[line] = general_utils.get_initd(logger, args, line)
          if len(work) &gt;= int(args.batch):
              pool.map(convert_to_tuple, itertools.izip(work.keys(), itertools.repeat(work)))
              work = {}
              if int(args.batch_sleep) &gt; 0:
                  logger.info('*** Sleeping for %d seconds before moving on to next batch ***', int(args.batch_sleep))
                  general_utils.sleep_func(int(args.batch_sleep))
      if len(work) &gt; 0:
          try:
              pool.map(convert_to_tuple, itertools.izip(work.keys(), itertools.repeat(work)))
          except KeyboardInterrupt:
              pool.terminate()
              general_utils.terminate(logger)
  </code></pre>
  
  <p>And here's what I've tried to to with Threading, which doesn't work (when I assign a batch_size larger than 1, the script simply gets stuck and I have to kill it forcefully. </p>
  
  <pre><code>def parse_args():
      """Define the argument parser, and the arguments to accept.."""
      global args, parser
      parser = MyParser(description=__doc__)
      parser.add_argument('-H', '--host', help='List of hosts to process, separated by "," and NO SPACES!')
      parser.add_argument('--batch', help='Do requests in batches', default=1)
      args = parser.parse_args()
  
      # If no arguments were passed, print the help file and exit with ERROR..
      if len(sys.argv) == 1:
          parser.print_help()
          print '\n\nERROR: No arguments passed!\n'
          sys.exit(3)
  
  
  def do_work(node):
      logger.info('[{0}]'.format(node))
      try:
          ssh.connect(node)
          stdin, stdout, stderr = ssh.exec_command('hostname ; date')
          print stdout.read()
          ssh.close()
      except:
          print 'ERROR!'
          sys.exit(2)
  
  
  def worker():
      while True:
          item = q.get()
          do_work(item)
          q.task_done()
  
  
  def iterate():
      for item in args.host.split(","):
          q.put(item)
  
      for i in range(int(args.batch)):
          t = Thread(target=worker)
          t.daemon = True
          t.start()
  
      q.join()
  
  
  def main():
      parse_args()
      try:
          iterate()
  
      except KeyboardInterrupt:
          exit(1)
  </code></pre>
  
  <p>In the script log I see a WARNING generated by Paramiko as below:</p>
  
  <p><code>2016-01-04 22:51:37,613  WARNING: Oops, unhandled type 3</code></p>
  
  <p>I tried to Google this <code>unhandled type 3</code> error, but didn't find anything related to my issue, since it's talking about 2 factor authentication or trying to connect via both password and SSH key at the same time, but I'm only loading the host keys without providing any password to the SSH Client.</p>
  
  <p>I would appreciate any help on this matter..</p>