Nano Hash - криптовалюты, майнинг, программирование

Paramiko Sessions закрывает транспорт в дочернем процессе

Мы используем paramiko для создания библиотеки соединений, которая активно использует свои функции get_pty или invoke_shell. Наша библиотека использует эти каналы для взаимодействия с целевым устройством.

Но всякий раз, когда мы используем библиотеку multiprocessing, мы не можем использовать дескрипторы соединения paramiko в дочернем процессе. transport закрывается в дочернем процессе.

Is there a way to tell paramiko not to close the connection/channel at fork. 

Это пример программы для воспроизведения проблемы

from paramiko import SSHClient, AutoAddPolicy
from multiprocessing import Process
import logging
log = logging.getLogger("paramiko.transport").setLevel(1)

client = SSHClient()

client.set_missing_host_key_policy(AutoAddPolicy())

client.connect(hostname="localhost")

def simple_work(handle):
    print("==== ENTERED CHILD PROCESS =====")
    stdin, stdout, stderr = handle.exec_command("ifconfig")
    print(stdout.read())
    print("==== EXITING CHILD PROCESS =====")

p = Process(target=simple_work, args=(client,))
p.start()
p.join(2)
print("==== MAIN PROCESS AFTER JOIN =====")
stdin, stdout, stderr = client.exec_command("ls")
print(stdout.read())

и это ошибка

==== ENTERED CHILD PROCESS =====
Success for unrequested channel! [??]
==== MAIN PROCESS AFTER JOIN =====
Traceback (most recent call last):
  File "repro.py", line 22, in <module>
    stdin, stdout, stderr = client.exec_command("ls")
  File "/Users/vivejha/Projects/cisco/lib/python3.4/site-packages/paramiko/client.py", line 401, in exec_command
    chan = self._transport.open_session(timeout=timeout)
  File "/Users/vivejha/Projects/cisco/lib/python3.4/site-packages/paramiko/transport.py", line 702, in open_session
    timeout=timeout)
  File "/Users/vivejha/Projects/cisco/lib/python3.4/site-packages/paramiko/transport.py", line 823, in open_channel
    raise e
paramiko.ssh_exception.SSHException: Unable to open channel.

Несколько важных вещей, на которые стоит обратить внимание

  1. Если я попытаюсь получить доступ к client в дочернем процессе. Во-первых, он вообще не работает.

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

  3. И самая большая проблема в том, что программа зависает в конце, исключение в порядке, но зависания менее всего ожидаемы.

  4. Если я не использую client в дочернем процессе и выполняю какую-то другую работу, то client в родительском процессе не затрагивается и работает как обычно.

ПРИМЕЧАНИЕ. В файле transport.py есть что-то под названием atfork, которое утверждает, что управляет этим поведением. Но, что удивительно, даже комментирование кода в этом методе не имеет никакого значения. Также во всей кодовой базе paramiko нет ссылок на atfork.

PS: я использую последнюю версию paramiko, и эта программа была запущена на Mac.

12.11.2015

  • пожалуйста, добавьте хотя бы минимальный пример, который вызывает эту проблему, включая установку logging.getLogger("paramiko.transport").setLevel(1). 17.11.2015
  • Я сделаю это в ближайшее время... 17.11.2015
  • @tintin Я обновил вопрос с кодом и выводом. 17.11.2015

Ответы:


1

Это просто фундаментальная проблема, когда сокеты связаны с fork. Оба процесса используют один и тот же сокет, но использовать его может только один. Просто представьте, что два разных процесса управляют одним сокетом. Они оба находятся в разных состояниях, например. один может отправлять и получать данные на удаленную сторону, в то время как другой находится в совершенно другом криптосостоянии. Просто подумайте о векторах одноразовых номеров/инициализации, они просто будут недействительными после разветвления, когда оба процесса расходятся.

Решение вашей проблемы, очевидно, заключается в переключении с MultiProcessing на MultiThreading. Таким образом, у вас есть только одно ssh-соединение, которое используется всеми потоками. Если вы действительно хотите использовать форк, вам придется разветвляться, создавая одно новое соединение для каждого форка.

см. transport.py

def atfork(self):
    """
    Terminate this Transport without closing the session.  On posix
    systems, if a Transport is open during process forking, both parent
    and child will share the underlying socket, but only one process can
    use the connection (without corrupting the session).  Use this method
    to clean up a Transport object without disrupting the other process.

В журнале paramiko вы увидите, что ваш родительский процесс получает SSH_DISCONNECT_MSG от удаленной стороны с ошибкой: Packet corrupt. Скорее всего, из-за того, что родитель находится в другом криптосостоянии и отправляет пакет, который сервер не может понять.

DEBUG:lala:==== ENTERED CHILD PROCESS =====
DEBUG:lala:<paramiko.SSHClient object at 0xb74bf1ac>
DEBUG:lala:<paramiko.Transport at 0xb6fed82cL (cipher aes128-ctr, 128 bits) (active; 0 open channel(s))>
DEBUG:paramiko.transport:[chan 1] Max packet in: 34816 bytes
WARNING:paramiko.transport:Success for unrequested channel! [??]


DEBUG:lala:==== MAIN PROCESS AFTER JOIN =====
WARNING:lala:<socket._socketobject object at 0xb706ef7c>
DEBUG:paramiko.transport:[chan 1] Max packet in: 34816 bytes
INFO:paramiko.transport:Disconnect (code 2): Packet corrupt

Вот базовый пример многопоточности с использованием concurrent.futures:

from concurrent.futures import ThreadPoolExecutor

def simple_work(handle):
    print("==== ENTERED CHILD PROCESS =====")
    stdin, stdout, stderr = handle.exec_command("whoami")
    print(stdout.read())
    print("==== EXITING CHILD PROCESS =====")

with ThreadPoolExecutor(max_workers=2) as executor:
    future = executor.submit(simple_work, client)
    print(future.result())

print("==== MAIN PROCESS AFTER JOIN =====")
stdin, stdout, stderr = client.exec_command("echo AFTER && whoami")
print(stdout.read())

Также обратите внимание, что в большинстве случаев вам даже не нужно вводить дополнительные потоки. Paramiko exec_command уже порождает новый поток и не будет блокироваться, пока вы не попытаетесь прочитать из любого псевдофайла stdout,stderr. Это означает, что вы можете просто выполнить несколько команд и позже прочитать из стандартного вывода. Но имейте в виду, что paramiko может зависнуть из-за переполнения буферов.

17.11.2015
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

Как написать эффективное резюме
Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..