Форум сайта python.su
Задача: требуется обойти несколько хостов с запуском определенной команды. В двух словах: собрать статистику типа df -h.
Решил использовать paramiko с авторизацией по ключу, а хосты обходить в отдельных потоках.
Вкратце: здесь config содержит настройки хостов и список интересующих точек монтирования. Результат пиклится в дамп.
Проблема в том, что если monitored_hosts содержит больше одного хоста, то скрипт создает только один дамп, для второго выдается ошибка SSHException: Negotiation failed. При чем если поставить паузу в 1 сек в цикле после dg.start(), то все проходит нормально. А если паузу поставить в 0.5, то второй файл получается обрезанным.
Есть у кого-либо подобный опыт, поделитесь решением?
python 2.4.3, paramiko 1.7
import paramiko, os, re, pickle from ConfigParser import ConfigParser import threading import sys sys.path.append('.') import config monitored_hosts = [config.host1, config.host2, ] class DataGatherer(threading.Thread): def __init__(self, entry): super(DataGatherer, self).__init__() self.cli = paramiko.SSHClient() self.cli.load_host_keys(os.path.expanduser('~/.ssh/known_hosts')) self.e = entry def run(self): try: try: self.cli.connect(self.e.host, self.e.port, self.e.username) stdin, stdout, stderr = self.cli.exec_command( self.e.df_command ) lst = list() for line in stdout: items = re.match(df_format, line.strip()) data = dict() for col in cols: data[col] = items.group(col) if data['mountpoint'] in self.e.mountpoints: lst.append(data) f = open('tmp/%s.df.dmp' % self.e.host, 'wb') pickle.dump( lst , f) f.close() except Exception, info: print "Exception occured: %s" % info finally: if self.cli: self.cli.close() cols = ['filesystem', 'size', 'used', 'avail', 'capacity', 'mountpoint'] df_format = r'(?P<filesystem>\S+)\s+(?P<size>\S+)\s+(?P<used>\S+)\s+(?P<avail>\S+)\s+(?P<capacity>\S+)\s+(?P<mountpoint>\S+)' if __name__ == '__main__': import time for entry in monitored_hosts: dg = DataGatherer(entry) dg.start()
Офлайн
Ошибка не особо информативная :) Что он пишет в логах?
Офлайн
Сейчас 5 хостов в цикле, потоки именую названиями хостов. C паузой 1.5 сек. все работает. Без паузы вот такой букет:
Exception in thread host2:
Traceback (most recent call last):
File "/usr/lib64/python2.4/threading.py", line 442, in __bootstrap
self.run()
File "bin/gatherer.py", line 22, in run
self.cli.connect(self.e.host, self.e.port, self.e.username)
File "build/bdist.linux-x86_64/egg/paramiko/client.py", line 259, in connect
File "build/bdist.linux-x86_64/egg/paramiko/transport.py", line 399, in start_client
SSHException: Negotiation failed.
Exception in thread host1:
Traceback (most recent call last):
File "/usr/lib64/python2.4/threading.py", line 442, in __bootstrap
self.run()
File "bin/gatherer.py", line 22, in run
self.cli.connect(self.e.host, self.e.port, self.e.username)
File "build/bdist.linux-x86_64/egg/paramiko/client.py", line 279, in connect
File "build/bdist.linux-x86_64/egg/paramiko/client.py", line 390, in _auth
File "build/bdist.linux-x86_64/egg/paramiko/transport.py", line 1176, in auth_publickey
File "build/bdist.linux-x86_64/egg/paramiko/auth_handler.py", line 163, in wait_for_response
EOFError
Exception in thread host3:
Traceback (most recent call last):
File "/usr/lib64/python2.4/threading.py", line 442, in __bootstrap
self.run()
File "bin/gatherer.py", line 22, in run
self.cli.connect(self.e.host, self.e.port, self.e.username)
File "build/bdist.linux-x86_64/egg/paramiko/client.py", line 259, in connect
File "build/bdist.linux-x86_64/egg/paramiko/transport.py", line 399, in start_client
SSHException: Negotiation failed.
Exception in thread host4:
Traceback (most recent call last):
File "/usr/lib64/python2.4/threading.py", line 442, in __bootstrap
self.run()
File "bin/gatherer.py", line 22, in run
self.cli.connect(self.e.host, self.e.port, self.e.username)
File "build/bdist.linux-x86_64/egg/paramiko/client.py", line 259, in connect
File "build/bdist.linux-x86_64/egg/paramiko/transport.py", line 398, in start_client
EOFError
Exception in thread host5:
Traceback (most recent call last):
File "/usr/lib64/python2.4/threading.py", line 442, in __bootstrap
self.run()
File "bin/gatherer.py", line 22, in run
self.cli.connect(self.e.host, self.e.port, self.e.username)
File "build/bdist.linux-x86_64/egg/paramiko/client.py", line 259, in connect
File "build/bdist.linux-x86_64/egg/paramiko/transport.py", line 398, in start_client
EOFError
Traceback (most recent call last):
File "/usr/lib64/python2.4/threading.py", line 442, in __bootstrap
self.run()
File "bin/gatherer.py", line 22, in run
self.cli.connect(self.e.host, self.e.port, self.e.username)
File "build/bdist.linux-x86_64/egg/paramiko/client.py", line 259, in connect
File "build/bdist.linux-x86_64/egg/paramiko/transport.py", line 398, in start_client
SSHException: Error reading SSH protocol banner(9, 'Bad file descriptor')
Traceback (most recent call last):
File "/usr/lib64/python2.4/threading.py", line 442, in __bootstrap
self.run()
File "bin/gatherer.py", line 22, in run
self.cli.connect(self.e.host, self.e.port, self.e.username)
File "build/bdist.linux-x86_64/egg/paramiko/client.py", line 279, in connect
File "build/bdist.linux-x86_64/egg/paramiko/client.py", line 406, in _auth
AuthenticationException: Authentication failed.
Отредактировано (Май 10, 2007 17:39:01)
Офлайн
Не этот лог(хотя это тоже полезно посмотреть). Включи перенаправление логов в файл. Может это что-то прояснит.
paramiko.util.log_to_file('/tmp/param.log')
Офлайн
Есть подозрение, что paramiko (или модуль, который он использует для SSH) далеко не thread-safe. Обычно такие вещи указываются в документации. Пока что все ошибки возникают в методе connect - можно попробовать устанавливать lock перед его вызовом и снимать после. Есть небольшой шанс, что поможет. Но это неправильный подход: часто бывает, что код вполне работает, пока не наскочишь на неприятное место, и будешь потом долго биться с отладкой. Правиль перед использованием любого модуля в многопоточном приложении узнать точно (прочитав документацию), какие части его бузопасны для использования в разных потоках.
Ну и, конечно же, fork спасёт в любом случае :)
Офлайн
Напишите в официальный список рассылки paramiko.
Если проблемы с английским – я помогу.
http://mail.lag.net/mailman/listinfo/paramiko
Офлайн
odsПо умолчанию на Линуксе paramiko использует стандартный ssh-client. Однако может использовать и внутреннюю питон-имплементацию протокола при наличи модуля pycrypto. Попробуйте попереключать клиентскую часть.
Есть подозрение, что paramiko (или модуль, который он использует для SSH) далеко не thread-safe. Обычно такие вещи указываются в документации. Пока что все ошибки возникают в методе connect - можно попробовать устанавливать lock перед его вызовом и снимать после. Есть небольшой шанс, что поможет. Но это неправильный подход: часто бывает, что код вполне работает, пока не наскочишь на неприятное место, и будешь потом долго биться с отладкой. Правиль перед использованием любого модуля в многопоточном приложении узнать точно (прочитав документацию), какие части его бузопасны для использования в разных потоках.
Ну и, конечно же, fork спасёт в любом случае :)
Отредактировано (Май 10, 2007 19:33:43)
Офлайн
bialixpycrypto имеется, попробую другой вариант.
По умолчанию на Линуксе paramiko использует стандартный ssh-client. Однако может использовать и внутреннюю питон-имплементацию протокола при наличи модуля pycrypto. Попробуйте попереключать клиентскую часть.
Офлайн
В доках ничего не нашел про потокобезопасность.
Зато быстро переделал под форканье, работает на ура. Будет время - поиграюсь еще, пока работает так.
Всем спасибо.
Офлайн