Представим, что вам нужно развернуть «с нуля» десяток однотипных access-коммутаторов Cisco. Типовая конфигурация включает имя хоста и домена, шлюз по-умолчанию, пароли, список пользователей, IP-адреса для SVI, номера VLAN'ов, настройки транков аплинков и т.д. Вводить это каждый раз руками очень долго и непроизводительно. Разумеется, можно создать типовой конфиг и заливать его по (T)FTP, но, во-первых, это потребует хотя бы минимальной настройки из консоли, во-вторых, изменяемые параметры конфигурации всё равно придётся менять. Для решения подобных (а также многих других) задач Cisco IOS содержит мощное средство автоматизации — встроенный интерпретатор языка Tcl (Cisco IOS scripting w/ Tcl).

Что такое Tcl


Tcl (читается «тикль», иногда «текл») — интерпретируемый язык программирования, разработанный в конце 80-х для встраивания в консольные приложения. Спектр возможностей современного Tcl довольно широк: тут и поддержка ООП, и развитые средства regexp, динамические массивы и т.д.

Поддержка этого языка впервые появилась на платформе Cisco IOS 12.2(3)T (в некоторых источниках указывается на 12.3(2), но я не нашел этому подтверждения) и в данный момент имеет несколько вариантов:

  • Интерпретатор Tcl с интерфейсом командной строки. Встроен в разные версии платформы Cisco IOS, включая IOS XE и XR и доступен для широкой линейки устройств. Позволяет выполнять команды Tcl, запускать готовые скрипты в виде файлов и т.д. Устройства, использующие в качестве операционной системы не IOS а, например, Cat OS или ASA (в одноименном брандмауэре) командной строки интерпретатора не содержат.
  • Т.н. «встроенный менеджер событий» или EEM — система отслеживания событий, позволяющая автоматически на них реагировать в режиме реального времени. Например, осуществлять мониторинг удаленного хоста с уведомлением по e-mail. EEM-сценарии (аплеты) пишутся на Tcl, но сам EEM не предоставляет отдельной командной строки Tcl. Пример использования см. тут. EEM доступен на платформе Nexus (NX OS) и ASA с версии 9.2(1) и выше.
  • Системы голосового меню IVR (Interactive Voice Responce).

Как определить наличие командного интерпретатора зная модель устройства либо версию IOS? Для этого существует Cisco Feature Navigator:

image

Пункт меню Research Features позволяет выбрать конкретную версию IOS для заданного IOS Train Release или конкретной аппаратной платформы. Пункт меню Research Software позволяет найти все версии IOS с поддержкой Tcl для заданного hardware. Кликаем, фильтруем поле Filter by по названию (Feature name) «Cisco IOS scripting w/ Tcl» (или просто «Tcl»), название фичи добавляем в список, выбираем Train release и получаем список всех версий IOS, содержащих данную фичу:



К сожалению, база CFN неполная и иногда не показывает всю информацию. Так, для платформы CAT2960S навигатор показал наличие Tcl в релизе IOS 15.2E1 и не показал в релизе 15.2E9, хотя по факту интерпретатор Tcl есть и там, и там.

Что же можно сделать в Cisco IOS используя Tcl? Довольно много: просматривать и изменять конфигурацию, создавать интерактивные сценарии, оперировать объектами MIB, сокетами TCP и UDP и даже… написать целый веб-шелл!

В общем виде программа на языке Tcl содержит последовательность команд, разделенных переводом строки или точкой с запятой. Пример, не требующий пояснений:

 puts "Hello, world!"; puts "My first Tcl IOS script!"

Некоторые операторы:

# комментарий до конца строки
set a 1 приcвоение a=1
$a получение значения переменной
{ } блочный оператор — определяет тело цикла или условия
[ ] оператор подстановки — при выполнении вместо квадратных скобок будет подставлено вычисленное значение содержащегося в них выражения
== <= <> операторы сравнения
puts "text" вывод строки «text» в stdout (т.е., на консоль)
puts $a аналогично для значения переменной a
gets stdin читает значения из консоли
set a [gets stdin] ввод значения из консоли и присвоение его переменной a
for {set i 1} {$i < 10} {inrc i} {....} цикл for
proc {argument, ....} {body} процедура

Более полный список см. progopedia.ru/language/tcl

Интерпретатор Tcl запускается командой tclsh из priveleged EXEC-режима:

sw#Tclsh
sw(tcl)#

Выполним первый скрипт:

image

Выход из интерпретатора — команда tclquit или просто exit. Tcl чувствителен к регистру, поэтому Puts "Hello, world" вызовет ошибку, а вот регистр команд оболочки IOS неважен. Все вводимые команды сначала обрабатываются интерпретатором Tcl, если введенная команда является исполнимой с т.з. Tcl, она исполняется и результат выводится на устройство TTY. Если команда не может быть выполнена интерпретатором, она передается парсеру команд IOS. Т.о., один скрипт может комбинировать операторы Tcl и команды IOS. Cреда IOS не содержит полноценного текстового редактора, поэтому pre-defined скрипты нужно создавать внешними средствами и уже потом копировать на flash или в память. Так же поддерживается прекомпиляция скрипта в байт-код с последующим запуском. Запуск файла со скриптом на выполнение осуществляется командой

tclsh flash:filename

Допустимы множественные сеансы интерпретатора Tcl из разных сессий TTY.

Внутренние команды интерпретатора Tcl:
exec — выполняет заданную в кавычках команду из набора IOS CLI priveleged EXEC.
sw(tcl)#exec "show int fa0" выдаст:

image

ios-config — выполняет команду из global configuration mode. За ней в отдельных парных кавычках указываются все последующие команды sub-configuration. Например:
sw(tcl)#ios_config "int fa0" "ip address 192.168.0.1 255.255.255.0" "no shut"
эквивалентно серии команд IOS:

sw#conf te
sw(config)#int fa0
sw(conf-int)#ip address 192.168.0.1 255.255.255.0
sw(conf-int)#no shut

Интерпретатор Tcl не дает процессам exec напрямую взаимодействовать с консолью. Поэтому передача данных в процессы exec, запущенные из оболочки Tcl, происходит с помощью команды typeahead:
typeahead "y\ny"
exec "reload"

Сначала в буфере ввода будут сохранены два символа «y», разделенных переводом строки (\n), затем будет запущена exec-команда reload, которая читает из буфера ввода команды отмены или подтверждения перезагрузки и (если нужно) сохранения конфигурации.

Тикль не поддерживает типизацию, это надо помнить, оперируя с переменными:

image

Вложенный оператор [expr {..}] вычислит значение выражения, заданного в фигурных скобках ($a + $b) и выполнит подстановку этого значения вместо квадратных скобок.

Пример процедуры в Tcl:
proc ping_net {x} {
for {set n 1} {$n < $x} {incr n} {
exec "ping 192.168.0.$n"
}
}

при вводе фигурной скобки интерпретатор не закроет командную строку до ввода парной закрывающей скобки. Процедура хранится в памяти интерпретатора до завершения сессии интерпретатора командой tclquit. Это дает возможность запускать процедуры и обращаться к переменным ранее запущенных скриптов. Помните, что ошибка в скрипте может привести к зацикливанию и блокировке вашего сеанса (V)TTY! Средств аварийного завершения (типа Ctrl+Break) консоль не имеет, единственный способ — новая сессия и сброс «зависшей» сессии командой clear line.

Теперь перейдем к решению практической задачи. Перед нами 48-ми портовый Cat2950S «из коробки». Скрипт, приведенный ниже

  • запрашивает с консоли порядковый номер свича sw_num
  • задает для него hostname вида switch_<sw_num>
  • запрашивает и устанавливает пароль на консоль priveleged EXEC
  • настраивает в соответствии с введенным номером свича адрес на его интерфейсе управления Fa0 (192.168.0.х) и интерфейсе Vlan1 (10.0.х.254)
  • создает port-based DHCP reservation и пул из 48 адресов, в котором за каждым клиентом зарезервирован один IP-адрес, младший октет которого равен порядковому номеру порта, через который это клиент подключен.

puts "Enter Switch number:"
set sw_num [gets stdin]
ios_config "hostname switch_$sw_num"

puts "Enter password (secret):"
set pass [gets stdin]
ios_config "enable secret 0 $pass"

ios_config "line 0 16" "password 0 $pass" "login"

ios_config "int fa0" "ip address 192.168.0.$sw_num 255.255.255.0" "no shut"
ios_config "int vlan1" "ip address 10.0.$sw_num.254 255.0.0.0" "no shut"

ios_config "ip dhcp use subscriber-id client-id"
ios_config "ip dhcp subscriber-id interface-name"

#перебираем в цикле все 48 портов и для каждого задаем опцию subscriber-id
for {set i 1} {$i <= 48} {incr i} {ios_config "int Gi1/0/$i" "ip dhcp server use subscriber-id client-id"}

ios_config "ip dhcp pool POOL1" "network 10.0.0.0 255.0.0.0" "reserved-only" "default-router 10.10.0.254"
# записываем в пул 48 зарезервированных IP-адресов, привязанных к соотв. порту
for {set i 1} {$i <= 48} {incr i} {ios_config "ip dhcp pool POOL1" "address 10.0.$sw_num.$i client-id Gi1/0/$i ascii"}
#

Примечание 1. В этом скрипте есть небольшая логическая ошибка. Попробуйте её найти.

Примечание 2. Некоторые текстовые редакторы любят помещать в конце файла непечатаемый символ EoF. Его можно увидеть в консоли IOS, выведя содержимое файла командой cat или more. Наткнувшись на EoF, интерпретатор Tcl выдаст ошибку и проигнорирует всю строку. Поэтому я оставил в конце скрипта экранирующий знак комментария.

Возникает вопрос: а как же записать скрипт в память коммутатора с ненастроенным IP работая только через консольный порт? Не набирать же скрипт руками! Неужели каждый раз вручную настраивать Management Interface и пользоваться FTP? Нет, можно проще. Cisco IOS умеет копировать файлы прямо через последовательный порт консоли по протоколу Xmodem и сохранять их на флеш. Для этого вам понадобится эмулятор терминала с поддержкой Xmodem, например, ZOC или Tera Term (а вот популярный бесплатный Putty, увы, не подойдет!). Копирование выполняется командой IOS copy xmodem: flash:filename , после чего нужно выполнить File Transfer в меню эмулятора терминала:

image

Также это можно сделать находясь в ROMmon (например, если вы «сносите» конфиг коммутатора, не имея пароля на priveleged EXEC). А вот обратное копирование файлов (с флэш-памяти коммутатора на ПК) не поддерживается.

К сожалению, из Tcl нельзя открыть телнет-сессию на удаленный роутер. При попытке
sw(tclsh)#exec "telnet host" сессия просто подвиснет на этапе ввода пароля.

На этом краткий экскурс в возможности языка Tcl на платформе Cisco IOS завершен, изучить вопрос более подробно вы можете в документе «Cisco IOS Scripting with TCL Configuration Guide» доступном на сайте Cisco.

Комментарии (1)


  1. Rulexec
    12.06.2019 19:09

    TCL интересный язык. В лиспе всё это списки, в TCL — всё это строки (даже числа).


    Весь синтаксис — выполнение процедуры (где первой строкой её имя, остальными — аргументы), двойные кавычки, формирующие строку с пробелами и позволяющие делать ${подстановку_переменных} и [подстановку вызова процедуры], фигурные кавычки, которые возвращают строку как есть.


    В итоге, когда мы видим if {$x == 5} { ... } else { ... } это на самом деле вызов встроенной процедуры if, где первый аргумент подаётся в процедуру expr, которая умеет брать строки и проверять её на логические условия. А блоки кода — это строки, одна из которых будет за'eval'ена, если условие выполнится.