title: Automate `lxc-attach` through ssh with Python
body: <h2>Question:</h2>
  <p>How do I automate this process, and include all of the password prompts?</p>
  <pre><code>Machine 1&gt; ssh user2@machine2
     password:
  Machine 2&gt; lxc-attach -n 0x1000
  Container&gt; ssh user3@machine3
     password:
  Machine 3&gt; get_temperature.sh
     temperature is 65C
  </code></pre>
  <h2>Background:</h2>
  <p>I am running some automated scripts on a computer (Machine 1) with several other test systems cascaded from this one. Only the first computer has internet access and stable state. Anything can be installed here and the automation is mostly python.</p>
  <p>The other test systems are not connected to the internet, are shutdown unexpectedly, and have their memory wiped often. Because of this, any settings I change or ssh keys I load outside of this automated script are not retained. I am just testing the hardware; none of this will be in production anywhere, and I don't have any way to change the state it is reset to.</p>
  <p><a href="https://i.stack.imgur.com/ykVNd.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ykVNd.png" alt="Diagram of Cascaded System" /></a></p>
  <h2>Attempt:</h2>
  <p>I have tried to solve this running paramiko on Machine 1. I can connect to Machine 2, but I have no way to answer the password prompt when connecting to Machine 3 from the Machine 2. This is what I have tried:</p>
  <pre><code>import paramiko
  
  ssh = paramiko.SSHClient()
  ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  
  ssh.connect('192.168.2.2', port=22, username='user2', password='pass2')
  print(&quot;connect to machine 2&quot;)
  
  channel = ssh.invoke_shell()
  channel_data = ''
  go = 1
  
  while True: 
      if channel.recv_ready():
          channel_data = str(channel.recv(9999))
          print('Channel Data: ', channel_data)
      else:
          continue
  
      if channel_data.endswith(&quot;~ &gt;'&quot;):
          if go == 1:
              channel.send('lxc-attach -n 0x1000')
              channel.send('\n')
              go += 1
          else: 
              channel.send('ssh user3@192.168.3.2')
              print(&quot;sending ssh command&quot;)
      elif 'assword:' in channel_data:
          channel.send('pass3')
          channel.send('\n')
          print(&quot;put the pass&quot;)
          break
  
  stdin, stdout, stderr = ssh.exec_command('pwd')
  print(stdout)
  </code></pre>
  <p>Next I tried using fabric to jump from one host to another. If I try to open a connection to Machine 3 or 4 with gateway settings, this exits with the error: <code>ChannelException(2, 'Connect failed')</code>. If I try to run the lxc-attach command first, it just hangs forever.</p>
  <pre><code>import fabric
  
  machine2 = fabric.Connection('192.168.2.2', user='user2', connect_kwargs={&quot;password&quot;: &quot;pass2&quot;})
  result = machine2.run('hostname')
  print('machine2 hostname', result)
  result = machine2.run('lxc-attach -n 0x1000')
  print('machine2 lxc ', result)
  
  machine3 = fabric.Connection('192.168.3.2', user='user3', gateway=machine2, connect_kwargs={&quot;password&quot;: &quot;pass3&quot;})
  machine4 = fabric.Connection('192.168.4.2', user='user4', gateway=machine3, connect_kwargs={&quot;password&quot;: &quot;pass4&quot;})
  
  result = machine3.run('hostname')
  print('machine3 hostname', result)
  </code></pre>
  <h2>Meta:</h2>
  <p>These similar questions about automating password entries:</p>
  <ul>
  <li><a href="https://stackoverflow.com/questions/43526330/shell-script-to-automate-ssh-login-using-password">Shell script to automate SSH login using password</a></li>
  <li><a href="https://stackoverflow.com/questions/1002627/automate-ssh-login-under-windows">Automate SSH login under windows</a></li>
  </ul>
  <p>Are answered with &quot;dont&quot;.</p>
  <p>Martin pointed me toward jump hosting, but I haven't gotten it to work. For this topic, most questions seem to point back to this one:</p>
  <ul>
  <li><a href="https://stackoverflow.com/questions/35304525/nested-ssh-using-python-paramiko">Nested SSH using Python Paramiko</a></li>
  </ul>
  <p>I believe that my problem is trying to jump host through the container. It seems the lxc-attach command opens a raw shell that doesn't get passed back.</p>
  <p><strong>Is there a way to spawn a shell that acts more like it's interactive counterparts?</strong> I tried <code>pexpect</code>, but Machine 1 is Windows, so it doesn't work. The commands on the <a href="https://pexpect.readthedocs.io/en/stable/overview.html#windows" rel="nofollow noreferrer">Pexpect Windows Documentation</a> exit with <code>module 'pexpect' has no attribute 'popen_spawn'</code>.</p>