Захваченный вывод команды на удаленном хосте (SSH через Cron) пуст.

Ниже приведен сценарий, который подключается к удаленному хосту (маршрутизатору Cisco IOS-XR) и выполняет одну команду через SSH. Идея состоит в том, чтобы получить результат команды (целое число), чтобы Cacti мог отобразить его в виде графика. Cacti запускает этот скрипт каждые 5 минут, когда он выполняет обычную процедуру опроса:

#!/bin/bash

if [[ -z $1 ]]
then
    exit 1
fi

HOST="$1"
USER="cact-ssh-user"
TIMEOUT=10s
export SSHPASS="aaaaaaaaaaaaa"

CMD="show controllers np struct IPV4-LEAF-FAST-P np0 | in Entries"
RAW_OUTPUT=$(timeout $TIMEOUT sshpass -e ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null $USER@$HOST "$CMD" 2>/dev/null)
GRT_UCASTV4_USED=$(echo -n "$RAW_OUTPUT" | grep "Entries" | awk '{print $6}' | tr -d "," | tr -d " ")

echo -n "ucastv4_used:$GRT_UCASTV4_USED"

Эта команда отлично работает через интерактивную оболочку (когда я запускаю скрипт на сервере Cacti, используя /path/to/script/script.sh 10.0.0.1. Однако, когда Cacti cronjob запускает вывод, просто пустой. Поэтому в моем сеансе SSH на сервере Cacti вывод:

$ ./script 10.0.0.1
ucastv4_used:1234

В журнале Cacti вывод: 05/22/2017 03:35:21 PM - SPINE: Poller[0] Host[69] TH[1] DS[6837] SCRIPT: /opt/scripts/cacti-scripts/asr9001-get-tcam-ucast-usage.sh 10.0.0.1, output: ucastv4_used:

У меня есть su для пользователя Cacti, и скрипт работает просто отлично. Так что это похоже на то, что он работает как cronjob, вывод команды SSH куда-то волшебным образом перенаправляется, и я не знаю, куда и почему.

Чтобы попытаться отладить это, я добавил следующие строки в скрипт (непосредственно под #!/bin/bash) и дождался 5-минутного интервала опроса Cacti (я вижу в журнале Cacti, когда скрипт вызывается каждые 5 минут);

exec >/tmp/stdout.log 2>/tmp/stderr.log
set -x

stdout.log просто содержит ucastv4_used: то же, что и cacti.log, а файл stderr.log содержит баннер входа в систему для удаленного хоста SSH и ничего больше. Куда пропал вывод SSH?

Устал менять строку SSH в скрипте на вывод в файл и потом читать оттуда:

timeout $TIMEOUT sshpass -e ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null $USER@$HOST "$CMD" > /tmp/output 2>/dev/null
GRT_UCASTV4_USED=$(grep "Entries" /tmp/output | awk '{print $6}' | tr -d "," | tr -d " ")

Файл /tmp/output пуст, поэтому переменная GRT_UCASTV4_USED тоже пуста. stdout.log становится таким же, как и раньше: ucastv4_used:

Я также попытался изменить #!/bin/bash на #!/bin/bash -i, чтобы вызвать интерактивный сеанс. Этот вид работает в том, что с -i, если я добавлю echo $PS1 в скрипт, я вижу в файле stdout.log, что установлено $PS1, а без -i он ничего не печатает. Однако вывод команды SSH по-прежнему отсутствует. Куда идет команда вывода SSH?

Я также пытался использовать ssh ..... | tee /tmp/output, чтобы вывод отображался в /tmp/output и /tmp/stdout.log, но оба пустые.

Я вижу на удаленном маршрутизаторе, что сеанс SSH входит и выполняет команду. Это от debug ssh server:

RP/0/RSP0/CPU0:May 22 14:52:57.976 UTC: SSHD_[65909]: (open_master_file) command added show controllers np struct IPV4-LEAF-FAST-P np0 | in Entries

Кроме того, поскольку это работает через мой интерактивный сеанс с сервером Cacti, я предполагаю, что проблема здесь, а не в маршрутизаторе. Я также уверен, что Cacti сам по себе не является проблемой, я могу запустить позвоночник для опроса этого хоста маршрутизатора из моего интерактивного сеанса SSH, и сценарий работает нормально (дальше указывая на проблему, которая каким-то образом в неинтерактивной оболочке вывод SSH испаряется):

$ cd /usr/local/spine/bin
$ ./spine -V 7 69 69
...
05/22/2017 04:06:56 PM - SPINE: Poller[0] Host[69] TH[1] DS[6837] SCRIPT: /opt/scripts/cacti-scripts/asr9001-get-tcam-ucast-usage.sh 10.0.0.1, output: ucastv4_used:658809

Таким образом, кажется, что вывод SSH куда-то перенаправляется, и я не могу его «получить», или маршрутизатор каким-то образом знает, что это неинтерактивный клиент SSH, и ничего не отправляет обратно. Как еще я могу отладить это?

Обновление 1. Используя debug ssh server на маршрутизаторе Cisco, я записал журналы отладки, когда запускаю сценарий через интерактивный сеанс SSH с сервером Cacti и когда он запускается через интервал опроса Cacti/задание cron. Я diff отредактировал вывод, и единственная интересная разница, которую я могу найти (помимо таких вещей, как изменение SSH PID и эфемерного исходного порта сервера Cacti и т. д.), заключается в следующем:

*** 132,145 ****
   (sshd_interactive_shell) *** removing alarm
   sshd_interactive_shell - ptyfd = 46
   event_contex_init done
!  sshd_ptytonet - Channel 1 Received EOT (bytes:1)
!  sshd_ptytonet - Channel 1 exec command executed sending CHANNEL_CLOSE
!  (close_channel), pid:182260085, sig rcvd:1, state:10 chan_id:1
!  addrem_ssh_info_tuple: REMOVE Inside the critical Section %pid:182260085
!  Cleanup sshd process 182260085, session id 1, channel_id 1
!  addrem_ssh_info_tuple: REMOVE exiting the Critical Section %pid:182260085
   close_channel: Accounting stopped: scriptaccount
!  In delete channel code, pid:182260085, sig rcvd:1, state:10 chan_id:1
   Sending Exit Status: 0 sig: 1
   Sending Channel EOF msg
   Sending Channel close msg for remote_chan_id = 0 chan_id = 1
--- 134,147 ----
   (sshd_interactive_shell) *** removing alarm
   sshd_interactive_shell - ptyfd = 46
   event_contex_init done
!  Pad_len = 6, Packlen = 12
!  sshd_nettopty: EOF received. Disconnecting session
!  (close_channel), pid:182329717, sig rcvd:1, state:10 chan_id:1
!  addrem_ssh_info_tuple: REMOVE Inside the critical Section %pid:182329717
!  Cleanup sshd process 182329717, session id 1, channel_id 1
!  addrem_ssh_info_tuple: REMOVE exiting the Critical Section %pid:182329717
   close_channel: Accounting stopped: scriptaccount
!  In delete channel code, pid:182329717, sig rcvd:1, state:10 chan_id:1
   Sending Exit Status: 0 sig: 1
   Sending Channel EOF msg
   Sending Channel close msg for remote_chan_id = 0 chan_id = 1

Верхняя половина — это мой интерактивный сеанс с сервером Cacti. Я отмечаю в верхней половине sshd_ptytonet - Channel 1 Received EOT (bytes:1), тогда как через cronjob отладка показывает sshd_nettopty: EOF received. Disconnecting session. Является ли неинтерактивный сеанс просто передачей моей SSH-команды удаленному хосту и выходом из нее как можно быстрее (поэтому он не ждет, пока SSH-сервер ответит выводом команды)?


person jwbensley    schedule 22.05.2017    source источник
comment
Вау, это качественный вопрос с надлежащим исследованием. Вот, +1 на меня!   -  person Hubert Grzeskowiak    schedule 22.05.2017
comment
Я предполагаю, что удаленная команда знает, что она запущена в тупой оболочке, и поэтому ничего не выводит. Вы могли бы обойти это, явно вызвав удаленный bash. Все, что вы пытались вызвать интерактивную оболочку, вам, вероятно, следует попробовать на удаленном хосте, а не локально. Также может помочь установка SHELL и TERM.   -  person Hubert Grzeskowiak    schedule 22.05.2017
comment
К сожалению, я не могу вызвать интерактивную оболочку bash (или другую) на удаленном хосте, потому что это маршрутизатор Cisco, и при подключении через SSH я попадаю в их собственный интерфейс командной строки.   -  person jwbensley    schedule 22.05.2017


Ответы (1)


  • Во-первых, скажите SSH-клиенту, чтобы он не выделял PTY с опцией -T, потому что, очевидно, cron не имеет ее.
  • Затем дайте ему что-нибудь бесконечное на stdin, чтобы он продолжал работать, пока stdout не будет открыт, у нас есть /dev/zero именно для этой цели.

RAW_OUTPUT=$(timeout $TIMEOUT sshpass -e ssh -T -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null $USER@$HOST "$CMD" </dev/zero 2>/dev/null)

person Dm1    schedule 12.09.2017