Ansible для Cisco - изменение конфигурации на свичах
Первое, если вы услышали про Ansible в первые и не знаете как его готовить, есть потрясающий ресурс с книгой (спасибо человеку который ее написал) https://www.gitbook.com/book/natenka/ansible-dlya-setevih-inzhenerov. После прочтения данного материала жизнь налаживается и хочется испробовать инструмент на полную.
У нас стояла задача изменить настройки сервера времени на паре десятков свичей. Печаль пришло тогда, когда выяснилось что часть свичей древняя и с еще более старой прошивкой, в которой есть только Telnet. И если при работе с SSH и Ansible все легко и просто, то с telnet есть некоторые трудности. Google выдает разные варианты решения вопроса от https://github.com/ansible/ansible/pull/4230 и https://habrahabr.ru/post/328486/ до https://www.packetgeek.net/2015/08/using-ansible-to-push-cisco-ios-configurations/. В любом случае придется немного по извращатся.
И так, начнем с легкого.
SSH
Нам понадобится:
- Создать invetory с перечнем хостов
- Создать файл с переменными
- И playbook с задачами
Расписывать назначение каждого из файлов я не буду. Если вы прочли книжку из первого абзаца, то вам это уже не понадобится.
Inventory назовем cisco-switch и внесем следующие данные
[cisco-shh]
192.82.130.7
192.82.130.12
[cisco-telnet]
192.82.132.1
192.82.132.11
192.82.132.12
192.82.132.14
192.82.132.15
Мы сразу разделили две группы для удобства использования.
Файлы с переменным распологаем в каталоге /group-vars. У нас будет три файла.
1. Определяет общие переменные для всех устройств all.yml
cli:
host: "{{ inventory_hostname }}"
username: "admin"
password: "SomePassword"
transport: cli
authorize: yes
auth_pass: "SomePassword"
В файле мы указываем:
inventory_hostname - это специальная переменная, которая указывает на тот хост, для которого Ansible выполняет действия.
host - имя или IP-адрес удаленного устройства
port - к какому порту подключаться
username - имя пользователя
password - пароль
transport - тип подключения: CLI или API. По умолчанию - cli
authorize - нужно ли переходить в привилегированный режим (enable, для Cisco)
auth_pass - пароль для привилегированного режима
2. Создаем файлы переменных для каждой группы хостов.
Для групп устройств, переменные должны находится в каталоге group_vars, в файлах, которые называются, как имя группы.
В наших файлах мы задаем две переменные NTP Server и временную зону.
cisco-shh.yml
ntp_server: 192.82.130.50
time_zone: EET 2
cisco-telnet.yml
ntp_server: 192.82.132.50
time_zone: EET 2
3. И самое вкусное - создаем Playbook. Кроме основных функций по передаче команд оборудования, мы решили еще немного поиграться и проверить а применились ли наши команды переданные через Ansible. Обращайте внимание на то что в файле не должно быть табуляций а иерархическая структура формируется пробелами.
ntp-config.yml
- name: Configure NTP
hosts: cisco-kiev
gather_facts: false
connection: local
tasks:
- name: Send config commands
ios_config:
lines:
- ntp server {{ntp_server}}
- clock timezone {{time_zone}}
- clock summer-time EET recurring last Sun Mar 2:00 last Sun Oct 2:00
replace: block
backup: yes
provider: "{{ cli }}"
register: cfg
- name: Show config updates
debug: var=cfg.updates
when: cfg.changed
- name: Save config
ios_command:
commands:
- write
provider: "{{ cli }}"
when: cfg.changed
- name: show ntp
ios_command:
commands:
- show clock
- show ntp status
- show ntp associations
- show version
provider: "{{ cli }}"
register: out
- name: Debug registered var
debug: var=out.stdout_lines
Для Cisco используются следующие модули
ios_command - выполняет команды show
ios_config - выполняет команды конфигурации
ios_facts - собирает факты об устройствах
Ранее мы создали файл с переменными All.yml и теперь при помощи аргумента provider можем передать все переменные в playbook без явного указания каждый раз данных для авторизации.
И еще немножко о Debug. На каждую задачу, можно задать переменную в атребуте register в которую будет записан результат работы задачи. А потом через debug можно преобразовать все в красивый и читабельный вид или отобразить изменения внесенные в конфигурацию.
Так же стоит обратить внимание на replace: block. Параметр replace указывает как именно нужно заменять конфигурацию. В нашем случае мы указали - отправлять все команды вне зависимости есть ли они в конфигурации.
И что же у нас получилось.
Запускаем ansible-playbook -i cisco-switch ntp_config.yml. Через параметр -i указываем файл inventory.
Полотнище вывода я показывать не буду. Но можем убедится что все отработало.
PLAY RECAP *********************************************************************************************************
192.82.130.12 : ok=5 changed=1 unreachable=0 failed=0
192.82.130.7 : ok=5 changed=1 unreachable=0 failed=0
Telnet
И так. Для передачи команд оборудованию принимающему подключения только по telnet нам понадобится ранее созданные файл inventory cisco-switch.yml, файлы с переменными, playbook, файл с шаблонами команд и скрипт который будет передавать эти команды.
Файл с командами будет выглядеть вот так:
ntp-telnet.j2
config t
ntp server {{ntp_server}}
clock timezone {{time_zone}}
clock summer-time EET recurring last Sun Mar 2:00 last Sun Oct 2:00
end
write
Теперь перейдем к скрипту. Скрипт мы решили задействовать из статьи https://www.packetgeek.net/2015/08/using-ansible-to-push-cisco-ios-configurations/. Но как всегда полезли проблемы со всех сторон. Во-первых библиотека netlib уже не та что раньше и часть функций из нее просто убрали. Во-вторых если вы ее еще не ставили, может быть придется повозится.
Для установки:
1. Скачаем и распакуем git clone https://github.com/jtdub/netlib.git
2. Убедитесь что у вас стоит pip и установите pexpect (pip install pexpect)
3. Выполните все рекомендации из файла READ.MD
4. Если во время установки netlib (python setup.py install) вы столкнулись с ошибкой
Running setuptools-35.0.2/setup.py -q bdist_egg --dist-dir /tmp/easy_install-JhhA2M/setuptools-35.0.2/egg-dist-tmp-cJZPpx
Segmentation fault
Выполните pip install --update setuptools
5. Повторите установку netlib.
О скрипте:
Изначально скрипт от автора выглядил вот так
#!/usr/bin/env python
from netlib.netlib.user_creds import simple_yaml
from netlib.netlib.conn_type import SSH
from os.path import expanduser
import sys
creds = simple_yaml()
base_dir = expanduser("~/net-ansible")
hostname = sys.argv[1]
command_file = sys.argv[2]
ssh = SSH(hostname, creds['username'], creds['password'])
ssh.connect()
ssh.set_enable(creds['enable'])
with open(base_dir + "/" + command_file) as f:
for line in f.readlines():
line = line.strip()
ssh.command(line)
f.close()
ssh.close()
НО!!! во время выполнения скрипта (как это делается напишу дальше) мы сталкиваемся с ошибкой No module named netlib.netlib.user_creds, stderr_lines. И все почему - The past methods of password storage, simple_creds and simple_yaml have been depricated and removed from netlib. KeyRing оставшейся в последний версии библиотеки нам не подошел. И в итоге скрипт мутировал и стал выглядит так.
#!/usr/bin/env python
from netlib.conn_type import SSH
from netlib.conn_type import Telnet
from os.path import expanduser
import sys
base_dir = "/etc/ansible/"
hostname = sys.argv[1]
command_file = sys.argv[4]
username = sys.argv[2]
password = sys.argv[3]
ssh = Telnet(hostname, username, password)
ssh.connect()
#We do not use enable in telnet session with user level 15
#ssh.set_enable(creds['enable'])
with open(base_dir + "/" + command_file) as f:
for line in f.readlines():
line = line.strip()
ssh.command(line)
f.close()
ssh.close()
И нам осталось разобраться с playbook. Создаем ntp_telnet.yml.
- name: Configure NTP telnet
hosts: cisco-telnet
connection: local
gather_facts: false
tasks:
- name: IOS | Create NTP Configuration
template:
src=input/ntp-telnet.j2
dest=input/{{ inventory_hostname }}.conf
delegate_to: 127.0.0.1
- name: NTP Configuration
command: script/to_telnet.py {{ inventory_hostname }} {{ cli.username }} {{cli.password}} input/{{ inventory_hostname }}.conf
delegate_to: 127.0.0.1
В данном в rolebook в первой задаче на базе созданного шаблона конфигурации мы создаем файлы с набором команд для каждого хоста. Во второй задаче передаем эти файлы скрипту для выполнения.
Вот и все. Отмучались.
PLAY RECAP *********************************************************************************************************
10.82.132.1 : ok=3 changed=1 unreachable=0 failed=0
10.82.132.11 : ok=3 changed=1 unreachable=0 failed=0
10.82.132.12 : ok=3 changed=1 unreachable=0 failed=0
10.82.132.14 : ok=3 changed=1 unreachable=0 failed=0
10.82.132.15 : ok=3 changed=1 unreachable=0 failed=0
У нас стояла задача изменить настройки сервера времени на паре десятков свичей. Печаль пришло тогда, когда выяснилось что часть свичей древняя и с еще более старой прошивкой, в которой есть только Telnet. И если при работе с SSH и Ansible все легко и просто, то с telnet есть некоторые трудности. Google выдает разные варианты решения вопроса от https://github.com/ansible/ansible/pull/4230 и https://habrahabr.ru/post/328486/ до https://www.packetgeek.net/2015/08/using-ansible-to-push-cisco-ios-configurations/. В любом случае придется немного по извращатся.
И так, начнем с легкого.
SSH
Нам понадобится:
- Создать invetory с перечнем хостов
- Создать файл с переменными
- И playbook с задачами
Расписывать назначение каждого из файлов я не буду. Если вы прочли книжку из первого абзаца, то вам это уже не понадобится.
Inventory назовем cisco-switch и внесем следующие данные
[cisco-shh]
192.82.130.7
192.82.130.12
[cisco-telnet]
192.82.132.1
192.82.132.11
192.82.132.12
192.82.132.14
192.82.132.15
Мы сразу разделили две группы для удобства использования.
Файлы с переменным распологаем в каталоге /group-vars. У нас будет три файла.
1. Определяет общие переменные для всех устройств all.yml
cli:
host: "{{ inventory_hostname }}"
username: "admin"
password: "SomePassword"
transport: cli
authorize: yes
auth_pass: "SomePassword"
В файле мы указываем:
inventory_hostname - это специальная переменная, которая указывает на тот хост, для которого Ansible выполняет действия.
host - имя или IP-адрес удаленного устройства
port - к какому порту подключаться
username - имя пользователя
password - пароль
transport - тип подключения: CLI или API. По умолчанию - cli
authorize - нужно ли переходить в привилегированный режим (enable, для Cisco)
auth_pass - пароль для привилегированного режима
2. Создаем файлы переменных для каждой группы хостов.
Для групп устройств, переменные должны находится в каталоге group_vars, в файлах, которые называются, как имя группы.
В наших файлах мы задаем две переменные NTP Server и временную зону.
cisco-shh.yml
ntp_server: 192.82.130.50
time_zone: EET 2
cisco-telnet.yml
ntp_server: 192.82.132.50
time_zone: EET 2
3. И самое вкусное - создаем Playbook. Кроме основных функций по передаче команд оборудования, мы решили еще немного поиграться и проверить а применились ли наши команды переданные через Ansible. Обращайте внимание на то что в файле не должно быть табуляций а иерархическая структура формируется пробелами.
ntp-config.yml
- name: Configure NTP
hosts: cisco-kiev
gather_facts: false
connection: local
tasks:
- name: Send config commands
ios_config:
lines:
- ntp server {{ntp_server}}
- clock timezone {{time_zone}}
- clock summer-time EET recurring last Sun Mar 2:00 last Sun Oct 2:00
replace: block
backup: yes
provider: "{{ cli }}"
register: cfg
- name: Show config updates
debug: var=cfg.updates
when: cfg.changed
- name: Save config
ios_command:
commands:
- write
provider: "{{ cli }}"
when: cfg.changed
- name: show ntp
ios_command:
commands:
- show clock
- show ntp status
- show ntp associations
- show version
provider: "{{ cli }}"
register: out
- name: Debug registered var
debug: var=out.stdout_lines
Для Cisco используются следующие модули
ios_command - выполняет команды show
ios_config - выполняет команды конфигурации
ios_facts - собирает факты об устройствах
Ранее мы создали файл с переменными All.yml и теперь при помощи аргумента provider можем передать все переменные в playbook без явного указания каждый раз данных для авторизации.
И еще немножко о Debug. На каждую задачу, можно задать переменную в атребуте register в которую будет записан результат работы задачи. А потом через debug можно преобразовать все в красивый и читабельный вид или отобразить изменения внесенные в конфигурацию.
Так же стоит обратить внимание на replace: block. Параметр replace указывает как именно нужно заменять конфигурацию. В нашем случае мы указали - отправлять все команды вне зависимости есть ли они в конфигурации.
И что же у нас получилось.
Запускаем ansible-playbook -i cisco-switch ntp_config.yml. Через параметр -i указываем файл inventory.
Полотнище вывода я показывать не буду. Но можем убедится что все отработало.
PLAY RECAP *********************************************************************************************************
192.82.130.12 : ok=5 changed=1 unreachable=0 failed=0
192.82.130.7 : ok=5 changed=1 unreachable=0 failed=0
Telnet
И так. Для передачи команд оборудованию принимающему подключения только по telnet нам понадобится ранее созданные файл inventory cisco-switch.yml, файлы с переменными, playbook, файл с шаблонами команд и скрипт который будет передавать эти команды.
Файл с командами будет выглядеть вот так:
ntp-telnet.j2
config t
ntp server {{ntp_server}}
clock timezone {{time_zone}}
clock summer-time EET recurring last Sun Mar 2:00 last Sun Oct 2:00
end
write
Теперь перейдем к скрипту. Скрипт мы решили задействовать из статьи https://www.packetgeek.net/2015/08/using-ansible-to-push-cisco-ios-configurations/. Но как всегда полезли проблемы со всех сторон. Во-первых библиотека netlib уже не та что раньше и часть функций из нее просто убрали. Во-вторых если вы ее еще не ставили, может быть придется повозится.
Для установки:
1. Скачаем и распакуем git clone https://github.com/jtdub/netlib.git
2. Убедитесь что у вас стоит pip и установите pexpect (pip install pexpect)
3. Выполните все рекомендации из файла READ.MD
4. Если во время установки netlib (python setup.py install) вы столкнулись с ошибкой
Running setuptools-35.0.2/setup.py -q bdist_egg --dist-dir /tmp/easy_install-JhhA2M/setuptools-35.0.2/egg-dist-tmp-cJZPpx
Segmentation fault
Выполните pip install --update setuptools
5. Повторите установку netlib.
О скрипте:
Изначально скрипт от автора выглядил вот так
#!/usr/bin/env python
from netlib.netlib.user_creds import simple_yaml
from netlib.netlib.conn_type import SSH
from os.path import expanduser
import sys
creds = simple_yaml()
base_dir = expanduser("~/net-ansible")
hostname = sys.argv[1]
command_file = sys.argv[2]
ssh = SSH(hostname, creds['username'], creds['password'])
ssh.connect()
ssh.set_enable(creds['enable'])
with open(base_dir + "/" + command_file) as f:
for line in f.readlines():
line = line.strip()
ssh.command(line)
f.close()
ssh.close()
НО!!! во время выполнения скрипта (как это делается напишу дальше) мы сталкиваемся с ошибкой No module named netlib.netlib.user_creds, stderr_lines. И все почему - The past methods of password storage, simple_creds and simple_yaml have been depricated and removed from netlib. KeyRing оставшейся в последний версии библиотеки нам не подошел. И в итоге скрипт мутировал и стал выглядит так.
#!/usr/bin/env python
from netlib.conn_type import SSH
from netlib.conn_type import Telnet
from os.path import expanduser
import sys
base_dir = "/etc/ansible/"
hostname = sys.argv[1]
command_file = sys.argv[4]
username = sys.argv[2]
password = sys.argv[3]
ssh = Telnet(hostname, username, password)
ssh.connect()
#We do not use enable in telnet session with user level 15
#ssh.set_enable(creds['enable'])
with open(base_dir + "/" + command_file) as f:
for line in f.readlines():
line = line.strip()
ssh.command(line)
f.close()
ssh.close()
И нам осталось разобраться с playbook. Создаем ntp_telnet.yml.
- name: Configure NTP telnet
hosts: cisco-telnet
connection: local
gather_facts: false
tasks:
- name: IOS | Create NTP Configuration
template:
src=input/ntp-telnet.j2
dest=input/{{ inventory_hostname }}.conf
delegate_to: 127.0.0.1
- name: NTP Configuration
command: script/to_telnet.py {{ inventory_hostname }} {{ cli.username }} {{cli.password}} input/{{ inventory_hostname }}.conf
delegate_to: 127.0.0.1
В данном в rolebook в первой задаче на базе созданного шаблона конфигурации мы создаем файлы с набором команд для каждого хоста. Во второй задаче передаем эти файлы скрипту для выполнения.
Вот и все. Отмучались.
PLAY RECAP *********************************************************************************************************
10.82.132.1 : ok=3 changed=1 unreachable=0 failed=0
10.82.132.11 : ok=3 changed=1 unreachable=0 failed=0
10.82.132.12 : ok=3 changed=1 unreachable=0 failed=0
10.82.132.14 : ok=3 changed=1 unreachable=0 failed=0
10.82.132.15 : ok=3 changed=1 unreachable=0 failed=0
Комментарии