Paramiko ssh.close () проблема с несколькими клиентами - PullRequest
0 голосов
/ 30 апреля 2019

У меня есть интересная проблема с Paramiko.Я использую его для SSHClient в своем коде, где я сначала тестирую соединение, затем подключаюсь (всегда к одному хосту) и, наконец, выполняю задачу.Все это работает очень хорошо, и даже при одновременном запуске, за исключением одного конкретного сценария.Допустим, у меня есть 2 задачи, первая занимает 2 минуты, а вторая - 5 минут.Если я запускаю их одновременно, но только в этом порядке - сначала я запускаю 2-минутное задание, а затем, пока оно выполняется, запускаю 5-минутное;По завершении 2-минутного задания обе задачи завершаются, то есть 5-минутное отключается.Я подозреваю, что проблема связана с ssh.close (), который закрывает оба сеанса, а также их основной транспорт.Я использую потоки и вызываю функцию SSH как отдельные потоки, но это, похоже, не имеет большого значения.

Вот упрощенный код:

connect.py:

import paramiko


HOST = "test.example.com"
USER = "test"
KEY = "/root/.ssh/id_rsa"

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

def test_ssh():
  try:
      ssh.connect(HOST, username=USER, key_filename=KEY)
  except Exception as e:
      raise ValueError("Connection Error: {}".format(str(e)))
      ssh.close()

def run_ssh(cmd, logname):
  file = open(logname, 'w')
  stdin, stdout, stderr = ssh.exec_command(cmd, get_pty=True)
  for line in iter(stdout.readline, ''):
    file.write(''.join(line))
  file.close()
  ssh.close()

app.py:

import threading
from flask import jsonify
from connect import test_ssh, run_ssh
import time

min2 ='/home/test/2min.sh'
log2 = '2minuteoutput.log'
min5 ='/home/test/5min.sh'
log5 = '5minuteoutput.log'

def run(cmd, logname):
  try:
    test_ssh()
  except ValueError as e:
    return jsonify({"status": "error", 'message' : str(e)})
  else:
    somethread = threading.Thread(target=run_ssh, args=(cmd, logname))
    somethread.start()

run(min2, log2)
time.sleep(5)
run(min5, log5)

На другом хосте я размещаю2 сценария оболочки, которые я выполняю:

/ home / test / 2min.sh (принадлежит тестовому пользователю и с исполняемым файлом):

#!/bin/bash
echo "Starting 2 minute job at: "`date`
sleep 60
echo "2min job min1" `date`
sleep 60
echo "2min job min2" `date`
echo "Completed 2 minute job at:" `date`

/ home / test / 5min.sh:

#!/bin/bash
echo "Starting 5 minute job at: "`date`
sleep 60
echo " 5 min job: min1" `date`
sleep 60
echo "5min job: min2" `date`
sleep 60
echo "5 min job: min3" `date`
sleep 60
echo "5min job min4" `date`
sleep 60
echo "5min job min5" `date`
echo "Completed 5 minute job at:" `date`

Затем я запускаю его как python app.py и выходные журналы выглядят так:

2minuteoutout.log:

Starting 2 minute job at: Tue Apr 30 20:09:06 UTC 2019
2min job min1 Tue Apr 30 20:10:06 UTC 2019
2min job min2 Tue Apr 30 20:11:06 UTC 2019
Completed 2 minute job at: Tue Apr 30 20:11:06 UTC 2019

5minuteoutput.log:

Starting 5 minute job at: Tue Apr 30 20:09:11 UTC 2019
 5 min job: min1 Tue Apr 30 20:10:11 UTC 2019

Обе работы были завершены в 20:11:06 UTC, когда ssh.close () была вызвана из первой работы.

Также вот вывод журнала отладки paramiko:

DEB [20190430-21:20:26.906] thr=1   paramiko.transport: starting thread (client mode): 0xd6ed09b0
DEB [20190430-21:20:26.907] thr=1   paramiko.transport: Local version/idstring: SSH-2.0-paramiko_2.4.2
DEB [20190430-21:20:26.996] thr=1   paramiko.transport: Remote version/idstring: SSH-2.0-OpenSSH_7.4
INF [20190430-21:20:26.997] thr=1   paramiko.transport: Connected (version 2.0, client OpenSSH_7.4)
DEB [20190430-21:20:27.085] thr=1   paramiko.transport: kex algos:['curve25519-sha256', 'curve25519-sha256@libssh.org', 'ecdh-sha2-nistp256', 'ecdh-sha2-nistp384', 'ecdh-sha2-nistp521', 'diffie-hellman-group-exchange-sha256', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512
DEB [20190430-21:20:27.085] thr=1   paramiko.transport: Kex agreed: ecdh-sha2-nistp256
DEB [20190430-21:20:27.085] thr=1   paramiko.transport: HostKey agreed: ssh-ed25519
DEB [20190430-21:20:27.085] thr=1   paramiko.transport: Cipher agreed: aes128-ctr
DEB [20190430-21:20:27.085] thr=1   paramiko.transport: MAC agreed: hmac-sha1
DEB [20190430-21:20:27.086] thr=1   paramiko.transport: Compression agreed: none
DEB [20190430-21:20:27.215] thr=1   paramiko.transport: kex engine KexNistp256 specified hash_algo <built-in function openssl_sha256>
DEB [20190430-21:20:27.216] thr=1   paramiko.transport: Switch to new keys ...
DEB [20190430-21:20:27.216] thr=2   paramiko.transport: Adding ssh-ed25519 host key for test.example.com: 
DEB [20190430-21:20:27.217] thr=2   paramiko.transport: Trying discovered key in /root/.ssh/id_rsa
DEB [20190430-21:20:27.299] thr=1   paramiko.transport: userauth is OK
INF [20190430-21:20:27.399] thr=1   paramiko.transport: Authentication (publickey) successful!
DEB [20190430-21:20:27.400] thr=3   paramiko.transport: [chan 0] Max packet in: 32768 bytes
DEB [20190430-21:20:27.484] thr=1   paramiko.transport: Received global request "hostkeys-00@openssh.com"
DEB [20190430-21:20:27.484] thr=1   paramiko.transport: Rejecting "hostkeys-00@openssh.com" global request from server.
DEB [20190430-21:20:27.604] thr=1   paramiko.transport: [chan 0] Max packet out: 32768 bytes
DEB [20190430-21:20:27.604] thr=1   paramiko.transport: Secsh channel 0 opened.
DEB [20190430-21:20:27.683] thr=1   paramiko.transport: [chan 0] Sesch channel 0 request ok
DEB [20190430-21:20:27.767] thr=1   paramiko.transport: [chan 0] Sesch channel 0 request ok
DEB [20190430-21:20:32.491] thr=4   paramiko.transport: starting thread (client mode): 0xd14075c0
DEB [20190430-21:20:32.492] thr=4   paramiko.transport: Local version/idstring: SSH-2.0-paramiko_2.4.2
DEB [20190430-21:20:32.579] thr=4   paramiko.transport: Remote version/idstring: SSH-2.0-OpenSSH_7.4
INF [20190430-21:20:32.579] thr=4   paramiko.transport: Connected (version 2.0, client OpenSSH_7.4)
DEB [20190430-21:20:32.667] thr=4   paramiko.transport: kex algos:['curve25519-sha256', 'curve25519-sha256@libssh.org', 'ecdh-sha2-nistp256', 'ecdh-sha2-nistp384', 'ecdh-sha2-nistp521', 'diffie-hellman-group-exchange-sha256', 'diffie-hellman-group16-sha512', 'diffie-hellman-group18-sha512
DEB [20190430-21:20:32.667] thr=4   paramiko.transport: Kex agreed: ecdh-sha2-nistp256
DEB [20190430-21:20:32.667] thr=4   paramiko.transport: HostKey agreed: ssh-ed25519
DEB [20190430-21:20:32.667] thr=4   paramiko.transport: Cipher agreed: aes128-ctr
DEB [20190430-21:20:32.667] thr=4   paramiko.transport: MAC agreed: hmac-sha1
DEB [20190430-21:20:32.667] thr=4   paramiko.transport: Compression agreed: none
DEB [20190430-21:20:32.756] thr=4   paramiko.transport: kex engine KexNistp256 specified hash_algo <built-in function openssl_sha256>
DEB [20190430-21:20:32.757] thr=4   paramiko.transport: Switch to new keys ...
DEB [20190430-21:20:32.758] thr=2   paramiko.transport: Trying discovered key in /root/.ssh/id_rsa
DEB [20190430-21:20:32.841] thr=4   paramiko.transport: userauth is OK
INF [20190430-21:20:32.932] thr=4   paramiko.transport: Authentication (publickey) successful!
DEB [20190430-21:20:32.933] thr=5   paramiko.transport: [chan 0] Max packet in: 32768 bytes
DEB [20190430-21:20:33.016] thr=4   paramiko.transport: Received global request "hostkeys-00@openssh.com"
DEB [20190430-21:20:33.016] thr=4   paramiko.transport: Rejecting "hostkeys-00@openssh.com" global request from server.
DEB [20190430-21:20:33.137] thr=4   paramiko.transport: [chan 0] Max packet out: 32768 bytes
DEB [20190430-21:20:33.138] thr=4   paramiko.transport: Secsh channel 0 opened.
DEB [20190430-21:20:33.224] thr=4   paramiko.transport: [chan 0] Sesch channel 0 request ok
DEB [20190430-21:20:33.310] thr=4   paramiko.transport: [chan 0] Sesch channel 0 request ok
DEB [20190430-21:22:27.780] thr=1   paramiko.transport: [chan 0] EOF received (0)
DEB [20190430-21:22:27.780] thr=1   paramiko.transport: [chan 0] EOF sent (0)

То, что я собрал из журнала - похоже, что paramiko.transport уже использует потоки.Также, если я правильно читаю (?) - открывается 1 канал.Он указан как открытый дважды (для каждого сценария - один раз в 21:20:27 и затем в 21:20:33).Однако оба они называются chan 0, так что может быть один и тот же канал повторно использован?Как только ssh.close () выдается из первого задания, 1 канал закрывается.Этот вид имеет смысл, поскольку он уже имеет активное соединение с тем же сервером, поэтому может использовать его повторно.Я тоже попробовал использовать канал, но получил тот же результат;

Исходя из этого, мне, возможно, потребуется найти способ запуска / принудительного запуска нескольких каналов - это было бы трудно сделать в интерактивном режиме, а с некоторым типом цикла он мог бы открывать неиспользуемые каналы для каждого вызова итребуется логика для отправки следующих вызовов на этот другой канал.

Я хочу убедиться, что приложение, над которым я работаю, полностью способно обрабатывать несколько задач одновременно, поэтому я хотел задать этот вопрос и посмотреть,может ли кто-нибудь, более знакомый с paramiko, дать мне несколько советов - любая помощь очень ценится!

1 Ответ

0 голосов
/ 01 мая 2019

Оказывается, моя проблема была связана с разделением объекта 'ssh' в connect.py между функциями test_ssh и run_ssh.Я учил, что уже проверял это, но, очевидно, нет.

Чтобы не было путаницы, я назвал их по-разному, и поместил их в функции, как в:

def test_ssh():
  sshtest = paramiko.SSHClient()
  sshtest.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  try:
      sshtest.connect(HOST, username=USER, key_filename=KEY)
  except Exception as e:
      raise ValueError("Connection Error: {}".format(str(e)))
      sshtest.close()
  finally:
      sshtest.close()

def run_ssh(cmd, logname):
  ssh = paramiko.SSHClient()
  ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  ssh.connect(HOST, username=USER, key_filename=KEY)
  file = open(logname, 'w')
  stdin, stdout, stderr = ssh.exec_command(cmd, get_pty=True)
  for line in iter(stdout.readline, ''):
    file.write(''.join(line))
  file.close()
  ssh.close()

Св этом коде все работает как положено.

...