Вопрос или проблема
Я пытаюсь получить вывод IQN с нескольких серверов. Я получаю следующую ошибку.
Вывод ошибки
spawn /usr/bin/ssh VM2 | /usr/bin/cat /etc/iscsi/initiatorname.iscsi
root@VM2's password:
bash: -c: line 0: syntax error near unexpected token `|'
bash: -c: line 0: `| /usr/bin/cat /etc/iscsi/initiatorname.iscsi'
скрипт
#!/bin/bash
HOSTS="VM2 VM3 VM4 VM1"
read -p "Password: " PASSWORD
for HOST in $HOSTS
do
expect -c "
spawn cat /etc/iscsi/initiatorname.iscsi
expect {
"*password:*" { send $PASSWORD\r;interact }
}
exit
"
done
Я делаю что-то неправильно?
Прежде всего, я не совсем понимаю, что именно вы пытаетесь достичь.
Понял, что вы хотите cat /etc/iscsi/initiatorname.iscsi
на каждом сервере, верно?
Но тогда зачем вам нужно interact
с каждым сервером?
Я выложу решение для первой проблемы, когда вы входите на каждый сервер и используете cat
файл. Я могу отредактировать решение, если вы хотите добавить interact
также.
Поэтому сначала я решил записать большую часть скрипта на expect
и не передавать его через bash
. Думаю, таким образом у вас будет больше ошибок.
Вот bash-скрипт, который используется как запускающий, он запрашивает пароль, чтобы пароль не был напечатан или виден (это сложно сделать непосредственно в expect
)
#!/bin/bash
expect_script="./connect_to_many_hosts_and_cat_files.exp"
[ ! -f $expect_script ] && echo expect_script does not exist && exit 1
echo "Please enter a password:"
read -s password
$expect_script $password
Теперь сам скрипт expect
#!/usr/bin/expect
if {$argc != 1} {
puts "please run expect script using bash script"
puts "that prompts for password"
exit
}
set password [lindex $argv 0]
# замените имена хостов своими !!!
set hosts "host1 host2"
foreach host [ split $hosts " " ] {
set number_of_password_prompts($host) 0
puts "# "
puts "# ssh $host"
puts "# "
spawn ssh $host
expect {
# здесь я добавил проверку пароля, на случай, если пароль неверен
# выглядит немного некрасиво, но лучше, чем ничего
"Password:" {
if { $number_of_password_prompts($host) == 0 } {
incr number_of_password_prompts($host)
puts "# "
puts "# Authenticating try $number_of_password_prompts($host)"
puts "# "
send "$password\r"
exp_continue
} else {
puts "# "
puts "# Password is wrong"
puts "# "
exit
}
}
# на случай, если на хосте /bin/sh или /bin/bash
"$ " {
puts "# "
puts "# cat /etc/iscsi/initiatorname.iscsi"
puts "# "
send "cat /etc/iscsi/initiatorname.iscsi \r"
expect "$ "
}
# на случай, если на хосте /bin/csh
"% " {
puts "# "
puts "# cat /etc/iscsi/initiatorname.iscsi"
puts "# "
send "cat /etc/iscsi/initiatorname.iscsi \r"
expect "% "
}
send "exit\r"
expect eof
close
}
}
И наконец, убедитесь, что вы использовали chmod +x
для обоих файлов. Вы также можете запускать expect и по-другому. И также убедитесь, что у вас правильное местоположение expect. У меня это /usr/bin/expect
. У вас может быть что-то другое.
Также важно. Для expect
важно, какие приглашения на серверах. Если это /bin/bash
, вы видите, что я добавил условие с $
, если это /bin/csh
, тогда нужно добавить условие с %
.
Очень важно проверять, как будет выглядеть ваш $PS1
или другое приглашение после ssh
. Скрипт выше не будет работать, если ваше приглашение
user@host:~>
Чтобы это работало, нужно добавить еще одно условие в expect
"> " {
puts "# "
puts "# cat /etc/iscsi/initiatorname.iscsi"
puts "# "
send "cat /etc/iscsi/initiatorname.iscsi \r"
expect "> "
}
Обратите на это внимание, я потратил много дней, пытаясь понять, почему вещи не работают.
Используйте ansible для этого случая.
yum install ansible -y
vim /etc/ansible/hosts
[servers]
192.168.0.2
192.168.0.3
ssh-keygen
Скопируйте открытый ключ на целевой сервер.
После этого попробуйте подключиться к серверу
ansible servers -m ping
В ansible документации вы можете найти информацию о том, как управлять серверами.
Ответ или решение
Когда вы пытаетесь получить вывод IQN (iSCSI Qualified Name) с нескольких серверов, необходимо использовать подходящий инструмент для удаленного доступа и выполнения команд на серверах. Пользователь, который представил свой скрипт, сталкивается с ошибкой, связанной с использованием команды SSH в некорректном синтаксисе. Давайте разберем сложившуюся ситуацию и предложим решения.
Теория
Для автоматизации задач работы с несколькими серверами, таких как запуск команд и сбор вывода, можно использовать различные инструменты, например, expect
или Ansible
. Каждый инструмент имеет свои преимущества и ограничения:
-
Expect: Скриптовый инструмент, который позволяет управлять взаимодействием с приложениями, запрашивающими ввод пользователя. Особенно эффективен для автоматизации SSH-сессий. Важно правильно задавать и обрабатывать промпты (строки запроса), которые приходят с удаленных серверов.
-
Ansible: Более современное средство, основанное на SSH, но не требующее постоянного взаимодействия посредством терминала. Ansible имеет мощный синтаксис и возможность выполнения задач на большом количестве серверов одновременно, обеспечивая простую и безопасную автоматизацию.
Пример решения
Из описания видно, что вы уже начали работать с expect
, однако вам необходимо более четко настроить взаимодействие и разделить логику работы между различными скриптами (bash и expect), либо рассмотреть возможность перехода на Ansible.
Решение с использованием expect
Приведенные вами скрипты должны быть немного скорректированы для успешного выполнения команд на удаленных серверах.
- Bash-скрипт для запуска expect:
#!/bin/bash
expect_script="./connect_to_many_hosts_and_cat_files.exp"
# Проверяем наличие скрипта
[ ! -f $expect_script ] && echo "Expect script does not exist" && exit 1
# Ввод пароля без его отображения на экране
echo "Please enter a password:"
read -s password
# Запуск expect скрипта
$expect_script $password
- Expect-скрипт для выполнения команд:
#!/usr/bin/expect
# Проверяем количество аргументов
if {$argc != 1} {
puts "Run expect script using bash script"
puts "that prompts for password"
exit
}
# Получаем пароль из аргументов
set password [lindex $argv 0]
# Задаем список хостов
set hosts "host1 host2 host3"
# Перебор каждого хоста
foreach host [split $hosts " "] {
set timeout 10
spawn ssh $host
expect {
# Проверка на запрос пароля
"*password:*" {
send "$password\r"
exp_continue
}
# Определение shell-промпта
"$ " {
send "cat /etc/iscsi/initiatorname.iscsi\r"
expect "$ "
}
}
}
Преимущество использования Ansible
Ansible предоставляет простую и эффективную возможность управления множеством серверов. Преимущественный выбор для таких задач как ваша, поскольку он не требует ручного взаимодействия с каждой SSH-сессией и обеспечивает безопасность через SSH-ключи. Вот пример настройки Ansible для вашей задачи:
- Установка Ansible:
yum install ansible -y
- Описываем хосты в Ansible:
Редактируем файл /etc/ansible/hosts
:
[servers]
192.168.0.2
192.168.0.3
...
- Настройка SSH-ключей для доступа без пароля:
Создаем SSH-ключи:
ssh-keygen
Копируем открытый ключ на удаленные серверы:
ssh-copy-id user@192.168.0.2
ssh-copy-id user@192.168.0.3
- Проверка подключения:
ansible servers -m ping
- Получение IQN с использованием Ansible:
Создайте playbook, например iqn_fetch.yml
:
---
- hosts: servers
tasks:
- name: Fetch IQN from servers
command: cat /etc/iscsi/initiatorname.iscsi
register: iqn_output
- name: Display IQN
debug:
var: iqn_output.stdout
Запустите playbook:
ansible-playbook iqn_fetch.yml
Применение
Используйте expect
, если вы временно автоматизируете задачи в рамках существующего скриптинга путем прямого взаимодействия через SSH. Рассмотрите переход на Ansible для более долговременной и масштабируемой автоматизации. Подход Ansible позволяет расширять вашу инфраструктуру без необходимости изменения скриптов автоматизации, обеспечивая высокую уровень надежности и безопасности благодаря использованию SSH-ключей вместо паролей.
Таким образом, использование современных инструментов автоматизации, таких как Ansible, не только упрощает управление многими серверами, но и повышает общую безопасность системы за счет отказа от паролей и использования проверенных методов взаимодействия.