Ниже приведен сценарий, который подключается к удаленному хосту (маршрутизатору 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-сервер ответит выводом команды)?