Paramiko и exec_command - убивающий удаленный процесс?
Я использую Paramiko для tail -f
файла на удаленном сервере.
Раньше мы запускали это через ssh -t
, но это оказалось flaky, а -t
вызвало проблемы с нашей системой удаленного планирования.
Мой вопрос: как убить хвост, когда script ловит SIGINT?
My script (на основе Долгосрочные команды ssh в модуле python paramiko (и как их закончить))
#!/usr/bin/env python2
import paramiko
import select
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('someserver', username='victorhooi', password='blahblah')
transport = client.get_transport()
channel = transport.open_session()
channel.exec_command("tail -f /home/victorhooi/macbeth.txt")
while True:
try:
rl, wl, xl = select.select([channel],[],[],0.0)
if len(rl) > 0:
# Must be stdout
print channel.recv(1024)
except KeyboardInterrupt:
print("Caught control-C")
client.close()
channel.close()
exit(0)
script успешно завершает мой Ctrl-C и заканчивается. Однако он оставляет процесс tail -f
запущенным в удаленной системе,
Ни client.close(), ни channel.close(), похоже, не завершают его.
Какую команду я могу задать в блоке except, чтобы ее убить?
На удаленном сервере работает Solaris 10.
Ответы
Ответ 1
Хотя это не самый эффективный метод, это должно сработать. После вас CTRL+C; В обработчике KeyboardInterrupt вы можете exec_command("killall -u %s tail" % uname)
сделать так:
#!/usr/bin/env python2
import paramiko
import select
import time
ltime = time.time()
# Or use random:
# import random
# ltime = random.randint(0, 500)
uname = "victorhooi"
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('someserver', username=uname, password='blahblah')
transport = client.get_transport()
channel = transport.open_session()
channel.exec_command("tail -%df /home/victorhooi/macbeth.txt" % ltime)
while True:
try:
rl, wl, xl = select.select([channel],[],[],0.0)
if len(rl) > 0:
# Must be stdout
print channel.recv(1024)
except KeyboardInterrupt:
print("Caught control-C")
channel.close()
try:
# open new socket and kill the proc..
client.get_transport().open_session().exec_command("kill -9 `ps -fu %s | grep 'tail -%df /home/victorhooi/macbeth.txt' | grep -v grep | awk '{print $2}'`" % (uname, ltime))
except:
pass
client.close()
exit(0)
Это приведет к уничтожению любых открытых процессов с именем tail
. Это может вызвать проблемы, но если у вас есть tail
open, что вы не хотите закрывать, если это возможно, grep
a ps
, получите pid и kill -9
его.
Сначала установите хвост для чтения строк n
из конца файла перед следующим. установите n
для уникального nuber, такого как time.time()
, так как хвост не заботится, будет ли это число больше, чем количество строк в файле, большое число из time.time()
не должно вызывать проблем и будет уникальным. Тогда grep для этого уникального числа в ps
:
client.get_transport().open_session().exec_command("kill -9 `ps -fu %s | grep 'tail -%df /home/victorhooi/macbeth.txt' | grep -v grep | awk '{print $2}'`" % (uname, ltime))
Ответ 2
Есть один способ сделать это. Он работает как на оболочке
ssh -t commandname
Опция -t открывает псевдо-pty, чтобы помочь ssh отслеживать, как долго этот процесс должен продолжаться. то же самое можно сделать через pormiko через
channel.get_pty()
до команды execute_command (...). Это не откроет оболочку, как это происходит с channel.invoke_shell(), она просто запрашивает такой псевдо-интерфейс для привязки всех процессов. Эффект можно также увидеть, если ps aux выдается на удаленном компьютере, теперь процесс привязан к sshd с интерфейсом ptxXY.
Ответ 3
Вы должны использовать ssh keepalives... проблема заключается в том, что удаленная оболочка не знает (по умолчанию), что ваш сеанс ssh был убит. Keepalives позволит удаленной оболочке обнаружить, что вы убили сеанс
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('someserver', username='victorhooi', password='blahblah')
transport = client.get_transport()
transport.set_keepalive(1) # <------------------------------
# ... carry on as usual...
Установите значение keepalive как можно меньше (даже 1 секунду)... через несколько секунд удаленная оболочка увидит, что имя пользователя ssh умерло, и оно прекратит любые процессы, которые были созданы им.
Ответ 4
Я просто ударил эту проблему и не смог выпустить pkill, чтобы закрыть процесс в конце.
Лучшее решение - изменить команду, в которой вы работаете:
tail -f /path/to/file & { read ; kill %1; }
Это позволит вам запускать команду хвоста столько, сколько вам нужно. Как только вы отправите новую строку в удаленный процесс, kill% 1 выполнит и остановит команду хвоста, которую вы создали. (для справки:% 1 является спецификацией jobs и используется для описания первого процесса, который был задан в вашей сессии, то есть команды хвоста)
Ответ 5
Здесь можно получить идентификатор удаленного процесса:
def execute(channel, command):
command = 'echo $$; exec ' + command
stdin, stdout, stderr = channel.exec_command(command)
pid = int(stdout.readline())
return pid, stdin, stdout, stderr
А вот как его использовать (замените ...
битами в исходном вопросе):
pid, _, _, _ = execute(channel, "tail -f /home/victorhooi/macbeth.txt")
while True:
try:
# ...
except KeyboardInterrupt:
client.exec_command("kill %d" % pid)
# ...
Ответ 6
В частности, для "хвоста" вы можете использовать аргумент -pid = PID и позволить хвосту позаботиться об этом:
--pid=PID with -f, terminate after process ID, PID dies
Ответ 7
Вы можете использовать get_pty, как описано в fooobar.com/questions/241659/....
например. сценарий - когда вызывать client/channel.close():
Шаг1: Выполните удаленную команду, которая записывает в файл журнала.
Шаг 2: Создайте поток, который выполняет команду хвоста и блокирует цикл цикла чтения
Шаг 3: В основном потоке, когда команда вернется, вы знаете, что больше не будет журналов, убейте хвост.
Ответ 8
Была та же проблема с ssh -t
. Существует библиотека под названием closer - она запускает удаленный процесс через ssh и автоматически закрывается для вас. Проверьте это.