Upload
others
View
22
Download
0
Embed Size (px)
Citation preview
1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.7.1
1.7.2
1.7.3
1.7.4
1.7.5
1.8
1.8.1
1.8.2
1.8.2.1
1.8.3
1.9
1.9.1
1.9.2
1.9.2.1
1.9.2.2
1.9.3
1.9.3.1
1.9.3.2
1.9.4
1.9.4.1
1.9.4.2
1.9.4.2.1
1.9.5
СодержаниеВведение
Окурсе
Какучитьсяпоэтомукурсу
Примерпланаобучения
FAQ
Благодарности
1.Подготовкакработе
ОСиредактор
Системауправленияпакетамиpip
virtualenv,virtualenvwrapper
ИнтерпретаторPython(проверка)
Задания
2.НачалоработысPython
СинтаксисPython
ИнтерпретаторPython.iPython
МагияiPython
Переменные
3.ТипыданныхвPython
Числа
Строки(Strings)
Полезныеметодыдляработысостроками
Форматированиестрок
Список(List)
Полезныеметодыдляработысосписками
Вариантысозданиясписка
Словарь(Dictionary)
Полезныеметодыдляработысословарями
Вариантысозданиясловаря
Словарьиздвухсписков(advanced)
Кортеж(Tuple)
2
1.9.6
1.9.6.1
1.9.6.2
1.9.6.3
1.9.7
1.9.8
1.9.9
1.10
1.10.1
1.10.2
1.10.3
1.11
1.11.1
1.11.2
1.11.2.1
1.11.2.2
1.11.2.3
1.11.3
1.11.4
1.11.5
1.11.6
1.11.7
1.12
1.12.1
1.12.2
1.12.3
1.12.4
1.12.5
1.12.6
1.13
1.13.1
1.13.2
1.13.3
1.13.3.1
Множество(Set)
Полезныеметодыдляработысмножествами
Операциисмножествами
Вариантысозданиямножества
Преобразованиетипов
Проверкатипов
Задания
4.Созданиебазовыхскриптов
Передачааргументовскрипту
Вводинформациипользователем
Задания
5.Контрольходапрограммы
if/elif/else
for
Вложенныеfor
Совмещениеforиif
Итераторenumerate
while
break,continue,pass
for/else,while/else
Работасисключениямиtry/except/else/finally
Задания
6.Работасфайлами
Открытиефайлов
Чтениефайлов
Записьфайлов
Закрытиефайлов
Конструкцияwith
Задания
7.Функции
Созданиефункций
Пространстваимен.Областивидимости
Параметрыиаргументыфункций
Типыпараметров
3
1.13.3.2
1.13.3.3
1.13.3.4
1.13.3.5
1.13.4
1.14
1.14.1
1.14.2
1.14.3
1.15
1.15.1
1.15.2
1.15.3
1.15.4
1.15.4.1
1.15.5
1.16
1.16.1
1.16.2
1.16.3
1.16.4
1.17
1.17.1
1.17.2
1.17.3
1.17.3.1
1.17.3.2
1.17.3.3
1.17.3.4
1.17.3.5
1.17.3.6
1.17.3.7
1.17.3.8
Типыаргументов
Аргументыпеременнойдлины
Распаковкааргументов
Примериспользования
Задания
8.Модули
Импортмодуля
Созданиесвоихмодулей
Задания
9.Регулярныевыражения
Модульre
Специальныесимволы
Жадностьрегулярныхвыражений
Группировкавыражений
Разборвыводакомандыshowipdhcpsnoopingспомощьюименованныхгрупп
Задания
10.Сериализацияданных
CSV
JSON
YAML
Задания
11.Работасбазамиданных
SQL
SQLite
ОсновыSQL(вsqlite3CLI)
CREATE
DROP
INSERT
SELECT
WHERE
ALTER
UPDATE
REPLACE
4
1.17.3.9
1.17.3.10
1.17.4
1.17.4.1
1.17.5
1.18
1.18.1
1.18.2
1.18.3
1.18.4
1.18.5
1.18.5.1
1.18.6
1.18.6.1
1.18.6.2
1.18.7
1.19
1.19.1
1.19.2
1.19.3
1.19.3.1
1.19.3.2
1.19.3.3
1.19.3.4
1.19.3.5
1.19.3.6
1.19.3.7
1.19.3.8
1.19.4
1.19.5
1.20
1.20.1
1.20.2
DELETE
ORDERBY
Модульsqlite3
ПримериспользованияSQLite
Задания
12.Подключениекоборудованию
Вводпароля
Pexpect
Telnetlib
Paramiko
Netmiko
Возможностиnetmiko
Одновременноеподключениекнесколькимустройствам
Модульthreading
Модульmultiprocessing
Задания
13.ШаблоныконфигурацийсJinja
ПримериспользованияJinja
ПримериспользованияJinjaскорректнымиспользованиемпрограммногоинтерфейса
СинтаксисшаблоновJinja2
Контрольсимволовwhitespace
Переменные
for
if/elif/else
Фильтры
Тесты
Присваивание(set)
Include
Наследованиешаблонов
Задания
14.TextFSM.Обработкавыводакоманд
СинтаксисшаблоновTextFSM
ПримерыиспользованияTextFSM
5
1.20.3
1.20.4
1.21
1.21.1
1.21.1.1
1.21.1.2
1.21.1.3
1.21.1.4
1.21.2
1.21.2.1
1.21.2.2
1.21.3
1.21.3.1
1.21.3.2
1.21.3.3
1.21.3.3.1
1.21.3.3.2
1.21.3.3.3
1.21.3.3.4
1.21.3.3.5
1.21.3.3.6
1.21.3.3.7
1.21.3.3.8
1.21.3.3.9
1.21.3.3.10
1.21.3.3.11
1.21.3.4
1.21.4
1.21.5
1.22
1.22.1
1.22.1.1
1.22.1.2
CLITable
Задания
15.Ansible
ОсновыAnsible
Инвентарныйфайл
Ad-Hocкоманды
Конфигурационныйфайл
Модули
Основыplaybook
Переменные
Результатвыполнениямодуля
Сетевыемодули
ios_command
ios_facts
ios_config
lines(commands)
parents
Отображениеобновлений
save
backup
defaults
after
before
match
replace
src
ntc_ansible
ПодробнееобAnsible
Задания
Дополнительнаяинформация
Полезныемодули
Модульsubprocess
Модульos
6
1.22.1.3
1.22.1.4
1.22.2
1.22.2.1
1.22.2.2
1.22.2.3
1.22.2.4
1.22.2.5
1.22.2.6
1.22.3
1.22.3.1
1.23
1.23.1
1.23.2
1.24
Модульargparse
Модульipaddress
Полезныефункции
Функцияsorted
Функцияlambda
Функцияzip
Функцияmap
Функцияfilter
Функцииanyиall
Соглашениеобименах
Подчеркиваниевименах
Полезныессылки
Материалыпотемамкурса
Продолжениеобучения
Отзывы
7
Pythonдлясетевыхинженеров
Автор:НаташаСамойленко
ПрисоединяйтеськкомандеPyNEngвslack
Зачемвамучитьсяпрограммировать?Знаниепрограммированиядлясетевогоинженера,сравнимосознаниеманглийского.
Есливызнаетеанглийский,хотябынауровне,которыйпозволяетчитатьтехническуюдокументацию,высразужерасширяетесвоивозможности:
доступновразыбольшелитературы,форумов,блоговпрактическидлялюбоговопроса/проблемыестьрешение,есливыввелизапросвгуглнаанглийском
Знаниепрограммированиявэтомоченьпохоже.Есливызнаете,например,Python,хотябынабазовомуровне,выужеоткрываетемассуновыхвозможностейдлясебя.
Аналогиясанглийскимподходитещёипотому,чтоможноработатьсетевыминженеромибытьхорошимсетевыминженеромибеззнанияанглийского.Английскийпростодаетвозможности,нооннеявляетсяобязательнымтребованием.Аналогичноиспрограммированием.
ОкурсеЕсливдвухсловах,тоэтотакой“CCNA”попитону.Соднойстороны,курсдостаточнобазовый,чтобыегомогодолетьлюбойжелающий,сдругойстороны,вкурсерассматриваютсявсеосновныетемы,которыепозволятдальшерастисамостоятельно.
КурснеставитсвоейцельюглубокоерассмотрениеязыкаPython.Задачакурса,объяснитьпонятнымязыкомосновыPythonидатьнеобходимыеинструментыдляегопрактическогоиспользования.
Введение
8
Все,чторассматриваетсявкурсе,ориентированонасетевоеоборудованиеиработусним.Этодаетвозможностьсразуиспользоватьвработето,чтобылоизученонакурсе.
ВсепримерыпоказываютсянапримереоборудованияCisco.Но,конечноже,аналогичныепринципыприменимыдлялюбогодругогооборудования.
ДлякогоэтоткурсДлясетевыхинженеровсопытомпрограммированияибез.
Всепримерыидомашниезаданиябудутпостроенысуклономнасетевоеоборудование.
Этоткурсбудетполезендлясетевиков,которыехотятавтоматизироватьзадачискоторымисталкиваютсякаждыйденьи/илихотелизанятьсяпрограммированием,нонезналискакойстороныподойти.
Ещёнерешилинуженликурс?Почитайтеотзывы.
ThisworkislicensedunderaCreativeCommonsAttribution-ShareAlike4.0InternationalLicense.
Введение
9
Окурсе
Краткоесодержаниекурса1.Подготовкакработе2.НачалоработысPython3.ТипыданныхвPython4.Созданиебазовыхскриптов5.Контрольходапрограммы6.Работасфайлами7.Функции8.Модули9.Регулярныевыражения10.Сериализацияданных11.Работасбазамиданных12.Подключениекоборудованию13.ШаблоныконфигурацийсJinja14.TextFSM.Обработкавыводакоманд15.AnsibleДополнительнаяинформацияПолезныессылки
ЕсливыскачалиPDFииспугались,чтовкурсепочти600страниц,непереживайте.Половинукурсазанимаютразделы12-15.Внихмноговыводов,которыепоказываютрезультатывыполненияскриптов,поэтомуобъемполучилсябольшой.
Курсвсегдабудетоставатьсябесплатным.Поэтомувамненужнопереживать,чтоонбудетудален.
ОС,Python
Вкурсеиспользуются:
DebianLinuxPython2.7
ПозжекурсбудетпереведеннаPython3.x.
Окурсе
10
Длякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Примеры
Всепримеры,которыеиспользуютсявкурсе,можноскачатьоднимархивомвzipилиtar.gz.
Примеры,которыерассматриваютсявразделахкурса,являютсяобучающими.Этозначит,чтоонинеобязательнопоказываютлучшийвариантрешениязадачи,таккакониоснованытольконатойинформации,котораярассматриваласьвкурсе.
Крометого,довольночасто,примеры,которыедавалисьвразделах,развиваютсявзаданиях.Тоесть,вамнужнобудетсделатьболеехорошую,универсальную,правильнуюверсиюкода.
Еслиестьвозможность,лучшенабиратькод,которыйиспользуетсявкурсе,самостоятельно.Или,какминимум,скачатьпримерыипопробоватьчто-товнихизменить.Такинформациябудетлучшезапоминаться.
Еслитакойвозможностинет,например,потомучтовычитаетекурсвдороге,можнопопробоватьповторитьпримерысамостоятельнопозже.
Но,влюбомслучае,обязательнонужноделатьзадания.
Задания
Всезаданияивспомогательныефайлыможноскачатьоднимархивомвzipилиtar.gz.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Однако,еслиполучается,лучшеделатьзаданияпопорядку.
Вкурсеспециальноневыложеныответыназадания.Ксожалению,когдаестьответы,оченьчасто,вместотого,чтобыпопытатьсярешитьсложноезаданиесамостоятельно,подглядываютвответы.
Конечно,иногдавозникаетситуация,когданикакнеполучаетсярешитьзадание.Тогдапопробуйтеотложитьегоисделатькакое-тодругое.
Окурсе
11
Наstackoverflowестьответыпрактическиналюбыевопросы.Такчто,еслигуглотправилвастуда,значит,сбольшойвероятностью,ответнайден.Запросы,конечноже,лучшеписатьнаанглийском.ПоPythonоченьмногоматериалови,какправило,подсказкунайтилегко.
Ответымоглибыпоказать,какещёможновыполнитьзаданиеиликакболееправильно/корочевыполнитьего.Но,наэтотсчет,тоженепереживайте,таккак,скореевсего,вследующихразделахвстретитсяпример,вкоторомбудетпоказано,какписатьтакойкод.
Презентации
Длячаститемкурсаестьпрезентации.Понимудобнобыстропросматриватьинформациюиповторять.Ониособеннополезныдлябазовыхтем.
Скачатьвсепрезентацииоднимархивом.
Современем,будутдобавленыпрезентациидлявсехразделов.
ОбновлениекурсаКурсбылполностьюзавершен07.02.2017.
Но,вразделдополнительнаяинформацияещёбудутдобавленынесколькоподразделов.Также,скореевсего,будутдобавленыновыезадания.
Поэтому,есливынебудетечитатькурсвближайшеевремя,толучшесохранитессылкунаонлайнверсиюкурса,анеPDF/mobi/epub.А,когдарешитечитать,скачаете,еслинужно,свежуюверсию.
Дополнениябудутделатьсявближайшие2-6месяцевинебудутвлиятьнаизначальнуюструктурукурса.
Gitbookотображает,когдабылисделаныпоследниеизменения,поэтомулегкоможноопределитьбылилиизменениязапоследнеевремя.
Окурсе
12
ФорматыкурсаКурсможночитатьвнесколькихформатах:
онлайнPDF/ePub/Mobi
Ониавтоматическиобновляются,поэтомувсегдасодержатодинаковуюинформацию.
Пожалуйста,невыкладывайтескачанныеверсиикурса.Вместоэтого,простодавайтессылкунакурс.
КомментарииGitBookпозволяетоставлятькомментарииусоответствующейчастикурса.ДляэтогонужнозарегистрироватьсянаGitBookизайтиподсвоимакаунтом.
Вонлайнверсиикурса,еслинавестикурсорнакакой-тоабзацтекста,справапоявитсяплюс:
Еслинажатьнанего,откроетсяокнодлянаборасообщения:
Окурсе
13
Послепубликации,сообщениенаходитсяоколоконкретноготекставкурсеидругиечитателимогутпрочестьего.Еслиэтобылкомментарийобопечатке,яудалюего,послетого,какправкисделаны.
Еслижекомментарийбылонепонятноммоментевкурсе,комментарийостанется,вместесмоимипояснениями.
Комментарии,соответственно,доступнывонлайнверсиикурса,рядомстекстом:
Атакже,наглавнойстраницекурса:
Окурсе
14
Окурсе
15
Какучитьсяпоэтомукурсу
1вариант:
Этотвариантподходитвтакихслучаях:
Есливынеуверены,чтохотитепроходитьвеськурсЕсливамнужнопростоавтоматизироватьподключениекустройствамивыполнениекомандЕсливынеуверены,чтохотитеизучатьPython
Вэтомслучае,начнитесAnsible.
ДляегоустановкиибазовогоиспользованиянетребуетсязнатьPython.ДостаточноустановитьAnsible,взятьпримериспользованияипопробоватьвыполнитькомандыshowнаоборудовании.
Послеэтого,можноиспользоватьAnsibleдляотправкикоманд,которыеневлияютнапередачутрафика:
подписьинтерфейсовнастройкаобщихпараметров,такихкак,например,aliasгенерацияконфигурациипошаблону,дляпервичнойнастройкиоборудования
Есликакие-тозадачинеполучаетсярешитьсAnsible,иливыпростозахотитеизучитьPython,переходитекварианту2.
2вариант:ЕсливыужерешилиизучатьPython
ЕсливыужерешилиизучатьPythonипримернопредставляете,чтоонможетдать,томожнопростовзятьрасписаниекурсаиидтипонему.
Ихотямногиевещи,особенновначалекурса,будутбазовыми,непропускайтеих,есливыихнезнаете.Этибазовыеструктурыданных,управляющиеструктуры,будутосновойвсего,чтомыбудемизучатьдальше.
Обязательнопрактикуйтесь:
пробуйтевоспроизвестивсепримерывглавах(набирайтепримерывручнуюилипробуйтеповторятьпопамяти)пробуйтеменятькакие-топараметрывпримерах,чтобыпосмотреть"аможнолитаксделать"обязательновыполняйтевсезаданияпослеглав
Какучитьсяпоэтомукурсу
16
Планзанятийможноизменитьподсебя,нонестоитделатьбольшиеперерывымеждутемамиинестоитзаниматьсяслишкоммного:
еслиделатьбольшиеперерывы,товсё,чтоизученораньше,начнетзабыватьсяеслиторопиться,тоскореевсего,всёсведетсякчтениютекста,безвыполнениязаданий.Втакомслучае,откурсабудеточеньмалопользы
Еслиследоватьрасписанию,пустьиснебольшимиизмениями,токконцукурсанаберетсядвамесяцапрактики.Заэтовремявсебазовыепонятияхорошоусвоятсяи,главное,послезавершениякурса,небросатьпрактиковатьсяиучиться.
Походукурса,скореевсего,будутвозникатьидеи,чтосделатьпоработе.Этоотлично!Делайтеобязательно.Записывайтеидеи,чтобынезабыть,еслинеможетесразусделатьэто.
Примерыиспользования,которыепридумываетевы,будутособенносильнопомогатьрасти.Таккакониестественнымобразомбудутразвиваться:будетдобавлятьсяфункционал,будутулучшения,которыенужныименновам.
Еслижевычувствуете,чточитаете,нопоканикакихидейнет,ничегострашного.Вкурседостаточноупражнений,скоторымивысможетепотренироватьсяписатькод.Икоторые,весьмавероятно,натолкнутнакакие-тоидеи.
Послекурса
Ксожалению,новыезнанияоченьбыстрозабываютсябезпримененияибезповторения.
Неделайтеслишкомбольшойперерывпослекурса.Еслиоставитьновыезнаниябезпримененияна2-4недели,тобольшаячастьизнихсущественновыветрится.
Есливамудалосьнапридумыватьсебезадач,походукурса-отлично.Реализуйтеих.Напишитесписокиделайтезадачипостепенно.Этоотличныйспособизучатьдальшеиповторятьпройденное.
Идеисамибудутдвигатьвасдальше,выбудетеизучатьновыетемы,новыевозможностиестественно,походуразвитиявашихпрограмм.
Создайтерепозиторийнаgithubивыкладывайтетудасвоискрипты.Дорабатывайтеих,поделитесьсколлегами.
Отличныйспособзапомнитьлучшекакую-тотему-рассказатьдругому.
Есливыхотитеидальшеразвиватьсявэтойтеме,продолжайтеучиться,читать.Вразделересурсыяподготовиларазличныессылкинавидео,книги,курсы,блогиизадачкипоPython.Выберитетотресурс,которыйвамнравитсяиизучайтеPython
Какучитьсяпоэтомукурсу
17
дальше.
Иобязательнопрактикуйтесь.Толькочитатькнигу/статьюилисмотретьвидео,недостаточно.Обязательнопишитечто-то.
Какучитьсяпоэтомукурсу
18
ПримерпланаобученияТутнаходитсяпримерплана,покоторомуможноучитьсяпоэтомукурсу.
Впланевсёразбитопонеделям.Можноидтибыстрееилимедленнее,взависимостиотуровняиналичиявремени.Повозможности,попробуйтепридерживатьсятемпа,возможно,онвамподойдет.
Например,можночитатьтемыввыходные,анарабочейнеделевыполнятьзаданияпоэтойтеме.
Неделя Темы Задания
0 Подготовкакработе Подготовкакработе
1НачалоработысPythonТипыданныхвPythonСозданиебазовыхскриптов
ТипыданныхБазовыескрипты
2 КонтрольходапрограммыРаботасфайлами
КонтрольходапрограммыФайлы
3 Функции Функции
4 МодулиРегулярныевыражения
МодулиРегулярныевыражения
5 Сериализацияданных Сериализацияданных
6 Работасбазамиданных Базыданных
7 Подключениекоборудованию Подключениекоборудованию
8 ШаблоныконфигурацийсJinja Jinja2
9 ОбработканеструктурированноговыводакомандсTextFSM TextFSM
10 Ansible Ansible
Примерпланаобучения
19
Чемэтоотличаетсяотобычноговводногокурсапопитону?
тутосновыдаютсядостаточнокороткоподразумеваетсяопределеннаяпредметнаяобластьзнаний(опытработыссетевымоборудованием)всепримеры,повозможности,ориентированынасетевоеоборудование
Ясетевик.Длячегомненуженэтоткурс?
Впервуюочередь,дляавтоматизициирутинныхзадач.
Автоматизациядаетнесколькопреимуществ:
даетвозможностьмыслитьболеевысокоуровневокогдавысвободныотрутиннойработы,прощеподнятьсянадвсем
уваспоявляетсявремяивозможностьдуматьобулучшенияхдоверие.Вынебоитесьделатькаждоеизменение
каждоеизменениекакправило,сопряженосриском,таккаксетьэтоосноваработывсехприложенийиценаошибки,какправило,высока
выполучаетеконсистентнуюконфигурацию,отнастроенныхпользователейиподписейинтерфейсов,дофункционалабезопасности
выможетенепереживатьотом,незабылиливычто-то
Конечно,небудеттакого,чтопослекурсавы"всёавтоматизируетеинаступитсчастье".Ноэтошагвэтусторону.
Иянивкоемслучаенеагитируюзато,чтобыавтоматизациявыполняласькучейсамописныхскриптов.Еслиестьсофт,которыйрешаетнужныевамзадачи,этоотлично.Используйтеего.
Но,еслиегонет,илиесливыпростоещёотакомнедумали,попробуйтеначатьспростого.Ansible,например,дастсразудовольномного.
Зачемтогдаучитьpython?Проблемавтом,чтототжеAnsibleнерешитвсевопросы.И,возможно,вампонадобитсядобавитькакой-тофункционалсамостоятельно.
И,кроменепосредственнойнастройкиоборудования,естьежедневныерутинныезадачи,которыеможноавтоматизироватьспомощьюPython.
Скажемтак,есливынехотитеразбиратьсясPython,нохотитеавтоматизироватьпроцесснастройки/работысоборудованием,посмотритенаAnsible.Влюбомслучае,дажеизкоробки,онужебудеточеньполезен.
FAQ
20
Еслижевыпотомвойдетевовкусизахотитедобавитьчто-тосвое,чегонетвAnsible,возвращайтесь:)
Иещё,этоткурснетолькоотомкакиспользоватьPythonдляподключениянаоборудованиеинастройкиего.Ониотом,какрешатьзадачи,которыенекасаютсяподключениякоборудованию.Например,изменитьчто-товнесколькихфайлахконфигурации.Илиобработатьlog-файл.
Pythonпоможетвамрешатьиподобныезадачи.
Почему"длясетевыхинженеров"?
Естьнесколькопричин:
сетевыеинженерыужеобладаютопытомработывИТичастьконцепцийимзнакома.
скореевсегокакие-тоосновыпрограммированиябольшинствуужебудутзнакомы.Аэтозначит,чтобудетгораздопрощеразобратьсяисPython.
работавкоманднойстрокеинаписаниескриптов,врядликого-тоизнихиспугаетусетевыхинженеровестьзнакомаяимпредметнаяобласть,накоторуюможноопираться,присоставлениипримеровизаданий.
Еслирассказыватьнапримерахокотикахизайчиках,этоодно.Нокогдавпримерахестьвозможностьиспользоватьидеиизпредметнойобласти,всестановитсяпроще,рождаютсяконкретныеидеикакулучшитькакую-топрограмму,скрипт.Акогдачеловекпытаетсяееулучшить,онначинаетразбиратьсясчем-тоновым.Иэтооченьсильнопомогаетпродвигатьсявперед
ПочемуPython?
Вконтекстеработыссетевымоборудованиемчастоиспользуетсяименноpython.ВнекоторомоборудованииpythonвстроенилиестьAPI,которыеподдерживаютPython
Pythonдостаточнопростдляизучения.Конечно,этовсёотносительноикому-тоболеепростымможетбытьдругойязык,носкорееэтобудетиз-заопытаработысязыком,анепотому,чтоPythonсложный
СPythonвыврядлибыстродойдетедограницвозможностейязыка.Pythonможетиспользоватьсянетолькодляскриптинга,ноидляразработкиприложений.Конечно,дляэтогокурсаэтоневажно,но,покрайнеймере,выпотратитевремянаязык,которыйпозволитвамлегкошагнутьдальше,чемпростыескрипты
FAQ
21
изсетевойсферы,например,наPythonнаписанGNS3
Иещеодинмомент,вконтекстекурса,Pythonнужнорассматриватьнекакединственноправильныйвариант,некак"правильный"язык.Нет,этопростоинструмент.
Инструменткакотвертка,например.Мыучимсяимпользоваться,дляконкретныхзадач.
Тоесть,никакойидеологическойподоплекитутнет.Никакого"толькоPython"тоже.Никакого"поклонения"темболее.
Страннопоклонятьсяотвертке:)
Тутвсёпросто.Естьхорошийудобныйинструмент,которыйподойдеткразнымзадачам.Оннелучшийвовсемидалеконеединственныйязыквпринципе.
Начнитеснего.Потомвысможетесамостоятельновыбратьчто-тодругое,еслизахотите.Этизнаниявсёравнонепропадут.
Почемуpython2,анеpython3.x?
Опятьпроотвертку:)
Естьотверткаобычная(python2)иновая(python3),классная,сулучшеннымдизайном,болеебезопасная,красиваяитакдалее.
Длянашихзадачподойдетиобычная:)
Есливыпишетеприложение,анепростопарускриптов,токонечнолучшеписатьнаpython3.x.
Грубоговоря,намраноотакомдумать.
Аеслисеръезно,то,ксожалению,наданныймоментневсепроектыподдерживаютpython3.Поэтому,покачто,вкурсебудетиспользоватьсяpython2.
Вдальнейшем,скореевсего,курсбудетпеределаннаpython3.x.
Янезнаюнужнолимнеэто.
Яконечножесчитаю,чтонужно:)Иначе,ябынеписалаэтоткурс.
Нода,совсемнефакт,чтовамзахочетсяпогружатьсявэтовсё.Поэтому,дляначала,попробуйтеразобратьсясAnsible.Возможно,вамхватитегодостаточнонадолго.
FAQ
22
Начнитеспростыхкомандshow.Попробуйтеподключитьсясначалактестовомуоборудованию(виртуалкам).
Затемпопробуйтевыполнитькомандуshowнареальнойсети,на2-3устройствах.Потомнабольшемколичестве.
Есливамэтогодостаточно,можноостановитьсятут.
СледующимшагомябыпопробовалаиспользоватьAnsibleдлягенерациишаблоновконфигурации.
Зачемсетевомуинженерупрограммирование?
Намойвзгляд,длясетевогоинженераумениепрограммироватьоченьважно.Инепотому,чтосейчасвсеобэтомговорят,илипугаютSDN,потерейработыиподобным.Апотому,чтосетевойинженерпостоянносталкиваетсяс:
рутиннымизадачамиспроблемамиирешениями,которыенадопротестироватьсбольшимобъемомоднотипныхзадачсбольшимколичествомповторяющихсязадачсбольшимколичествомоборудования
А,наданныймомент,большоеколичествооборудования,по-прежнему,предлагаетнамтолькоинтерфейскоманднойстрокиинеструктурированныйвыводкоманд.Управляющийсофтчастоограниченвендором,дорогостоитиимеетурезанныевозможности.
Ивитогемывручнуюсноваисноваделаемодноитоже.
Дажетакиебанальныевещикак:отправитьоднуитужекомандуshowна20устройств,уженевсегдапростосделать.Да,допустимвашSSHклиентподдерживаетэтувозможность.Аесливамтеперьнадопроанализироватьвывод?
Мыограниченытемисредствами,которыенамдали.Азнаниепрограммирования,дажесамоебазовое,позволяетнамрасширитьнашисредстваидаже,создаватьновые.
Ида,янесчитаю,чтовсемнадобежатьучитьсяпрограммировать.Носчитаю,чтодляинженераэтооченьважныйнавык.Да,дляинженера.Недлявсехнасвете.
Сейчасявнонаблюдаетсятенденция"Всеучимсяпрограммировать".Иэтохорошо,вцелом.Нопрограммированиеэтонечто-тоэлементарное.Этосложно,вэтонужновкладыватьмноговремени,особенно,есливыникогданеимелиотношениякинженерному/техническомумиру.
FAQ
23
Атоиногдаскладываетсявпечатление,чтодостаточнопройти"вотэтивоткурсы"ичерез3месяцавыкрутойпрограммистсвысокойзарплатой.Нет,этоткурснеобэтом:)
Мынеговоримвэтомкурсеопрограммированиикакпрофессииинеставимтакуюцель.Мыговоримопрограммированиикакинструменте,такомкак:знаниеCLIunix,например.
Иделоневтом,чтоинженерыкакие-тоособенные.Апросто,какправило:
ониужеимеюттехническоеобразованиемногиеработают,такилииначевкоманднойстрокесталкивались,какминимум,скаким-тоязыкомпрограммированияуних,таксказать,"инженерныйскладума"
Этонезначит,чтовсемостальным"недано".Простоинженерамэтобудетпроще.
Курсбудеткогда-топлатным?
Нет,курс,которыйопубликовантут,всегдабудетбесплатным.
Ячитаюплатноонлайнкурс"Pythonдлясетевыхинженеров".Ноэтонебудетвлиятьнаэтоткурс.Онвсегдабудетбесплатным.
FAQ
24
БлагодарностиСпасибовсем,ктопроявилинтерескпервомуанонсукурса.Вашинтересподтвердил,чтоэтобудеткому-тонужно.
ПавелПасынок,спасиботебезато,чтосогласилсянакурс.Свамибылоинтересноработатьиэтодобавиломнемотивациизавершитькурс.Ияособеннорада,чтознания,которыевыполучилинакурсе,нашлипрактическоеприменение.
АлексейКириллов,самоебольшоеспасиботебе:)Явсегдамоглаобсудитьстобойлюбойвопроспокурсу.Тыпомогалмнеподдерживатьмотивациюинеуходитьвдебри.Общениестобойвдохновляломеняпродолжать,особенновсложныемоменты.Спасиботебезавдохновение,положительныеэмоциииподдержку!
Благодарности
25
ПодготовкакработеДоначалаработысPython,нужноубедиться,чтоPythonустановленвоперационнойсистеме.
ЕсливыиспользуетеLinux/Unix/MacOS,то,скореевсего,Pythonужеустановлен.Инужнотолькопроверить,чтоустановленаверсия2.7(котораяиспользуетсявкурсе).
ЕсливыиспользуетеWindows,то,скореевсего,Pythonнужнобудетустановить.ОдинизсамыхпростыхвариантовдляWindows,установитьокружениеAnaconda.ВокружениитакжеестьIDESpyder,которыйможноиспользоватьвместоредактора.
ДляустановкиAnsibleпонадобитсяLinux.
Ещёодинважныймомент-выборредактора.ВследующемразделеприведеныпримерыредакторовдляразныхОС.
ВместоредактораможноиспользоватьIDE(Integrateddevelopmentenvironment).
IDEэтохорошаявещь,но,нестоитпереходитьнаIDEиз-затакихвещейкак:
подсветкакодаподсказкисинтаксисаавтоматическиеотступы(важнодляPython)
Всёэтоестьвлюбомхорошемредакторе.Какправило,дляэтогоможетпотребоватьсяустановитьдополнительныемодули.
Вначалеработы,можетполучитьсятак,чтоIDEбудеттолькоотвлекатьобилиемвозможностей.
СписокIDEдляPythonможноможнопосмотретьтут.Например,можновыбратьPyCharmили(дляWindows)Spyder.
ИдеальнымвариантомдлякурсабудетиспользованиевиртуалкиподуправлениемLinux/Unix.Этонеобязательно,номногиевещибудутнамногоудобней.Плюс,ненадобудетпереживатьзасвоюОС,зато,чтобынесделатьчего-толишнего.
1.Подготовкакработе
26
ОСиредакторДлятого,чтобыначатьработатьсPython,надоопределитьсяснесколькимивещами:
КакаяоперационнаясистемабудетиспользоватьсяКакойредакторбудетиспользоватьсяКакаяверсияPythonбудетиспользоваться
МожновыбратьлюбуюОСилюбойредактор.Но,желательноиспользоватьPythonверсии2.7,таккаквэтомкурсебудетиспользоватьсяэтаверсия.
ДлякурсажелательноиспользоватьнеWindows,таккак,например,AnsibleможноустановитьтольконаLinux/Unix.
Таккакпочтидлявсехразделов,подойдетлюбаяОС,можноотложитьвопросдоразделапоAnsible.
Вкурсеиспользуются:
DebianLinuxvim(редакторнеимеетпринципиальногозначения,лучшевыбратьнаиболееудобный)Python2.7
УстановкаPython2.7,еслиегонетвОС,выполняетсясамостоятельно.
ПопулярныередакторыдляразныхОС(vimиemacsнеуказаны):
Linux:gEditnanoSublimeTextgeany
MacOSTextMateTextWrangler
Windows:Notepad++
Дляначалаработы,можновзятьпервыйизспискадлясоответствующейоперационнойсистемы.
Далеевыводыкоманд,интерпретатора,скриптов,выполняютсянаDebianLinux.ВдругихОСвыводможетнезначительноотличаться.
ОСиредактор
27
ОСиредактор
28
СистемауправленияпакетамиpipДляустановкипакетовPython,которыепонадобятсявкурсе,будетиспользоватьсяpip.
pip-этосистемауправленияпакетами,котораяиспользуетсядляустановкипакетовизPythonPackageIndex(PyPi).
Скореевсего,еслиувасужеустановленPython,тоустановлениpip.
Проверкаpip:
$pip--version
pip1.1from/usr/lib/python2.7/dist-packages(python2.7)
Есликомандавыдалаошибку,значитpipнеустановлен.Установитьегоможнотак:
apt-getinstallpython-pip
Системауправленияпакетамиpip
29
virtualenv,virtualenvwrappervirtualenv-этоинструмент,которыйпозволяетсоздаватьвиртуальныеокружения.
Виртуальныеокружения:
позволяютизолироватьразличныепроектызависимости,которыхтребуютразныепроекты,находятсявразныхместах
Например,есливпроекте1требуетсяпакетверсии1.0,авпроекте2требуетсятотжепакет,новерсии3.1
пакеты,которыеустановленыввиртуальныхокружениях,неперебиваютглобальныепакеты
Вкурсеиспользуетсяvirtualenvwrapper:онпозволяетнемногопрощеработатьсvirtualenv.
Установкаvirtualenvwrapperспомощьюpip:
pipinstallvirtualenvwrapper
Послеустановки,в.bashrcнужнодобавитьнесколькострок
exportWORKON_HOME=~/venv
./usr/local/bin/virtualenvwrapper.sh
WORKON_HOME-указываетрасположениевиртуальныхокружений.Автораястрока-гденаходитсяскрипт,установленныйспакетомvirtualenvwrapper):
Длятогочтобыскриптvirtualenvwrapper.shвыполнилсяиможнобылоработатьсвиртуальнымиокружениями,надоперезапуститьbash.Например,такимобразом:
execbash
Такойвариантможетбытьневсегдаправильным.Подробнеевответенаstackoverflow.
Работасвиртуальнымиокружениями
Созданиеновоговиртуальногоокружения:
virtualenv,virtualenvwrapper
30
$mkvirtualenvPyNEng
NewpythonexecutableinPyNEng/bin/python
Installingdistribute........................done.
Installingpip...............done.
(PyNEng)$
Вскобкахпередстандартнымприглашениемотображаетсяимяпроекта(виртуальногоокружения).Этоозначает,чтомынаходимсявэтомвиртуальномокружении.
ВvirtualenvwrapperпоTabработаетавтопродолжениеименивиртуальногоокружения.
Этоособенноудобновтехслучаях,когдавиртуальныхокружениймного.
Теперьвтомкаталоге,которыйбылуказанвWORKON_HOME,созданкаталогPyNEng:
(PyNEng)$ls-lsvenv
total52
....
4-rwxr-xr-x1natanata99Sep3016:41preactivate
4-rw-r--r--1natanata76Sep3016:41predeactivate
4-rwxr-xr-x1natanata91Sep3016:41premkproject
4-rwxr-xr-x1natanata130Sep3016:41premkvirtualenv
4-rwxr-xr-x1natanata111Sep3016:41prermvirtualenv
4drwxr-xr-x6natanata4096Sep3016:42PyNEng
Выйтиизвиртуальногоокружения:
(PyNEng)$deactivate
$
Дляпереходавсозданноевиртуальноеокружение,надовыполнитькомандуworkon:
$workonPyNEng
(PyNEng)$
Еслинеобходимоперейтиизодноговиртуальногоокружениявдругое,тонеобязательноделатьdeactivate,можноперейтисразучерезworkon:
$workonTest
(Test)$workonPyNEng
(PyNEng)$
virtualenv,virtualenvwrapper
31
Есливиртуальноеокружениенужноудалить,используетсякомандаrmvirtualenv:
$rmvirtualenvTest
RemovingTest...
$
Посмотретькакиепакетыустановленыввиртуальномокружении:
(PyNEng)$lssitepackages
ANSI.pypexpect-3.3-py2.7.egg-info
ANSI.pycpickleshare-0.5-py2.7.egg-info
decorator-4.0.4-py2.7.egg-infopickleshare.py
decorator.pypickleshare.pyc
decorator.pycpip-1.1-py2.7.egg
distribute-0.6.24-py2.7.eggpxssh.py
easy-install.pthpxssh.pyc
fdpexpect.pyrequests
fdpexpect.pycrequests-2.7.0-py2.7.egg-info
FSM.pyscreen.py
FSM.pycscreen.pyc
IPythonsetuptools.pth
ipython-4.0.0-py2.7.egg-infosimplegeneric-0.8.1-py2.7.egg-info
ipython_genutilssimplegeneric.py
ipython_genutils-0.1.0-py2.7.egg-infosimplegeneric.pyc
path.pytest_path.py
path.py-8.1.1-py2.7.egg-infotest_path.pyc
path.pyctraitlets
pexpecttraitlets-4.0.0-py2.7.egg-info
Зависимости(requirements)
Приработенадпроектом,современем,ввиртуальномокружениинаходитсявсёбольшеустановленныхпакетов.И,принеобходимостиподелитьсяпроектомилископироватьегонадругойсервер,нужнобудетзановоустановитьвсезависимости.
ВPythonестьвозможность(ипринято)записатьвсезависимостивотдельныйфайл.
Этоделаетсятакимобразом:
(PyNEng)$pipfreeze>requirements.txt
Теперьвфайлеrequirements.txtнаходятсявсезависимости,сверсиямипакетов(файлrequirements.txt):
virtualenv,virtualenvwrapper
32
Jinja2==2.8
pexpect==4.0.1
tornado==4.3
...
Послеэтого,принеобходимостиустановитьвсезависимости,надоперейтивновоевиртуальноеокружение,котороевысоздалинасервереилипростонановыйсерверидатькоманду:
$pipinstall-rrequirements.txt
Тутвприглашениинетименивиртуальногоокружения,таккакустанавливатьтакимобразомзависимостиможнонетольковвиртуальномокружении,ноивсистемевцелом.
Установкапакетов
Например,установимввиртуальномокружениипакетsimplejson.
(PyNEng)$pipinstallsimplejson
...
Successfullyinstalledsimplejson
Cleaningup...
Еслиперейтивipython(рассматриваетсявразделеipython)иимпортироватьsimplejson,тоондоступениникакихошибокнет:
(PyNEng)$ipython
In[1]:importsimplejson
In[2]:simplejson
simplejson
In[2]:simplejson.
simplejson.Decimalsimplejson.decoder
simplejson.JSONDecodeErrorsimplejson.dump
simplejson.JSONDecodersimplejson.dumps
simplejson.JSONEncodersimplejson.encoder
simplejson.JSONEncoderForHTMLsimplejson.load
simplejson.OrderedDictsimplejson.loads
simplejson.absolute_importsimplejson.scanner
simplejson.compatsimplejson.simple_first
virtualenv,virtualenvwrapper
33
Но,есливыйтиизвиртуальноокружения,ипопытатьсясделатьтожесамое,тотакогомодулянет:
(PyNEng)$deactivate
$ipython
In[1]:importsimplejson
------------------------------------------------------------------
ImportErrorTraceback(mostrecentcalllast)
<ipython-input-1-ac998a77e3e2>in<module>()
---->1importsimplejson
ImportError:Nomodulenamedsimplejson
virtualenv,virtualenvwrapper
34
ИнтерпретаторPython(проверка)Передначаломработы,надопроверить,чтопривызовеинтерпретатораPython,выводбудеттаким:
$python
Python2.7.3(default,Mar132014,11:03:55)
[GCC4.7.2]onlinux2
Type"help","copyright","credits"or"license"formoreinformation.
>>>
Выводпоказывает,чтоустановленPython2.7.Приглашение>>>этостандартноеприглашениеинтерпретатораPython.
Вызовинтерпретаторавыполняетсяпокомандеpython,чтобывыйтинужнонабратьquit()либонажатьCtrl+D
ИнтерпретаторPython(проверка)
35
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание1.1
Единственноезаданиевэтомразделе:подготовкакработе.
Дляэтогонужно:
определитьсясОС,которуювыбудетеиспользоватьтаккаквсепримерывкурсеориентированынаLinux(Debian),желательноиспользоватьегожелательноиспользоватьновуювиртуальнуюмашину,чтобыбылоспокойнейэкспериментировать
установитьPython2.7проверить,чтоPythonиpipустановлены
создатьвиртуальноеокружение,вкоторомвыбудетеработатьвеськурсопределитьсясредактором/IDEначинаясраздела12,мыбудемподключатьсякоборудованию.Поэтомувамнужноподготовитьвиртуальноеилиреальноеоборудование
Задания
36
НачалоработысPythonВэтомразделерассматриваются:
синтаксисPythonработавинтерактивномрежимепеременныевPython
2.НачалоработысPython
37
СинтаксисPythonПервое,что,какправило,бросаетсявглаза,еслиговоритьосинтаксисеPython,этото,чтоотступыимеютзначение:
ониопределяюткакиевыраженияпопадаютвблоккодакогдаблоккодазаканчивается
ПримеркодаPython:
a=10
b=5
ifa>b:
print"AбольшеB"
print"Тутможновыполнитьещёчто-то"
else:
print"BбольшеилиравноA"
print"Продолжаемтутже"
print"TheEnd"
Обратитевнимание,чтотуткодпоказандлядемонстрациисинтаксиса.ВследующихразделахрассматриваютсятипыданныхPythonисозданиескриптов.
Несмотрянато,чтоещенерассматриваласьконструкцияif/else,всёдолжнобытьпонятно.
Pythonпонимаеткакиестрокиотносятсякifнаосновеотступов.Выполнениечастиifa>bзаканчивается,когдавстречаетсястрокастемжеотступом,чтоисамастрокаifa>b.
Аналогичносблокомelse.
ВтораяособенностьPython:посленекоторыхвыраженийдолжноидтидвоеточие(например,послеifa>bилипослеelse).
Несколькоправилирекомендаций:
ВкачествеотступовмогутиспользоватьсяTabилипробелы.лучшеиспользоватьпробелы
аточнее,настроитьредактортак,чтобыTabбылравен4пробелам(тогда,прииспользованииTab,будутставиться4пробела)
Количествопробеловдолжнобытьодинаковымводномблоке.лучшечтобыколичествопробеловбылоодинаковымвовсемкоде
СинтаксисPython
38
популярныйвариантиспользовать2-4пробела(вкурсеиспользуются4пробела)
Длятогочтобыпроблемсотступаминебыло,надосразунастроитьредактортакимобразом,чтобыотступыделалисьавтоматически.
Ещеоднаособенностьприведенногопримеракода:пустыестроки.Такимобразомкодформатируется,чтобыегобылопрощечитать.
ОстальныеособенностисинтаксисабудутпоказанывпроцессезнакомствасструктурамиданныхвPython.
Комментарии
Принаписаниикода,частонужнонаписатькомментарий.Например,чтобыописатьособенностиработыкода.
КомментариивPythonмогутбытьоднострочными:
#Оченьважныйкомментарий
a=10
b=5#Оченьнужныйкомментарий
Однострочныекомментарииначинаютсясознака#.
Обратитевнимание,чтокоментарийможетбытьивстрокегденаходитсякод,исампосебе.
Принеобходимостинаписатьнесколькострокскомментариями,чтобынеставитьпередкаждойрешетку,можносделатьмногострочныйкомментарий:
"""Оченьважный
идлинныйкомментарий
"""
a=10
b=5
Комментариимогутиспользоватьсякакдлятого,чтобыкомментировать,чтопроисходитвкоде,такидлятого,чтобызакомментироватьвременнокакое-товыражение.
СинтаксисPython
39
ИнтерпретаторPython.iPythonИнтерпретаторпозволяетполучатьмоментальныйоткликнавыполненныедействия.
Можносказать,чтоинтерпретаторработаеткаккоманднаястрокасетевыхустройств:каждаякомандабудетвыполнятьсясразупосленажатияenter(покрайнеймере,похоженаcisco).
НодляинтерпретатораPythonестьисключение:болеесложныеобъекты(например,циклы,функции)выполняются,толькопосленажатияenterдвараза..
Впредыдущемразделе,дляпроверкиустановкиPython,вызвалсястандартныйинтерпретатор.
Но,кроменего,вPythonестьусовершенствованныйинтерпретаторiPython(документацияiPython).
iPythonпозволяетнамногобольше,чемстандартныйинтерпретатор,которыйвызываетсяпокомандеpython.
Несколькопримеров(возможностиipythonнамногошире):
автопродолжениекомандпоTabилиподсказка,есливариантовкоманднесколькоболееструктурированныйипонятныйвыводкомандавтоматическиеотступывциклахидругихобъектахисториявыполнениякоманд
понейможнопередвигатьсяилипосмотреть"волшебной"командой%history
УстановитьiPythonможноспомощьюpip(есливиртуальнаямашинаустанавливаетсяснуля):
apt-getinstallpython-pippython-dev
pipinstall—upgradepip
pipinstallipython
Еслисустановкойipythonвозникаютпроблемы,послекоторыхнеработаетpip,выполнитетакиедействия:
Еслиставиличерезaptpip,тонадоегоудалить:
apt-getremovepython-pip
ИнтерпретаторPython.iPython
40
Послеэтогозановопереустановитьpipиустановитьipythonтакимобразом:
wgethttps://bootstrap.pypa.io/get-pip.py
pythonget-pip.py
apt-getinstallpython-dev
pipinstallipython
ДалеекакинтерпретаторбудетиспользоватьсяiPython.
Длязнакомствасинтерпретаторомможнопопробоватьегоиспользоватькаккалькулятор:
In[1]:1+2
Out[1]:3
In[2]:22*45
Out[2]:990
In[3]:2**3
Out[3]:8
ВiPythonвводивыводподписаны:
In-этото,чтонаписалпользовательOut-этовыводкоманды(еслионесть)ЧислапослеInиOutэтонумерациявыполненныхкомандвтекущейсессииiPython
Примервыводастроки:
In[4]:print'Hello!'
Hello!
Когдавинтерпретаторесоздается,например,цикл,товнутрициклаприглашениеменяетсяна....Длявыполненияциклаивыходаизэтогоподрежима,необходимодваждынажатьEnter:
In[5]:foriinrange(5):
...:printi
...:
0
1
2
3
4
ИнтерпретаторPython.iPython
41
Операторprintпозволяетвывестиинформациюнастандартныйпотоквывода.
Еслинеобходимовывестистроку,тоеенужнообязательнозаключитьвкавычки(двойныеилиодинарные).Еслиженужновывести,например,результатвычисленияилипросточисло,токавычкиненужны:
In[6]:print'Hello!'
Hello!
In[7]:print5*5
25
Еслинужновывестинесколькозначений,можноперечислитьихчереззапятую:
In[8]:print1*5,2*5,3*5,4*5
5101520
In[9]:print'one','two','three'
onetwothree
Поумолчанию,вконцевыражениябудетпереводстроки.Еслинеобходимо,чтобыпослевыводавыражениянебылопереводастроки,товконцекомандыставитсязапятая(обратитевнимание,чтовинтепретатореэтонезаметно.Поэтомулучшеэтопопробоватьвфайле).
Допустим,естьфайлfile4.py(обратитевниманиеназапятуюпослепервойстроки):
print'Hello!',
print'Hello!'
Тогдапривыполнениискрипта,выводбудетвыглядетьтак(послепервогоHelloнетпереводастроки,ноестьпробел):
Hello!Hello!
КаксоздаватьскриптырассматриваетсявразделеСозданиебазовыхскриптов.
dir
Командаdir()можетиспользоватьсядлятого,чтобыпосмотретькакиеатрибутыиметодыестьуобъекта.
ИнтерпретаторPython.iPython
42
Например,длячиславыводбудеттаким(обратитевниманиенаразличныеметоды,которыепозволяютделатьарифметическиеоперации):
In[10]:dir(5)
Out[10]:
['__abs__',
'__add__',
'__and__',
...
'bit_length',
'conjugate',
'denominator',
'imag',
'numerator',
'real']
Длястроки:
In[11]:dir('hello')
Out[11]:
['__add__',
'__class__',
'__contains__',
...
'startswith',
'strip',
'swapcase',
'title',
'translate',
'upper',
'zfill']
Есливыполнитькомандубезпередачизначения,тоонапоказываетсуществующиеметоды,атрибутыипеременные,определенныевтекущейсессииинтерпретатора:
ИнтерпретаторPython.iPython
43
In[12]:dir()
Out[12]:
['__builtin__',
'__builtins__',
'__doc__',
'__name__',
'_dh',
...
'_oh',
'_sh',
'exit',
'get_ipython',
'i',
'quit']
Примерпослесозданияпеременнойaифункцииtest:
In[13]:a='hello'
In[14]:deftest():
....:print'test'
....:
In[15]:dir()
Out[15]:
...
'a',
'exit',
'get_ipython',
'i',
'quit',
'test']
ИнтерпретаторPython.iPython
44
MagiccommandsВIPythonестьспециальныекоманды,которыеупрощаютработусинтерпретатором.Всеониначинаютсяна%.
%history
Например,команда%historyпозволяетпросмотретьисториютекущейсессии:
In[1]:a=10
In[2]:b=5
In[3]:ifa>b:
...:print"Aisbigger"
...:
Aisbigger
In[4]:%history
a=10
b=5
ifa>b:
print"Aisbigger"
%history
Такимобразомможноскопироватькакой-тоблоккода.
%cpaste
Ещеоднаоченьполезнаяволшебнаякоманда%cpaste
ПривставкекодасотступамивIPython,из-заавтоматическихотступовсамогоipython,начинаетсдвигатьсякод:
МагияiPython
45
In[1]:a=10
In[2]:b=5
In[3]:ifa>b:
...:print"Aisbigger"
...:else:
...:print"Aislessorequal"
...:
Aisbigger
In[4]:%hist
a=10
b=5
ifa>b:
print"Aisbigger"
else:
print"Aislessorequal"
%hist
In[5]:ifa>b:
...:print"Aisbigger"
...:else:
...:print"Aislessorequal"
...:
File"<ipython-input-8-4d18ff094f5c>",line3
else:
^
IndentationError:unindentdoesnotmatchanyouterindentationlevel
IfyouwanttopastecodeintoIPython,trythe%pasteand%cpastemagicfunctions.
Обратитевниманиенапоследнююстроку.IPythonподсказываеткакойкомандойвоспользоваться,чтобыкорректновставитьтакойкод.
Команды%pasteи%cpasteработаютнемногопо-разному.
%cpaste(послетогокаквсестрокископированы,надозавершитьработукомандынабрав'--'):
In[9]:%cpaste
Pastingcode;enter'--'aloneonthelinetostoporuseCtrl-D.
:ifa>b:
:print"Aisbigger"
:else:
:print"Aislessorequal"
:--
Aisbigger
%paste(требуетустановленногоTkinter):
МагияiPython
46
In[10]:%paste
ifa>b:
print"Aisbigger"
else:
print"Aislessorequal"
##--Endpastedtext--
Aisbigger
ПодробнееобIPythonможнопочитатьвдокументацияIPython.
КороткоинформациюможнопосмотретьвсамомIPythonкомандой%quickref:
МагияiPython
47
IPython--AnenhancedInteractivePython-QuickReferenceCard
===========================================================
obj?,obj??:Gethelp,ormorehelpforobject(alsoworksas
?obj,??obj).
?foo.*abc*:Listnamesin'foo'containing'abc'inthem.
%magic:InformationaboutIPython's'magic'%functions.
Magicfunctionsareprefixedby%or%%,andtypicallytaketheirarguments
withoutparentheses,quotesorevencommasforconvenience.Linemagicstakea
single%andcellmagicsareprefixedwithtwo%%.
Examplemagicfunctioncalls:
%aliasdls-F:'d'isnowanaliasfor'ls-F'
aliasdls-F:Worksif'alias'notapythonname
alist=%alias:Getlistofaliasesto'alist'
cd/usr/share:Obvious.cd-<tab>tochoosefromvisiteddirs.
%cd??:SeehelpANDsourceformagic%cd
%timeitx=10:timethe'x=10'statementwithhighprecision.
%%timeitx=2**100
x**100:time'x**100'withasetupof'x=2**100';setupcodeisnot
counted.Thisisanexampleofacellmagic.
Systemcommands:
!cpa.txtb/:Systemcommandescape,callsos.system()
cpa.txtb/:after%rehashx,mostsystemcommandsworkwithout!
cp${f}.txt$bar:Variableexpansioninmagicsandsystemcommands
files=!ls/usr:Capturesytemcommandoutput
files.s,files.l,files.n:"abc",['a','b','c'],'a\nb\nc'
History:
_i,_ii,_iii:Previous,nextprevious,nextnextpreviousinput
_i4,_ih[2:5]:Inputhistoryline4,lines2-4
exec_i81:Executeinputhistoryline#81again
%rep81:Editinputhistoryline#81
_,__,___:previous,nextprevious,nextnextpreviousoutput
_dh:Directoryhistory
_oh:Outputhistory
%hist:Commandhistoryofcurrentsession.
%hist-gfoo:Searchcommandhistoryof(almost)allsessionsfor'foo'.
%hist-g:Commandhistoryof(almost)allsessions.
%hist1/2-8:Commandhistorycontaininglines2-8ofsession1.
%hist1/~2/:Commandhistoryofsession1and2sessionsbeforecurrent.
МагияiPython
48
ПеременныеПеременныевPython:
нетребуютобъявлениятипапеременной(Pythonязыксдинамическойтипизацией)являютсяссылкаминаобластьпамяти
Правилаименованияпеременных:
имяпеременнойможетсостоятьтолькоизбукв,цифризнакаподчеркиванияимянеможетначинатьсясцифрыимянеможетсодержатьспециальныхсимволов@,$,%
СоздаватьпеременныевPythonоченьпросто:
In[1]:a=3
In[2]:b='Hello'
In[3]:c,d=9,'Test'
In[4]:printa,b,c,d
3Hello9Test
Обратитевнимание,чтовPythonненужноуказывать,чтоaэточисло,аbэтострока.
Переменныеявляютсяссылкаминаобластьпамяти.Этолегкопродемонстрироватьспомощьюфункцииid(),котораяпоказываетидентификаторобъекта:
In[5]:a=b=c=33
In[6]:id(a)
Out[6]:31671480
In[7]:id(b)
Out[7]:31671480
In[8]:id(c)
Out[8]:31671480
Вэтомпримеревидно,чтовсетриимениссылаютсянаодинитотжеидентификатор.Тоесть,этоодинитотжеобъект,накоторыйуказываюттриссылкиa,bиc.
Счислами,уPythonестьещёоднаособенность,котораяможетнемногосбить.Числаот-5до256заранеесозданыихранятсявмассиве(списке).Поэтому,присозданиичислаизэтогодиапазона,фактическисоздаетсяссылканачисловсозданноммассиве.
Переменные
49
ЭтаособенностьхарактернаименнодляреализацииCPython,котораярассматриваетсявкурсе.
Этоможнопроверитьтакимобразом:
In[9]:a=3
In[10]:b=3
In[11]:id(a)
Out[11]:4400936168
In[12]:id(b)
Out[12]:4400936168
In[13]:id(3)
Out[13]:4400936168
Обратитевнимание,чтоуa,bичисла3одинаковыеидентификаторы.Всеонипростоявляютсяссылкаминасуществующеечисловсписке.
Но,еслисделатьтожесамоесчисломбольше256:
In[14]:a=500
In[15]:b=500
In[16]:id(a)
Out[16]:140239990503056
In[17]:id(b)
Out[17]:140239990503032
In[18]:id(500)
Out[18]:140239990502960
Идентификаторыувсехразные.
Приэтом,еслисделатьприсваиваниетакоговида:
In[19]:a=b=c=500
Идентификаторыбудутувсеходинаковые:
Переменные
50
In[20]:id(a)
Out[20]:140239990503080
In[21]:id(b)
Out[21]:140239990503080
In[22]:id(c)
Out[22]:140239990503080
Таккак,втакомвариантеa,bиcпростоссылаютсянаодинитотжеобъект.
Именапеременных
Именапеременныхнедолжныпересекатьсясоператорамииназваниямимодулейилидругихзарезервированныхзначений.
ВPythonестьрекомендациипоименованиюфункций,классовипеременных:
именапеременныхобычнопишутсяполностьюбольшимиилималенькимибуквами
DB_NAMEdb_name
именафункцийзадаютсямаленькимибуквами,сподчеркиваниямимеждусловами
get_namesименаклассовзадаютсясловамисзаглавнымибуквами,безпробелов
CiscoSwitch
Переменные
51
ТипыданныхвPythonВPythonестьнесколькостандартныхтиповданных:
Numbers(числа)Strings(строки)Lists(списки)Dictionary(словари)Tuples(кортежи)Sets(множества)Boolean
Этитипыданныхможно,всвоюочередь,классифицироватьпонесколькимпризнакам:
Изменяемые:СпискиСловариМножества
НеизменяемыеЧислаСтрокиКортежи
Упорядоченные:СпискиКортежиСтроки
Неупорядоченные:СловариМножества
3.ТипыданныхвPython
52
ЧислаПримерразличныхтиповчисловыхзначений:
int(40,-80)float(1.5,-30.7)long(52934861L)
Счисламиможновыполнятьразличныематематическиеоперации.
In[1]:1+2
Out[1]:3
In[2]:1.0+2
Out[2]:3.0
In[3]:10-4
Out[3]:6
In[4]:2**3
Out[4]:8
Обратитевниманиенаотличияделенияintиfloat:
In[5]:10/3
Out[5]:3
In[6]:10/3.0
Out[6]:3.3333333333333335
In[7]:10/float(3)
Out[7]:3.3333333333333335
In[8]:float(10)/3
Out[8]:3.3333333333333335
Спомощьюфункцииroundможноокруглятьчисладонужногоколичествазнаков:
In[9]:round(10/3.0,2)
Out[9]:3.33
In[10]:round(10/3.0,4)
Out[10]:3.3333
Остатокотделения:
Числа
53
In[11]:10%3
Out[11]:1
Операторысравнения
In[12]:10>3.0
Out[12]:True
In[13]:10<3
Out[13]:False
In[14]:10==3
Out[14]:False
In[15]:10==10
Out[15]:True
In[16]:10<=10
Out[16]:True
In[17]:10.0==10
Out[17]:True
Функцияint()позволяетвыполнятьконвертациювтипint.Вовторомаргументеможноуказыватьсистемуисчисления:
In[18]:a='11'
In[19]:int(a)
Out[19]:11
Еслиуказать,чтострокуaнадовосприниматькакдвоичноечисло,торезультатбудеттаким:
In[20]:int(a,2)
Out[20]:3
Конвертациявintтипаfloat:
In[21]:int(3.333)
Out[21]:3
In[22]:int(3.9)
Out[22]:3
Числа
54
Функцияbinпозволяетполучитьдвоичноепредставлениечисла(обратитевнимание,чторезультатстрока):
In[23]:bin(8)
Out[23]:'0b1000'
In[24]:bin(255)
Out[24]:'0b11111111'
Аналогично,функцияhex()позволяетполучитьшестнадцатеричноезначение:
In[25]:hex(10)
Out[25]:'0xa'
И,конечноже,можноделатьнесколькопреобразованийодновременно:
In[26]:int('ff',16)
Out[26]:255
In[27]:bin(int('ff',16))
Out[27]:'0b11111111'
Дляболеесложныхматематическихфункций,вPythonестьмодульmath:
In[28]:importmath
In[29]:math.sqrt(9)
Out[29]:3.0
In[30]:math.sqrt(10)
Out[30]:3.1622776601683795
In[31]:math.factorial(3)
Out[31]:6
In[32]:math.pi
Out[32]:3.141592653589793
Числа
55
Строки(Strings)СтрокавPython-этопоследовательностьсимволов,заключеннаявкавычки.
Строки-этонеизменяемый,упорядоченныйтипданных.
Примерыстрок:
In[9]:'Hello'
Out[9]:'Hello'
In[10]:"Hello"
Out[10]:'Hello'
In[11]:tunnel="""
....:interfaceTunnel0
....:ipaddress10.10.10.1255.255.255.0
....:ipmtu1416
....:ipospfhello-interval5
....:tunnelsourceFastEthernet1/0
....:tunnelprotectionipsecprofileDMVPN
....:"""
In[12]:tunnel
Out[12]:'\ninterfaceTunnel0\nipaddress10.10.10.1255.255.255.0\nipmtu1416\nip
ospfhello-interval5\ntunnelsourceFastEthernet1/0\ntunnelprotectionipsecprofi
leDMVPN\n'
In[13]:printtunnel
interfaceTunnel0
ipaddress10.10.10.1255.255.255.0
ipmtu1416
ipospfhello-interval5
tunnelsourceFastEthernet1/0
tunnelprotectionipsecprofileDMVPN
Строкиможносуммировать.Тогдаониобъединяютсяводнустроку:
In[14]:intf='interface'
In[15]:tun='Tunnel0'
In[16]:intf+tun
Out[16]:'interfaceTunnel0'
In[17]:intf+''+tun
Out[17]:'interfaceTunnel0'
Строки(Strings)
56
Строкуможноумножатьначисло.Вэтомслучае,строкаповторяетсяуказанноеколичествораз:
In[18]:intf*5
Out[18]:'interfaceinterfaceinterfaceinterfaceinterface'
In[19]:'#'*40
Out[19]:'########################################'
То,чтострокиявляютсяупорядоченнымтипомданныхпозволяетобращатьсяксимволамвстрокепономеру,начинаяснуля:
In[20]:string1='interfaceFastEthernet1/0'
In[21]:string1[0]
Out[21]:'i'
Нумерациявсехсимволоввстрокеидетснуля.Но,еслинужнообратитьсяккакому-топосчетусимволу,начинаясконца,томожноуказыватьотрицательныезначения(наэтотразсединицы).
In[22]:string1[1]
Out[22]:'n'
In[23]:string1[-1]
Out[23]:'0'
Кромеобращениякконкретномусимволу,можноделатьсрезыстроки,указавдиапазонномеров(срезвыполняетсяповтороечисло,невключаяего):
In[24]:string1[0:9]
Out[24]:'interface'
In[25]:string1[10:22]
Out[25]:'FastEthernet'
Еслинеуказываетсявтороечисло,тосрезбудетдоконцастроки:
In[26]:string1[10:]
Out[26]:'FastEthernet1/0'
Срезатьтрипоследнихсимволастроки:
Строки(Strings)
57
In[27]:string1[-3:]
Out[27]:'1/0'
Строкавобратномпорядке:
In[28]:a='0123456789'
In[29]:a[::]
Out[29]:'0123456789'
In[30]:a[::-1]
Out[30]:'9876543210'
Записиa[::]иa[:]даютодинаковыйрезультат,нодвойноедвоеточиепозволяетуказывать,чтонадобратьнекаждыйэлемент,а,например,каждыйвторой.
Например,такимобразом,можнополучитьвсечетныечисластрокиa:
In[31]:a[::2]
Out[31]:'02468'
Такможнополучитьнечетные:
In[32]:a[1::2]
Out[32]:'13579'
Строки(Strings)
58
Полезныеметодыдляработысостроками
Таккакконфигурационныйфайл-этопростотекстовыйфайл,приавтоматизации,оченьчастонадобудетработатьсостроками.
Знаниеразличныхметодов(тоесть,действий),которыеможноприменятькстрокам,помогаетболееэффективноработатьсними.
Чащевсегоэтиметодыбудутприменятьсявциклах,приобработкефайла.
upper(),lower(),swapcase(),capitalize()
Методыupper(),lower(),swapcase(),capitalize()выполняютпреобразованиерегистрастроки:
In[25]:string1='FastEthernet'
In[26]:string1.upper()
Out[26]:'FASTETHERNET'
In[27]:string1.lower()
Out[27]:'fastethernet'
In[28]:string1.swapcase()
Out[28]:'fASTeTHERNET'
In[29]:string2='tunnel0'
In[30]:string2.capitalize()
Out[30]:'Tunnel0'
Оченьважнообращатьвниманиенато,чточастометодывозвращаютпреобразованнуюстроку.И,значит,надонезабытьприсвоитьеекакой-топеременной(можнотойже).
In[31]:string1=string1.upper()
In[32]:printstring1
FASTETHERNET
Такпроисходитиз-затого,чтострокаэтонеизменяемыйтипданных.
count()
Полезныеметодыдляработысостроками
59
Методcount()используетсядляподсчетатого,сколькоразсимволилиподстрока,встречаютсявстроке:
In[33]:string1='Hello,hello,hello,hello'
In[34]:string1.count('hello')
Out[34]:3
In[35]:string1.count('ello')
Out[35]:4
In[36]:string1.count('l')
Out[36]:8
find()
Методуfind()можнопередатьподстрокуилисимволионпокажетнакакойпозициинаходитсяпервыйсимволподстроки(дляпервогосовпадения):
In[37]:string1='interfaceFastEthernet0/1'
In[38]:string1.find('Fast')
Out[38]:10
In[39]:string1[string1.find('Fast')::]
Out[39]:'FastEthernet0/1'
startswith(),endswith()
Проверканатоначинается(илизаканчивается)листроканаопределенныесимволы(методыstartswith(),endswith()):
In[40]:string1='FastEthernet0/1'
In[41]:string1.startswith('Fast')
Out[41]:True
In[42]:string1.startswith('fast')
Out[42]:False
In[43]:string1.endswith('0/1')
Out[43]:True
In[44]:string1.endswith('0/2')
Out[44]:False
Полезныеметодыдляработысостроками
60
replace()
Заменапоследовательностисимволоввстроке,надругуюпоследовательность(методreplace()):
In[45]:string1='FastEthernet0/1'
In[46]:string1.replace('Fast','Gigabit')
Out[46]:'GigabitEthernet0/1'
strip()
Частоприобработкефайла,файлоткрываетсяпострочно.Новконцекаждойстроки,какправило,естькакие-тоспецсимволы(амогутбытьивначале).Например,переводстроки.
Длятого,чтобыизбавитьсяотних,оченьудобноиспользоватьметодstrip():
In[47]:string1='\n\tinterfaceFastEthernet0/1\n'
In[48]:printstring1
interfaceFastEthernet0/1
In[49]:string1
Out[49]:'\n\tinterfaceFastEthernet0/1\n'
In[50]:string1.strip()
Out[50]:'interfaceFastEthernet0/1'
Методstrip()убираетспецсимволыивначалеивконцестроки.Еслинеобходимоубратьсимволытолькослеваилитолькосправа,можноиспользовать,соответственно,методыlstrip()иrstrip().
split()
Методsplit()разбиваетстрокуначасти,используякакразделителькакой-тосимвол(илисимволы).Поумолчанию,вкачестверазделителяиспользуетсяпробел.Новскобкахможноуказатьлюбойразделитель.
Врезультате,строкабудетразбитаначастипоуказаномуразделителюипредставленаввидечастей,которыесодержатсявсписке:
Полезныеметодыдляработысостроками
61
In[51]:string1='switchporttrunkallowedvlan10,20,30,100-200'
In[52]:string1.split()
Out[52]:['switchport','trunk','allowed','vlan','10,20,30,100-200']
Ещеодинпример:
In[53]:string1='switchporttrunkallowedvlan10,20,30,100-200\n'
In[54]:commands=string1.strip().split()
In[55]:printcommands
['switchport','trunk','allowed','vlan','10,20,30,100-200']
In[56]:vlans=commands[-1].split(',')
In[57]:printvlans
['10','20','30','100-200']
Встрокеstring1былсимволпробелавначалеисимволпереводастрокивконце.Встрокеномер54,спомощьюметодаstrip(),этисимволыудаляются.
Методstrip()возвращаетстроку,котораяобрабатываетсяметодомsplit()иразделяетстрокуначасти,используяпробел,какразделитель.Итоговаястрокаприсваиваетсяпеременнойcommands.
Используятотжеспособ,чтоисостроками,кпоследнемуобъектувспискеvlans,применяетсяметодsplit().Но,наэтотраз,внутрискобокуказываетсядругойразделитель-запятая.Витоге,вспискеvlans,находятсяномераVLAN.
Уметодаsplit()естьещёоднахорошаяособенность:поумолчанию,методразбиваетстрокунепоодномупробелу,аполюбомуколичествупробелов.Этобудеточеньполезнымприобработкекомандshow.Например:
In[58]:sh_ip_int_br="FastEthernet0/015.0.15.1YESmanualup
up"
In[59]:sh_ip_int_br.split()
Out[59]:['FastEthernet0/0','15.0.15.1','YES','manual','up','up']
Авоттаквыглядитразделениетойжестроки,когдаодинпробелиспользуетсякакразделитель:
Полезныеметодыдляработысостроками
62
In[60]:sh_ip_int_br.split('')
Out[60]:
['FastEthernet0/0','','','','','','','','','','','','15.0.15.1','','','
','','','','YES','manual','up','','','','','','','','','','','','',
'','','','','','','','up']
Полезныеметодыдляработысостроками
63
Форматированиестрок
Приработесостроками,частовозникаютситуации,когдавшаблонстрокинадоподставитьразныеданные.
Этоможноделатьобъединяячастистрокииданные,новPythonестьболееудобныйспособ:форматированиестрок.
Существуетдвавариантаформатированиястрок:
соператором%(болеестарыйвариант)методомformat()(новыйвариант)
Несмотрянато,чторекомендуетсяиспользоватьметодformat,частоможновстретитьформатированиестрокичерезоператор%.
Примериспользованияметодаformat:
In[1]:"interfaceFastEthernet0/{}".format('1')
Out[1]:'interfaceFastEthernet0/1'
Аналогичныйпримерсоператором%:
In[2]:"interfaceFastEthernet0/%s"%'1'
Out[2]:'interfaceFastEthernet0/1'
Вформатированиистрокспециальныесимволыуказываюткакойтипданныхбудетподставляться:
%s-строкаилилюбойдругойобъектвкотороместьстроковоепредставление%d-integer%f-float
Спомощьюформатированиястрокможновыводитьрезультатстолбцами.Вформатированиистрокможноуказыватькакоеколичествосимволоввыделенонаданные.Есликоличествосимволоввданныхменьше,чемвыделенноеколичествосимволов,недостающиесимволызаполняютсяпробелами.
Например,такимобразомможновывестиданныестолбцамиодинаковойшириныпо15символовсвыравниваниемпоправойстороне:
Форматированиестрок
64
In[3]:vlan,mac,intf=['100','aabb.cc80.7000','Gi0/1']
In[4]:print"%15s%15s%15s"%(vlan,mac,intf)
100aabb.cc80.7000Gi0/1
In[5]:print"{:>15}{:>15}{:>15}".format(vlan,mac,intf)
100aabb.cc80.7000Gi0/1
Выравниваниеполевойстороне:
In[6]:print"%-15s%-15s%-15s"%(vlan,mac,intf)
100aabb.cc80.7000Gi0/1
In[7]:print"{:15}{:15}{:15}".format(vlan,mac,intf)
100aabb.cc80.7000Gi0/1
Спомощьюформатированиястрок,можнотакжевлиятьнаотображениечисел.
Например,можноуказатьсколькоцифрпослезапятойвыводить:
In[8]:print"%.3f"%(10.0/3)
3.333
In[9]:print"{:.3f}".format(10.0/3)
3.333
Конвертироватьвдвоичныйформат,указатьсколькоцифрдолжнобытьвотображениичислаидополнитьнедостающеенулями:
In[10]:'{:08b}'.format(10)
Out[10]:'00001010'
Аналогичныйрезультатможнополучитьспомощьюметодаzfill:
In[11]:bin(10)[2:].zfill(8)
Out[11]:'00001010'
Уформатированиястрокестьещёмноговозможностей.Хорошиепримерыиобъяснениядвухвариантовформатированиястрокможнонайтитут.
Форматированиестрок
65
Список(List)Списокэтоизменяемыйупорядоченныйтипданных.
СписоквPython-этопоследовательностьэлементов,разделенныхмеждусобойзапятойизаключенныхвквадратныескобки.
Примерысписков:
In[1]:list1=[10,20,30,77]
In[2]:list2=['one','dog','seven']
In[3]:list3=[1,20,4.0,'word']
Таккаксписок-этоупорядоченныйтипданных,то,какивстроках,вспискахможнообращатьсякэлементупономеру,делатьсрезы:
In[4]:list3=[1,20,4.0,'word']
In[5]:list3[1]
Out[5]:20
In[6]:list3[1::]
Out[6]:[20,4.0,'word']
In[7]:list3[-1]
Out[7]:'word'
In[8]:list3[::-1]
Out[8]:['word',4.0,20,1]
Перевернутьсписокнаоборот,можноиспомощьюметодаreverse():
In[10]:vlans=['10','15','20','30','100-200']
In[11]:vlans.reverse()
In[12]:vlans
Out[12]:['100-200','30','20','15','10']
Таккакспискиизменяемые,элементыспискаможноменять:
Список(List)
66
In[13]:list3
Out[13]:[1,20,4.0,'word']
In[14]:list3[0]='test'
In[15]:list3
Out[15]:['test',20,4.0,'word']
Можносоздаватьисписоксписков.И,какивобычномсписке,можнообращатьсякэлементамвовложенныхсписках:
In[16]:interfaces=[['FastEthernet0/0','15.0.15.1','YES','manual','up','up'],
....:['FastEthernet0/1','10.0.1.1','YES','manual','up','up'],
....:['FastEthernet0/2','10.0.2.1','YES','manual','up','down']]
In[17]:interfaces[0][0]
Out[17]:'FastEthernet0/0'
In[18]:interfaces[2][0]
Out[18]:'FastEthernet0/2'
In[19]:interfaces[2][1]
Out[19]:'10.0.2.1'
Список(List)
67
Полезныеметодыдляработысосписками
join()
Методjoin()собираетсписокстрокводнустрокусразделителем,которыйуказанвjoin():
In[16]:vlans=['10','20','30','100-200']
In[17]:','.join(vlans[:-1])
Out[17]:'10,20,30'
append()
Методappend()добавляетвконецспискауказанныйэлемент:
In[18]:vlans=['10','20','30','100-200']
In[19]:vlans.append('300')
In[20]:vlans
Out[20]:['10','20','30','100-200','300']
extend()
Еслинужнообъединитьдвасписка,томожноиспользоватьдваспособа.Методextend()иоперациюсложения:
In[21]:vlans=['10','20','30','100-200']
In[22]:vlans2=['300','400','500']
In[23]:vlans.extend(vlans2)
In[24]:vlans
Out[24]:['10','20','30','100-200','300','400','500']
In[25]:vlans+vlans2
Out[25]:['10','20','30','100-200','300','400','500','300','400','500']
In[26]:vlans
Out[26]:['10','20','30','100-200','300','400','500']
Полезныеметодыдляработысосписками
68
Ноприэтомметодextend()расширяетсписок"наместе",априоперациисложениявыводитсяитоговыйсуммарныйсписок,ноисходныеспискинеменяются.
pop()
Методpop()удаляетэлемент,которыйсоответствуетуказанномуномеру.Но,чтоважно,приэтомметодвозвращаетэтотэлемент:
In[28]:vlans=['10','20','30','100-200']
In[29]:vlans.pop(-1)
Out[29]:'100-200'
In[30]:vlans
Out[30]:['10','20','30']
Безуказанияномераудаляетсяпоследнийэлементсписка.
remove()
Методremove()удаляетуказанныйэлемент.
remove()невозвращаетудаленныйэлемент:
In[31]:vlans=['10','20','30','100-200']
In[32]:vlans.remove('20')
In[33]:vlans
Out[33]:['10','30','100-200']
Вметодеremoveнадоуказыватьсамэлемент,которыйнадоудалить,анеегономервсписке.Еслиуказатьномерэлемента,возникнетошибка:
In[34]:vlans.remove(-1)
-------------------------------------------------
ValueErrorTraceback(mostrecentcalllast)
<ipython-input-32-f4ee38810cb7>in<module>()
---->1vlans.remove(-1)
ValueError:list.remove(x):xnotinlist
index()
Полезныеметодыдляработысосписками
69
Методindex()используетсядлятого,чтобыпроверитьподкакимномеромвспискехранитсяэлемент:
In[35]:vlans=['10','20','30','100-200']
In[36]:vlans.index('30')
Out[36]:2
insert()
Методinsert()позволяетвставитьэлементнаопределенноеместовсписке:
In[37]:vlans=['10','20','30','100-200']
In[38]:vlans.insert(1,'15')
In[39]:vlans
Out[39]:['10','15','20','30','100-200']
Полезныеметодыдляработысосписками
70
Вариантысозданиясписка
Созданиеспискаспомощьюлитерала:
In[1]:vlans=[10,20,30,50]
Литерал-этовыражение,котороесоздаетобъект.
Созданиеспискаспомощьюфункцииlist():
In[2]:list1=list('router')
In[3]:printlist1
['r','o','u','t','e','r']
Генераторысписков:
In[4]:list2=['FastEthernet0/'+str(i)foriinrange(10)]
In[5]:list2
Out[6]:
['FastEthernet0/0',
'FastEthernet0/1',
'FastEthernet0/2',
'FastEthernet0/3',
'FastEthernet0/4',
'FastEthernet0/5',
'FastEthernet0/6',
'FastEthernet0/7',
'FastEthernet0/8',
'FastEthernet0/9']
Вариантысозданиясписка
71
Словарь(Dictionary)Словари-этоизменяемый,неупорядоченныйтипданных(кслову,вмодулеcollectionsдоступныупорядоченныеобъекты,внешнеидентичныесловарямOrderedDict).
Словарь(ассоциативныймассив,хеш-таблица):
данныевсловареэтопарыключ:значениедоступкзначениямосуществляетсяпоключу,анепономеру,каквспискахсловаринеупорядоченны,поэтомунестоитполагатьсянапорядокэлементовсловарятаккаксловариизменяемы,тоэлементысловаряможноменять,добавлять,удалятьключдолженбытьобъектомнеизменяемоготипа:
числострокакортеж
значениеможетбытьданнымилюбоготипа
Примерсловаря:
london={'name':'London1','location':'LondonStr','vendor':'Cisco','model':'44
51','IOS':'15.4'}
Можнозаписыватьитак:
london={
'id':1,
'name':'London',
'IT_VLAN':320,
'User_VLAN':1010,
'Mngmt_VLAN':99,
'to_name':None,
'to_id':None,
'port':'G1/0/11'
}
Длятого,чтобыполучитьзначениеизсловаря,надообратитьсяпоключу,такимжеобразом,какэтобыловсписках,тольковместономера,будетиспользоватьсяключ:
Словарь(Dictionary)
72
In[1]:london={'name':'London1','location':'LondonStr'}
In[2]:london['name']
Out[2]:'London1'
In[3]:london['location']
Out[3]:'LondonStr'
Аналогичнымобразомможнодобавитьновуюпаруключ:значение:
In[4]:london['vendor']='Cisco'
In[5]:printlondon
{'vendor':'Cisco','name':'London1','location':'LondonStr'}
Всловаревкачествезначенияможноиспользоватьсловарь:
london_co={
'r1':{
'hostname':'london_r1',
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'4451',
'IOS':'15.4',
'IP':'10.255.0.1'
},
'r2':{
'hostname':'london_r2',
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'4451',
'IOS':'15.4',
'IP':'10.255.0.2'
},
'sw1':{
'hostname':'london_sw1',
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'3850',
'IOS':'3.6.XE',
'IP':'10.255.0.101'
}
}
Получитьзначенияизвложенногословаряможнотак:
Словарь(Dictionary)
73
In[7]:london_co['r1']['IOS']
Out[7]:'15.4'
In[8]:london_co['r1']['model']
Out[8]:'4451'
In[9]:london_co['sw1']['IP']
Out[9]:'10.255.0.101'
Словарь(Dictionary)
74
Полезныеметодыдляработысословарями
clear()
Методclear()позволяеточиститьсловарь:
In[1]:london={'name':'London1','location':'LondonStr','vendor':'Cisco','mod
el':'4451','IOS':'15.4'}
In[2]:london.clear()
In[3]:london
Out[3]:{}
copy()
Методcopy()позволяетсоздатьполнуюкопиюсловаря.
Еслиуказать,чтоодинсловарьравендругому:
In[4]:london={'name':'London1','location':'LondonStr','vendor':'Cisco'}
In[5]:london2=london
In[6]:id(london)
Out[6]:25489072
In[7]:id(london2)
Out[7]:25489072
In[8]:london['vendor']='Juniper'
In[9]:london2['vendor']
Out[9]:'Juniper'
Вэтомслучаеlondon2этоещеодноимя,котороессылаетсянасловарь.И,приизмененияхсловаряlondon,меняетсяисловарьlondon2,таккакэтоссылкинаодинитотжеобъект.
Поэтому,еслинужносделатькопиюсловаря,надоиспользоватьметодcopy():
Полезныеметодыдляработысословарями
75
In[10]:london={'name':'London1','location':'LondonStr','vendor':'Cisco'}
In[11]:london2=london.copy()
In[12]:id(london)
Out[12]:25524512
In[13]:id(london2)
Out[13]:25563296
In[14]:london['vendor']='Juniper'
In[15]:london2['vendor']
Out[15]:'Cisco'
get()
Еслиприобращенииксловарюуказываетсяключ,которогонетвсловаре,возникаетошибка:
In[16]:london={'name':'London1','location':'LondonStr','vendor':'Cisco'}
In[17]:london['IOS']
---------------------------------------------------------------------------
KeyErrorTraceback(mostrecentcalllast)
<ipython-input-17-b4fae8480b21>in<module>()
---->1london['IOS']
KeyError:'IOS'
Методget()запрашиваетключи,еслиегонет,вместоошибкивозвращаетNone.
In[18]:london={'name':'London1','location':'LondonStr','vendor':'Cisco'}
In[19]:printlondon.get('IOS')
None
Методget()позволяетуказыватьдругоезначение,вместоNone:
In[20]:printlondon.get('IOS','Ooops')
Ooops
keys(),values(),items()
Методыkeys(),values(),items():
Полезныеметодыдляработысословарями
76
In[24]:london={'name':'London1','location':'LondonStr','vendor':'Cisco'}
In[25]:london.keys()
Out[25]:['vendor','name','location']
In[26]:london.values()
Out[26]:['Cisco','London1','LondonStr']
In[27]:london.items()
Out[27]:[('vendor','Cisco'),('name','London1'),('location','LondonStr')]
del
Удалитьключизначение:
In[28]:london={'name':'London1','location':'LondonStr','vendor':'Cisco'}
In[29]:del(london['name'])
In[30]:london
Out[30]:{'location':'LondonStr','vendor':'Cisco'}
Полезныеметодыдляработысословарями
77
Вариантысозданиясловаря
Словарьможносоздатьспомощьюлитерала:
In[1]:r1={'model':'4451','IOS':'15.4'}
Конструкторdictпозволяетсоздаватьсловарьнесколькимиспособами.
Есливролиключейиспользуютсястроки,можноиспользоватьтакойвариантсозданиясловаря:
In[2]:r1=dict(model='4451',IOS='15.4')
In[3]:r1
Out[3]:{'IOS':'15.4','model':'4451'}
Второйвариантсозданиясловаряспомощьюdict:
In[4]:r1=dict([('model','4451'),('IOS','15.4')])
In[5]:r1
Out[5]:{'IOS':'15.4','model':'4451'}
Вситуации,когданадосоздатьсловарьсизвестнымиключами,но,покачто,пустымизначениями(илиодинаковымизначениями),оченьудобенметодfromkeys():
In[5]:d_keys=['hostname','location','vendor','model','IOS','IP']
In[6]:r1=dict.fromkeys(d_keys,None)
In[7]:r1
Out[7]:
{'IOS':None,
'IP':None,
'hostname':None,
'location':None,
'model':None,
'vendor':None}
Ипоследнийметодсозданиясловаря-генераторысловарей.Сгенерируемсловарьснулевымизначениями,каквпредыдущемпримере:
Вариантысозданиясловаря
78
In[16]:d_keys=['hostname','location','vendor','model','IOS','IP']
In[17]:d={x:Noneforxind_keys}
In[18]:d
Out[18]:
{'IOS':None,
'IP':None,
'hostname':None,
'location':None,
'model':None,
'vendor':None}
Вариантысозданиясловаря
79
Словарьиздвухсписков(advanced)
Слово'advanced'взаголовке,означает,чтото,чторассматриваетсявэтомразделеещёнебыловтемахкурса.Но,вдальнейшем,такойприемможетпригодится.
Вразделевариантысозданиясловарярассматривалсятакойвариантсозданиясловаря:
In[4]:r1=dict([('model','4451'),('IOS','15.4')])
In[5]:r1
Out[5]:{'IOS':'15.4','model':'4451'}
Какправило,такойспособсозданияиспользуетсяневтомслучае,когдасловарьсоздаетсявручнуювтекстовомфайле,акогдасловарьсоздаетсяизкаких-тодругихпромежуточныхобъектов.
Воспользуемсятакимспособомсозданиясловаря,чтобыобъединитьдваспискаодинаковойдлинывсловарь.
Дляэтоговоспользуемсяфункциейzip(),котораявозвращаетсписоккортежей:
In[1]:a=[1,2,3]
In[2]:b=[100,200,300]
In[3]:zip(a,b)
Out[3]:[(1,100),(2,200),(3,300)]
Теперьнаболееполезномпримере:
Вариантысозданиясловаря
80
In[4]:d_keys=['hostname','location','vendor','model','IOS','IP']
In[5]:d_values=['london_r1','21NewGlobeWalk','Cisco','4451','15.4','10.255
.0.1']
In[6]:zip(d_keys,d_values)
Out[6]:
[('hostname','london_r1'),
('location','21NewGlobeWalk'),
('vendor','Cisco'),
('model','4451'),
('IOS','15.4'),
('IP','10.255.0.1')]
In[7]:dict(zip(d_keys,d_values))
Out[7]:
{'IOS':'15.4',
'IP':'10.255.0.1',
'hostname':'london_r1',
'location':'21NewGlobeWalk',
'model':'4451',
'vendor':'Cisco'}
In[8]:r1=dict(zip(d_keys,d_values))
In[9]:r1
Out[9]:
{'IOS':'15.4',
'IP':'10.255.0.1',
'hostname':'london_r1',
'location':'21NewGlobeWalk',
'model':'4451',
'vendor':'Cisco'}
Впримеренижеестьотдельныйсписок,вкоторомхранятсяключи,исловарь,вкоторомхранитсяввидесписка(чтобысохранитьпорядок)информацияокаждомустройстве.
Соберемихвсловарьсключамиизспискаиинформациейизсловаряdata:
Вариантысозданиясловаря
81
In[10]:d_keys=['hostname','location','vendor','model','IOS','IP']
In[11]:data={
....:'r1':['london_r1','21NewGlobeWalk','Cisco','4451','15.4','10.255.0.1'
],
....:'r2':['london_r2','21NewGlobeWalk','Cisco','4451','15.4','10.255.0.2'
],
....:'sw1':['london_sw1','21NewGlobeWalk','Cisco','3850','3.6.XE','10.255
.0.101']
....:}
In[12]:london_co={}
In[13]:forkindata.keys():
....:london_co[k]=dict(zip(d_keys,data[k]))
....:
In[14]:london_co
Out[14]:
{'r1':{'IOS':'15.4',
'IP':'10.255.0.1',
'hostname':'london_r1',
'location':'21NewGlobeWalk',
'model':'4451',
'vendor':'Cisco'},
'r2':{'IOS':'15.4',
'IP':'10.255.0.2',
'hostname':'london_r2',
'location':'21NewGlobeWalk',
'model':'4451',
'vendor':'Cisco'},
'sw1':{'IOS':'3.6.XE',
'IP':'10.255.0.101',
'hostname':'london_sw1',
'location':'21NewGlobeWalk',
'model':'3850',
'vendor':'Cisco'}}
Вариантысозданиясловаря
82
Кортеж(Tuple)Кортежэтонеизменяемыйупорядоченныйтипданных.
КортежвPython-этопоследовательностьэлементов,которыеразделенымеждусобойзапятойизаключенывскобки.
Грубоговоря,кортеж-этосписок,которыйнельзяизменить.Тоесть,вкортежеестьтолькоправаначтение.Этоможетбытьзащитойотслучайныхизменений.
Создатьпустойкортеж:
In[1]:tuple1=tuple()
In[2]:printtuple1
()
Кортежизодногоэлемента(обратитевниманиеназапятую):
In[3]:tuple2=('password',)
Кортежизсписка:
In[4]:list_keys=['hostname','location','vendor','model','IOS','IP']
In[5]:tuple_keys=tuple(list_keys)
In[6]:tuple_keys
Out[6]:('hostname','location','vendor','model','IOS','IP')
Кобъектамвкортежеможнообращатьсякакикобъектамсписка,попорядковомуномеру:
In[7]:tuple_keys[0]
Out[7]:'hostname'
Нотаккаккортежнеизменяем,присвоитьновоезначениенельзя:
Кортеж(Tuple)
83
In[8]:tuple_keys[1]='test'
---------------------------------------------------------------------------
TypeErrorTraceback(mostrecentcalllast)
<ipython-input-9-1c7162cdefa3>in<module>()
---->1tuple_keys[1]='test'
TypeError:'tuple'objectdoesnotsupportitemassignment
Кортеж(Tuple)
84
Множество(Set)Множество-этоизменяемыйнеупорядоченныйтипданных.Вмножествевсегдасодержатсятолькоуникальныеэлементы.
МножествовPython-этопоследовательностьэлементов,которыеразделенымеждусобойзапятойизаключенывфигурныескобки.
Спомощьюмножестваможнолегкоубратьповторяющиесяэлементы:
In[1]:vlans=[10,20,30,40,100,10]
In[2]:set(vlans)
Out[2]:{10,20,30,40,100}
In[3]:set1=set(vlans)
In[4]:printset1
set([40,100,10,20,30])
Множество(Set)
85
Полезныеметодыдляработысмножествами
add()
Методadd()добавляетэлементвомножество:
In[1]:set1={10,20,30,40}
In[2]:set1.add(50)
In[3]:set1
Out[3]:{10,20,30,40,50}
discard()
Методdiscard()позволяетудалятьэлементы,невыдаваяошибку,еслиэлементавмножественет:
In[3]:set1
Out[3]:{10,20,30,40,50}
In[4]:set1.discard(55)
In[5]:set1
Out[5]:{10,20,30,40,50}
In[6]:set1.discard(50)
In[7]:set1
Out[7]:{10,20,30,40}
clear()
Методclear()очищаетмножество:
In[8]:set1={10,20,30,40}
In[9]:set1.clear()
In[10]:set1
Out[10]:set()
Полезныеметодыдляработысмножествами
86
Полезныеметодыдляработысмножествами
87
Операциисмножествами
Множестваполезнытем,чтоснимиможноделатьразличныеоперацииинаходитьобъединениемножеств,пересечениеитакдалее.
Объединениемножествможнополучитьспомощьюметодаunion()илиоператора|:
In[1]:vlans1={10,20,30,50,100}
In[2]:vlans2={100,101,102,102,200}
In[3]:vlans1.union(vlans2)
Out[3]:{10,20,30,50,100,101,102,200}
In[4]:vlans1|vlans2
Out[4]:{10,20,30,50,100,101,102,200}
Пересечениемножествможнополучитьспомощьюметодаintersection()илиоператора&:
In[5]:vlans1={10,20,30,50,100}
In[6]:vlans2={100,101,102,102,200}
In[7]:vlans1.intersection(vlans2)
Out[7]:{100}
In[8]:vlans1&vlans2
Out[8]:{100}
Операциисмножествами
88
Вариантысозданиямножества
Нельзясоздатьпустоемножествоспомощьюлитерала(таккаквтакомслучаеэтобудетнемножество,асловарь):
In[1]:set1={}
In[2]:type(set1)
Out[2]:dict
Нопустоемножествоможносоздатьтакимобразом:
In[3]:set2=set()
In[4]:type(set2)
Out[4]:set
Множествоизстроки:
In[5]:set('longlonglonglongstring')
Out[5]:{'','g','i','l','n','o','r','s','t'}
Множествоизсписка:
In[6]:set([10,20,30,10,10,30])
Out[6]:{10,20,30}
Генератормножеств:
In[7]:set2={i+100foriinrange(10)}
In[8]:set2
Out[8]:{100,101,102,103,104,105,106,107,108,109}
In[9]:printset2
set([100,101,102,103,104,105,106,107,108,109])
Вариантысозданиямножества
89
ПреобразованиетиповВPythonестьнесколькополезныхвстроенныхфункций,которыепозволяютпреобразоватьданныеизодноготипавдругой.
int()
int()-преобразуетстрокувint:
In[1]:int("10")
Out[1]:10
Спомощьюфункцииintможнопреобразоватьичисловдвоичнойзаписивдесятичную(двоичнаязаписьдолжнабытьввидестроки)
In[2]:int("11111111",2)
Out[2]:255
bin()
Преобразоватьдесятичноечисловдвоичныйформатможноспомощьюbin():
In[3]:bin(10)
Out[3]:'0b1010'
In[4]:bin(255)
Out[4]:'0b11111111'
hex()
Аналогичнаяфункцияестьидляпреобразованиявшестнадцатиричныйформат:
In[5]:hex(10)
Out[5]:'0xa'
In[6]:hex(255)
Out[6]:'0xff'
list()
Преобразованиетипов
90
Функцияlist()преобразуетаргументвсписок:
In[7]:list("string")
Out[7]:['s','t','r','i','n','g']
In[8]:list({1,2,3})
Out[8]:[1,2,3]
In[9]:list((1,2,3,4))
Out[9]:[1,2,3,4]
set()
Функцияset()преобразуетаргументвмножество:
In[10]:set([1,2,3,3,4,4,4,4])
Out[10]:{1,2,3,4}
In[11]:set((1,2,3,3,4,4,4,4))
Out[11]:{1,2,3,4}
In[12]:set("stringstring")
Out[12]:{'','g','i','n','r','s','t'}
Этафункцияоченьполезна,когданужнополучитьуникальныеэлементывпоследовательности.
tuple()
Функцияtuple()преобразуетаргументвкортеж:
In[13]:tuple([1,2,3,4])
Out[13]:(1,2,3,4)
In[14]:tuple({1,2,3,4})
Out[14]:(1,2,3,4)
In[15]:tuple("string")
Out[15]:('s','t','r','i','n','g')
Этоможетпригодитсявтомслучае,еслинужнополучитьнеизменяемыйобъект.
str()
Функцияstr()преобразуетаргументвстроку:
Преобразованиетипов
91
In[16]:str(10)
Out[16]:'10'
Например,онапригодитсявситуации,когдаестьсписокVLANов,которыйнадопреобразоватьводнустроку,гденомераперечисленычереззапятую.
Еслисделатьjoinдляспискачисел,возникнетошибка:
In[17]:vlans=[10,20,30,40]
In[18]:','.join(vlans)
------------------------------------------------------
TypeErrorTraceback(mostrecentcalllast)
<ipython-input-39-d705aed3f1b3>in<module>()
---->1','.join(vlans)
TypeError:sequenceitem0:expectedstring,intfound
Чтобыисправитьэто,нужнопреобразоватьчиславстроки.Этоудобноделатьспомощьюlistcomprehensions:
In[19]:','.join([str(vlan)forvlaninvlans])
Out[19]:'10,20,30,40'
Преобразованиетипов
92
Проверкатипов
Припреобразованиитиповданных,могутвозникнутьошибкитакогорода:
In[1]:int('a')
------------------------------------------------------
ValueErrorTraceback(mostrecentcalllast)
<ipython-input-42-b3c3f4515dd4>in<module>()
---->1int('a')
ValueError:invalidliteralforint()withbase10:'a'
Ошибкаабсолютнологичная.Мыпытаемсяпреобразоватьвдесятичныйформатстроку'a'.
Иеслитутпримервыглядит,возможно,глупым,темнеменее,когданужно,например,пройтисьпоспискустрокипреобразоватьвчислатеизних,которыесодержатчисла,можнополучитьтакуюошибку.
Чтобыизбежатьеё,былобыхорошоиметьвозможностьпроверитьсчеммыработаем.
isdigit()
ВPythonтакиеметодыесть.Например,чтобыпроверитьсостоитлистрокаизоднихцифр,можноиспользоватьметодisdigit():
In[2]:"a".isdigit()
Out[2]:False
In[3]:"a10".isdigit()
Out[3]:False
In[4]:"10".isdigit()
Out[4]:True
Примериспользованияметода:
In[5]:vlans=['10','20','30','40','100-200']
In[6]:[int(vlan)forvlaninvlansifvlan.isdigit()]
Out[6]:[10,20,30,40]
isalpha()
Проверкатипов
93
Методisalpha()позволяетпроверитьсостоитлистрокаизоднихбукв:
In[7]:"a".isalpha()
Out[7]:True
In[8]:"a100".isalpha()
Out[8]:False
In[9]:"a--".isalpha()
Out[9]:False
In[10]:"a".isalpha()
Out[10]:False
isalnum()
Методisalnum()позволяетпроверитьсостоитлистрокаизбуквицифр:
In[11]:"a".isalnum()
Out[1]:True
In[12]:"a10".isalnum()
Out[12]:True
type()
Иногда,взависимостиотрезультата,библиотекаилифункцияможетвыводитьразныетипыобъектов.Например,еслиобъектодин,возращаетсястрока,еслинесколько,товозвращаетсякортеж.
Намженадопостроитьходпрограммыпо-разному,взависимостиоттого,былаливозвращенастрокаиликортеж.
Вэтомможетпомочьфункцияtype():
In[13]:type("string")
Out[13]:str
In[14]:type("string")isstr
Out[14]:True
Аналогичноскортежем(идругимитипамиданных):
Проверкатипов
94
In[15]:type((1,2,3))
Out[15]:tuple
In[16]:type((1,2,3))istuple
Out[16]:True
In[17]:type((1,2,3))islist
Out[17]:False
Проверкатипов
95
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание3.1
Обработатьстрокуospf_routeивывестиинформациюввиде:
Protocol:OSPF
Prefix:10.0.24.0/24
AD/Metric:110/41
Next-Hop:10.0.13.3
Lastupdate:3d18h
OutboundInterface:FastEthernet0/0
ВразделеФорматированиестрокдобавилисьпримеры,которыепомогутсотображениемвыводастолбцами.
ospf_route="O10.0.24.0/24[110/41]via10.0.13.3,3d18h,FastEthernet0/0"
Задание3.2
ПреобразоватьстрокуMACсформатаXXXX:XXXX:XXXXвXXXX.XXXX.XXXX
MAC="AAAA:BBBB:CCCC"
Задания
96
Задание3.3
ПолучитьизстрокиCONFIGсписокVLANвида['1','3','10','20','30','100'].
CONFIG="switchporttrunkallowedvlan1,3,10,20,30,100"
Задание3.4
Изстрокcommand1иcommand2получитьсписокVLAN,которыеестьивкомандеcommand1ивкомандеcommand2.Неиспользоватьдлярешениязадачициклы,операторif.
Дляданногопримера,результатомдолженбытьсписок:[1,3,100]
command1="switchporttrunkallowedvlan1,3,10,20,30,100"
command2="switchporttrunkallowedvlan1,3,100,200,300"
Задание3.5
СписокVLANS-этосписокVLANов,собранныхсовсехустройствсети,поэтомувспискеестьповторяющиесяномераVLAN.
ИзсписканужнополучитьуникальныйсписокVLANов,отсортированныйповозрастаниюномеров.
Неиспользоватьдлярешениязадачициклы,операторif.
VLANS=[10,20,30,1,2,100,10,30,3,4,10]
Задание3.6
ОбработатьстрокуNATтакимобразом,чтобывимениинтерфейсавместоFastEthernetбылоGigabitEthernet.
NAT="ipnatinsidesourcelistACLinterfaceFastEthernet0/1overload"
Задание3.7
ПреобразоватьMAC-адресвдвоичнуюстроку(бездвоеточий).
MAC="AAAA:BBBB:CCCC"
Задания
97
Задание3.8
ПреобразоватьIP-адрес(переменнаяIP)вдвоичныйформативывестивыводстолбцами,такимобразом:
первойстрокойдолжныидтидесятичныезначениябайтоввторойстрокойдвоичныезначения
Выводдолженбытьупорядочентакже,каквпримере:
столбцамиширинастолбца10символов
Примервывода:
10111
00001010000000010000000100000001
IP='192.168.3.1'
Задание3.9
Найтииндекспоследнеговхожденияэлементасконца.
Например,дляспискаnum_list,индекспоследнеговхожденияэлемента10-4;дляспискаword_list,индекспоследнеговхожденияэлемента'ruby'-6.
Сделатьрешениеобщим(тоесть,непривязыватьсякконкретномуэлементу)ипроверитьнаразныхспискахиэлементах.
Неиспользоватьдлярешенияциклы(for,while)иусловия(if/else).
Подсказка:функцияlen()возвращаетдлинусписка.
num_list=[10,2,30,100,10,50,11,30,15,7]
word_list=['python','ruby','perl','ruby','perl','python','ruby','perl']
Задания
98
СозданиебазовыхскриптовЕслиговоритьвцелом,тоскрипт-этообычныйфайл.Вэтомфайлехранитсяпоследовательностькоманд,которыенеобходимовыполнить.
Начнемсбазовогоскрипта.Выведемнастандартныйпотоквыводанесколькострок.
Дляэтогонадосоздатьфайлaccess_template.pyстакимсодержимым:
access_template=['switchportmodeaccess',
'switchportaccessvlan%d',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
print'\n'.join(access_template)%5
Сначалаэлементыспискаобъединяютсявстроку,котораяразделенасимволом\n,австрокуподставляетсяномерVLAN,используяформатированиестрок.
Послеэтого,надосохранитьфайлиперейтивкоманднуюстроку.
Таквыглядитвыполнениескрипта:
$pythonaccess_template.py
switchportmodeaccess
switchportaccessvlan5
switchportnonegotiate
spanning-treeportfast
spanning-treebpduguardenable
Ставитьрасширение.pyуфайланеобязательно.
Но,есливыиспользуетеwindows,тоэтожелательноделать,таккакWindowsиспользуетрасширениефайла,дляопределениятогокакобрабатыватьфайл.
Вкурсевсескрипты,которыебудутсоздаваться,используютрасширение.py.Можносказать,чтоэто"хорошийтон",создаватьскриптыPythonстакимрасширением.
Кодировка
4.Созданиебазовыхскриптов
99
Теперьпопробуемвывеститекст,набранныйкириллицей,настандартныйпотоквывода(файлaccess_template_encoding.py):
access_template=['switchportmodeaccess',
'switchportaccessvlan%d',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
print"Конфигурацияинтерфейсаврежимеaccess:"
print'\n'.join(access_template)%5
Припопыткезапуститьскриптполучаемтакуюошибку:
$pythonaccess_template_encoding.py
File"access_template_encoding.py",line7
SyntaxError:Non-ASCIIcharacter'\xd0'infileaccess_template_encoding.pyonline7,
butnoencodingdeclared;seehttp://python.org/dev/peps/pep-0263/fordetails
Длятогочтобынебылотакойошибки,необходимодобавитьвначалефайлатакуюстроку:
#-*-coding:utf-8-*-
Тогдаскриптaccess_template_encoding.pyбудетвыглядетьтак:
#-*-coding:utf-8-*-
access_template=['switchportmodeaccess',
'switchportaccessvlan%d',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
print"Конфигурацияинтерфейсаврежимеaccess:"
print'\n'.join(access_template)%5
Теперьошибкинет:
4.Созданиебазовыхскриптов
100
$pythonaccess_template_encoding.py
Конфигурацияинтерфейсаврежимеaccess:
switchportmodeaccess
switchportaccessvlan5
switchportnonegotiate
spanning-treeportfast
spanning-treebpduguardenable
Исполняемыйфайл
Длятого,чтобыфайлбылисполняемыминенужнобылокаждыйразписатьpythonпередвызовомфайла,нужно:
сделатьфайлисполняемым(дляlinux)впервойстрокефайладолжнанаходитсястрока#!/usr/bin/envpython
Примерфайлаaccess_template_exec.py:
#!/usr/bin/envpython
access_template=['switchportmodeaccess',
'switchportaccessvlan%d',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
print'\n'.join(access_template)%5
Послеэтого:
chmod+xaccess_template_exec.py
Теперьможновызыватьфайлтакимобразом:
$./access_template_exec.py
4.Созданиебазовыхскриптов
101
Передачааргументовскрипту(argv)Оченьчастоскриптрешаеткакую-тообщуюзадачу.Например,скриптобрабатываеткак-тофайлконфигурации.Конечно,втакомслучае,нехочетсякаждыйразрукамивскриптеправитьназваниефайла.
Гораздолучшебудетпередаватьимяфайлакакаргументскриптаизатемиспользоватьужеуказанныйфайл.
ВPython,вмодулеsysестьоченьпростойиудобныйспособдляработысаргументами-argv.
Посмотримнапример(файлaccess_template_argv.py):
fromsysimportargv
interface,vlan=argv[1:]
access_template=['switchportmodeaccess',
'switchportaccessvlan%d',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
print'interface%s'%interface
print'\n'.join(access_template)%int(vlan)
Проверяемработускрипта:
$pythonaccess_template_argv.pyGi0/74
interfaceGi0/7
switchportmodeaccess
switchportaccessvlan4
switchportnonegotiate
spanning-treeportfast
spanning-treebpduguardenable
Аргументы,которыебылипереданыскрипту,подставляютсякакзначениявшаблон.
Тутнадопояснитьнесколькомоментов:
argvэтосписокargvсодержитнетолькоаргументы,которыепередалискрипту,ноиназваниесамогоскрипта
Передачааргументовскрипту
102
Вданномслучае,вспискеargvнаходятсятакиеэлементы:
['access_template_argv.py','Gi0/7','4']
Сначалаидетимясамогоскрипта,затемаргументы,втомжепорядке.
Ещёодинмомент,которыйможетбытьнеоченьпонятным:
interface,vlan=argv[1:]
Выражениеargv[1:]должнобытьзнакомым.Этосрезсписка.Тоесть,вправойсторонеостаетсясписок,сдвумяэлементами:['Gi0/7','4'].
Разберемсясдвойнымприсваиванием.
ВPythonестьвозможностьзаразприсвоитьзначениянесколькимпеременным.Простойпример:
In[16]:a=5
In[17]:b=6
In[18]:c,d=5,6
In[19]:c
Out[19]:5
In[20]:d
Out[20]:6
Есливместочиселсписок,каквслучаесargv:
In[21]:arg=['Gi0/7','4']
In[22]:interface,vlan=arg
In[23]:interface
Out[23]:'Gi0/7'
In[24]:vlan
Out[24]:'4'
Обратитевнимание,чтоargvвсегдавозвращаетаргументыввидестроки.А,таккаквспискеaccess_templateвсинтаксисеформатированиястрок,указаночисло,переменнуюvlanнужнопреобразоватьвчисло(последняястрока).
Передачааргументовскрипту
103
ВводинформациипользователемИногда,необходимополучитьинформациюотпользователя.Попробуемсделатьтак,чтобыскриптзадавалвопросыпользователюизатемиспользовалэтотответ.
Вводотпользователяможетпонадобиться,например,длятого,чтобыввестипароль.
Дляполученияинформацииотпользователяиспользуетсяфункцияraw_input():
In[1]:printraw_input('Твойлюбимыйпротоколмаршрутизации?')
Твойлюбимыйпротоколмаршрутизации?OSPF
OSPF
Вданномслучае,информацияпростотутжевыводитсяпользователю,нокромеэтого,информация,которуюввелпользовательможетбытьсохраненавкакую-топеременную.Иможетиспользоватьсядалеевскрипте.
In[2]:protocol=raw_input('Твойлюбимыйпротоколмаршрутизации?')
Твойлюбимыйпротоколмаршрутизации?OSPF
In[3]:printprotocol
OSPF
Вскобкахобычнопишетсякакой-товопрос,которыйуточняет,какуюинформациюнужноввести.
Текствскобках,впринципе,писатьнеобязательно.Иможносделатьтакойжевыводспомощьюоператораprint:
In[4]:print'Твойлюбимыйпротоколмаршрутизации?'
Твойлюбимыйпротоколмаршрутизации?
In[5]:protocol=raw_input()
OSPF
In[6]:printprotocol
OSPF
Но,какправило,нагляднееписатьтекствсамойфункцииraw_print().
Запрашиваниеинформацииизскрипта(файлaccess_template_raw_input.py):
Вводинформациипользователем
104
interface=raw_input('Enterinterfacetypeandnumber:')
vlan=int(raw_input('EnterVLANnumber:'))
access_template=['switchportmodeaccess',
'switchportaccessvlan%d',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
print'\n'+'-'*30
print'interface%s'%interface
print'\n'.join(access_template)%vlan
Впервыхдвухстроках,запрашиваетсяинформацияупользователя.Функцияraw_input(),какиargvвозвращаетданныеввидестрок.Поэтому,параметрvlanпреобразуемвчисло.
Ещепоявиласьстрокаprint'\n'+'-'*30.Онаиспользуетсяпростодлятого,чтобыотделитьзапросинформацииотвывода.
Выполняемскрипт:
$pythonaccess_template_raw_input.py
Enterinterfacetypeandnumber:Gi0/3
EnterVLANnumber:55
------------------------------
interfaceGi0/3
switchportmodeaccess
switchportaccessvlan55
switchportnonegotiate
spanning-treeportfast
spanning-treebpduguardenable
Разницамеждуфункциямиraw_input()иinput()хорошоописананаstackoverflow
Вводинформациипользователем
105
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание4.1
ЗапроситьупользователявводIP-сетивформате:10.1.1.0/24
Затемвывестиинформациюосетиимаскевтакомформате:
Network:
10110
00001010000000010000000100000000
Mask:
/24
2552552550
11111111111111111111111100000000
Проверитьработускриптанаразныхкомбинацияхсеть/маска.
Задание4.1a
Всё,каквзадании4.1.Но,еслипользовательввеладресхоста,анеадрессети,тонадоадресхостапреобразоватьвадрессетиивывестиадрессетиимаску,каквзадании4.1.
Задания
106
Примерадресасети(всебитыхостовойчастиравнынулю):
10.0.1.0/24190.1.0.0/16
Примерадресахоста:
10.0.1.1/24-хостизсети10.0.1.0/2410.0.5.1/30-хостизсети10.0.5.0/30
Network:
10110
00001010000000010000000100000000
Mask:
/24
2552552550
11111111111111111111111100000000
Проверитьработускриптанаразныхкомбинацияхсеть/маска.
Задание4.1b
Преобразоватьскриптиззадания4.1aтакимобразом,чтобысеть/масканезапрашивалисьупользователя,апередавалиськакаргументскрипту.
Задание4.2
Вэтойзадаченельзяиспользоватьусловиеifинельзяизменятьсловарьlondon_co.
Взаданиисоздансловарьсинформациейоразныхустройствах.
Вамнужнозапроситьупользователявводимениустройства(r1,r2илиsw1).Ивывестиинформациюосоответствующемустройственастандартныйпотоквывода(информациябудетввидесловаря).
Примервыполненияскрипта:
$pythontask_4_2.py
Enterdevicename:r1
{'ios':'15.4','model':'4451','vendor':'Cisco','location':'21NewGlobeWalk','
ip':'10.255.0.1'}
Задания
107
london_co={
'r1':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'4451',
'ios':'15.4',
'ip':'10.255.0.1'
},
'r2':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'4451',
'ios':'15.4',
'ip':'10.255.0.2'
},
'sw1':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'3850',
'ios':'3.6.XE',
'ip':'10.255.0.101',
'vlans':'10,20,30',
'routing':True
}
}
Задание4.2a
Вэтойзадаченельзяиспользоватьусловиеifинельзяизменятьсловарьlondon_co.
Переделатьскриптиззадания4.2такимобразом,чтобы,кромеимениустройства,запрашивалсятакжепараметрустройства,которыйнужноотобразить.
Вывестиинформациюосоответствующемпараметре,указанногоустройства.
Примервыполненияскрипта:
$pythontask_4_2a.py
Enterdevicename:r1
Enterparametername:ios
15.4
Задания
108
london_co={
'r1':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'4451',
'ios':'15.4',
'ip':'10.255.0.1'
},
'r2':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'4451',
'ios':'15.4',
'ip':'10.255.0.2'
},
'sw1':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'3850',
'ios':'3.6.XE',
'ip':'10.255.0.101',
'vlans':'10,20,30',
'routing':True
}
}
Задание4.2b
Вэтойзадаченельзяиспользоватьусловиеifинельзяизменятьсловарьlondon_co.
Переделатьскриптиззадания4.2aтакимобразом,чтобы,призапросепараметра,отображалсясписоквозможныхпараметров.
Вывестиинформациюосоответствующемпараметре,указанногоустройства.
Примервыполненияскрипта:
$pythontask_4_2b.py
Enterdevicename:r1
Enterparametername(ios,model,vendor,location,ip):ip
10.255.0.1
Задания
109
london_co={
'r1':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'4451',
'ios':'15.4',
'ip':'10.255.0.1'
},
'r2':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'4451',
'ios':'15.4',
'ip':'10.255.0.2'
},
'sw1':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'3850',
'ios':'3.6.XE',
'ip':'10.255.0.101',
'vlans':'10,20,30',
'routing':True
}
}
Задание4.2c
Вэтойзадаченельзяиспользоватьусловиеifинельзяизменятьсловарьlondon_co.
Переделатьскриптиззадания4.2bтакимобразом,чтобы,призапросепараметра,которогонетвсловареустройства,отображалосьсообщение'Такогопараметранет'.
Попробуйтенабратьнеправильноеимепараметраилинесуществующийпараметр,чтобыувидетькакойбудетрезультат.Азатемвыполняйтезадание.
Есливыбрансуществующийпараметр,вывестиинформациюосоответствующемпараметре,указанногоустройства.
Примервыполненияскрипта:
$pythontask_4_2c.py
Enterdevicename:r1
Enterparametername(ios,model,vendor,location,ip):io
Такогопараметранет
Задания
110
london_co={
'r1':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'4451',
'ios':'15.4',
'ip':'10.255.0.1'
},
'r2':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'4451',
'ios':'15.4',
'ip':'10.255.0.2'
},
'sw1':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'3850',
'ios':'3.6.XE',
'ip':'10.255.0.101',
'vlans':'10,20,30',
'routing':True
}
}
Задание4.2d
Вэтойзадаченельзяиспользоватьусловиеifинельзяизменятьсловарьlondon_co.
Переделатьскриптиззадания4.2cтакимобразом,чтобы,призапросепараметра,пользовательмогвводитьназваниепараметравлюбомрегистре.
Примервыполненияскрипта:
$pythontask_4_2d.py
Enterdevicename:r1
Enterparametername(ios,model,vendor,location,ip):IOS
15.4
Задания
111
london_co={
'r1':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'4451',
'ios':'15.4',
'ip':'10.255.0.1'
},
'r2':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'4451',
'ios':'15.4',
'ip':'10.255.0.2'
},
'sw1':{
'location':'21NewGlobeWalk',
'vendor':'Cisco',
'model':'3850',
'ios':'3.6.XE',
'ip':'10.255.0.101',
'vlans':'10,20,30',
'routing':True
}
}
Задание4.3
(Задачанаосновепримероввразделе)
Вэтойзадаченельзяиспользоватьусловиеif.
Скриптдолжензапрашиватьупользователя:
информациюорежимеинтерфейса(access/trunk),примертекстазапроса:'Enterinterfacemode(access/trunk):'
номереинтерфейса(типиномер,видаGi0/3)примертекстазапроса:'Enterinterfacetypeandnumber:'
номерVLANа(длярежимаtrunkбудетвводитьсясписокVLANов)примертекстазапроса:'Entervlan(s):'
Взависимостиотвыбранногорежима,настандартныйпотоквывода,должнавозвращатьсясоответствующаяконфигурацияaccessилиtrunk(шаблоныкоманднаходятсявспискахaccess_templateиtrunk_template).
Приэтом,сначаладолжнаидтистрокаinterfaceиподставленномеринтерфейса,азатемсоответствующийшаблон,вкоторыйподставленномерVLANа(илисписокVLANов).
Задания
112
Нижепримерывыполненияскрипта,чтобыбылопрощепонятьзадачу.
Примервыполненияскрипта,привыборережимаaccess:
$pythontask_4_3.py
Enterinterfacemode(access/trunk):access
Enterinterfacetypeandnumber:Fa0/6
Entervlan(s):3
interfaceFa0/6
switchportmodeaccess
switchportaccessvlan3
switchportnonegotiate
spanning-treeportfast
spanning-treebpduguardenable
Примервыполненияскрипта,привыборережимаtrunk:
$pythontask_4_3.py
Enterinterfacemode(access/trunk):trunk
Enterinterfacetypeandnumber:Fa0/7
Entervlan(s):2,3,4,5
interfaceFa0/7
switchporttrunkencapsulationdot1q
switchportmodetrunk
switchporttrunkallowedvlan2,3,4,5
Начальноесодержимоескрипта:
access_template=['switchportmodeaccess',
'switchportaccessvlan%s',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
trunk_template=['switchporttrunkencapsulationdot1q',
'switchportmodetrunk',
'switchporttrunkallowedvlan%s']
Задание4.3a
Вэтойзадаченельзяиспользоватьусловиеif.
Дополнитьскриптиззадания4.3такимобразом,чтобы,взависимостиотвыбранногорежима,задавалисьразныевопросывзапросеономереVLANаилиспискаVLANов:
Задания
113
дляaccess:'EnterVLANnumber:'дляtrunk:'EnterallowedVLANs:'
access_template=['switchportmodeaccess',
'switchportaccessvlan%s',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
trunk_template=['switchporttrunkencapsulationdot1q',
'switchportmodetrunk',
'switchporttrunkallowedvlan%s']
Задания
114
КонтрольходапрограммыВэтомразделерассматриваютсявозможностиPythonвуправленииходомпрограммы.
Какминимум,стоитразобратьсясконструкциями:
if/elif/else
цикломforцикломwhile
Остальныеразделыможнопрочестьпозже.
Разделпроконструкцииfor/elseиwhile/else,возможно,будетпрощепонять,еслипрочестьихпослеразделаобобработкеисключений.
5.Контрольходапрограммы
115
if/elif/elseКонструкцияif/elif/elseдаетвозможностьвыполнятьразличныедействиявзависимостиотусловий.
Вэтойконструкциитолькоifявляетсяобязательным,elifиelseопциональны:
Проверкаifвсегдаидетпервой.Послеоператораifдолжнобытькакое-тоусловие:еслиэтоусловиевыполняется(возвращаетTrue),тодействиявблокеifвыполняются.Спомощьюelifможносделатьнесколькоразветвлений.Тоесть,проверятьвходящиеданныенаразныеусловия.
блокelifэтототжеif,нотолькоследующаяпроверка.Грубоговоря,это"аесли..."блоковelifможетбытьмного
Блокelseвыполняетсявтомслучае,еслиниодноизусловийifилиelifнебылоистинным.
Примерконструкции:
In[1]:a=9
In[2]:ifa==0:
...:print'aравно0'
...:elifa<10:
...:print'aменьше10'
...:else:
...:print'aбольше10'
...:
aменьше10
Условиямипослеifилиelifмогутбыть,например,такиеконструкции:
if/elif/else
116
In[7]:5>3
Out[7]:True
In[8]:5==5
Out[8]:True
In[9]:'vlan'in'switchporttrunkallowedvlan10,20'
Out[9]:True
In[10]:1in[1,2,3]
Out[10]:True
In[11]:0in[1,2,3]
Out[11]:False
TrueиFalse
ВPython:
True(истина)любоененулевоечислолюбаянепустаястрокалюбойнепустойобъект
False(ложь)0Noneпустаястрокапустойобъект
ОстальныезначенияTrueилиFalse,какправило,логическиследуютизусловия.
Например,таккакпустойсписокэтоложь,проверитьпустойлисписокможнотакимобразом:
In[12]:list_to_test=[1,2,3]
In[13]:iflist_to_test:
....:print"Вспискеестьобъекты"
....:
Вспискеестьобъекты
Тотжерезультатможнобылобыполучитьтакимобразом:
if/elif/else
117
In[14]:iflen(list_to_test)!=0:
....:print"Вспискеестьобъекты"
....:
Вспискеестьобъекты
Операторысравнения
Операторысравнения,которыемогутиспользоватьсявусловиях:
In[3]:5>6
Out[3]:False
In[4]:5>2
Out[4]:True
In[5]:5<2
Out[5]:False
In[6]:5==2
Out[6]:False
In[7]:5==5
Out[7]:True
In[8]:5>=5
Out[8]:True
In[9]:5<=10
Out[9]:True
In[10]:8!=10
Out[10]:True
Обратитевнимание,чторавенствопроверяетсядвойным==.
Операторin
Операторinпозволяетвыполнятьпроверкунаналичиеэлементавпоследовательности(например,элементавспискеилиподстрокивстроке):
if/elif/else
118
In[8]:'Fast'in'FastEthernet'
Out[8]:True
In[9]:'Gigabit'in'FastEthernet'
Out[9]:False
In[10]:vlan=[10,20,30,40]
In[11]:10invlan
Out[11]:True
In[12]:50invlan
Out[12]:False
Прииспользованиисословарямиусловиеinвыполняетпроверкупоключамсловаря:
In[15]:r1={
....:'IOS':'15.4',
....:'IP':'10.255.0.1',
....:'hostname':'london_r1',
....:'location':'21NewGlobeWalk',
....:'model':'4451',
....:'vendor':'Cisco'}
In[16]:'IOS'inr1
Out[16]:True
In[17]:'4451'inr1
Out[17]:False
Операторыand,or,not
Вусловияхмогуттакжеиспользоватьсялогическиеоператорыand,or,not:
if/elif/else
119
In[15]:r1={
....:'IOS':'15.4',
....:'IP':'10.255.0.1',
....:'hostname':'london_r1',
....:'location':'21NewGlobeWalk',
....:'model':'4451',
....:'vendor':'Cisco'}
In[18]:vlan=[10,20,30,40]
In[19]:'IOS'inr1and10invlan
Out[19]:True
In[20]:'4451'inr1and10invlan
Out[20]:False
In[21]:'4451'inr1or10invlan
Out[21]:True
In[22]:not'4451'inr1
Out[22]:True
In[23]:'4451'notinr1
Out[23]:True
Операторand
ВPythonоператорandвозвращаетнебулевозначение,азначениеодногоизоператоров.
Еслиобаоперандаявляютсяистиной,результатомвыражениябудетпоследнеезначение:
In[24]:'string1'and'string2'
Out[24]:'string2'
In[25]:'string1'and'string2'and'string3'
Out[25]:'string3'
Еслиодинизоператоровявляетсяложью,результатомвыражениябудетпервоеложноезначение:
In[26]:''and'string1'
Out[26]:''
In[27]:''and[]and'string1'
Out[27]:''
if/elif/else
120
Операторor
Операторor,какиоператорand,возвращаетзначениеодногоизоператоров.
Приоценкеоперандов,возвращаетсяпервыйистинныйоперанд:
In[28]:''or'string1'
Out[28]:'string1'
In[29]:''or[]or'string1'
Out[29]:'string1'
In[30]:'string1'or'string2'
Out[30]:'string1'
Есливсезначенияявляютсяложью,возвращаетсяпоследнеезначение:
In[31]:''or[]or{}
Out[31]:{}
Важнаяособенностьработыоператораor-операнды,которыенаходятсяпослеистинного,невычисляются:
In[32]:defprint_str(string):
....:printstring
....:
In[33]:''orprint_str('teststring')
teststring
In[34]:''or'string1'orprint_str('teststring')
Out[34]:'string1'
Примериспользованияконструкцииif/elif/else
Примерскриптаcheck_password.py,которыйпроверяетдлинупароляиестьливпаролеимяпользователя:
if/elif/else
121
#-*-coding:utf-8-*-
username=raw_input('Введитеимяпользователя:')
password=raw_input('Введитепароль:')
iflen(password)<8:
print'Парольслишкомкороткий'
elifusernameinpassword:
print'Парольсодержитимяпользователя'
else:
print'Парольдляпользователя%sустановлен'%username
Проверкаскрипта:
$pythoncheck_password.py
Введитеимяпользователя:nata
Введитепароль:nata1234
Парольсодержитимяпользователя
$pythoncheck_password.py
Введитеимяпользователя:nata
Введитепароль:123nata123
Парольсодержитимяпользователя
$pythoncheck_password.py
Введитеимяпользователя:nata
Введитепароль:1234
Парольслишкомкороткий
$pythoncheck_password.py
Введитеимяпользователя:nata
Введитепароль:123456789
Парольдляпользователяnataустановлен
Трехместноевыражение(Ternaryexpressions)
Иногдаудобнееиспользоватьтернарныйоператор,нежелиразвернутуюформу:
s=[1,2,3,4]
result=Trueiflen(s)>5elseFalse
if/elif/else
122
forЦиклforпроходитсяпоуказаннойпоследовательностиивыполняетдействия,которыеуказанывблокеfor.
Примерыпоследовательностей,покоторымможетпроходитьсяциклfor:
строкасписоксловарьфункция'''range()'''илиитератор'''xrange()'''любойдругойитератор(например,'''sorted()''','''enumerate()''')
Вкурсенерассматривается,чтотакоеитератор,но,есливдвухсловах,этотакойспециальныйобъект,которыйдаетследующеезначениетолькотогда,когдаононужно.Например,еслиприиспользованиивыраженияrange(10),функциявернетсписокчиселот0до9(включительно)сразу.Еслижевоспользоватьсяитераторомxrange(),тоонвернетспециальныйобъект.Инебудетвозвращатьчисла,покакнемунебудутобращаться,например,вциклеfor.Из-затакихособенностей,всегда,когданадоработатьсбольшимколичествомобъектов,лучшеиспользоватьитератор.Вобщемслучае,еголучшеиспользоватьвезде,гдеонподходитпофункциональности.
Циклforпроходитсяпостроке:
In[1]:forletterin'Teststring':
...:printletter
...:
T
e
s
t
s
t
r
i
n
g
Вциклеиспользуетсяпеременнаясименемletter.Хотяимяможетбытьлюбое,удобно,когдаимяподсказываетчерезкакиеобъектыпроходитцикл.
Примерциклаforсфункцией(итератором)xrange():
for
123
In[2]:foriinxrange(10):
...:print'interfaceFastEthernet0/'+str(i)
...:
interfaceFastEthernet0/0
interfaceFastEthernet0/1
interfaceFastEthernet0/2
interfaceFastEthernet0/3
interfaceFastEthernet0/4
interfaceFastEthernet0/5
interfaceFastEthernet0/6
interfaceFastEthernet0/7
interfaceFastEthernet0/8
interfaceFastEthernet0/9
Вэтомциклеиспользуетсяxrange(10).Этотитераторгенерируетчиславдиапазонеотнуля,доуказанногочисла(до10),невключаяего.
Вэтомпримере,циклпроходитпоспискуVLANов,поэтомупеременнуюможноназватьvlan:
In[3]:vlans=[10,20,30,40,100]
In[4]:forvlaninvlans:
...:print'vlan%d'%vlan
...:print'nameVLAN_%d'%vlan
...:
vlan10
nameVLAN_10
vlan20
nameVLAN_20
vlan30
nameVLAN_30
vlan40
nameVLAN_40
vlan100
nameVLAN_100
Когдациклидетпословарю,тофактическионпроходитсяпоключам:
for
124
In[5]:r1={
'IOS':'15.4',
'IP':'10.255.0.1',
'hostname':'london_r1',
'location':'21NewGlobeWalk',
'model':'4451',
'vendor':'Cisco'}
In[6]:forkinr1:
....:printk
....:
vendor
IP
hostname
IOS
location
model
Еслинеобходимовыводитьпарыключ-значениевцикле:
In[7]:forkeyinr1:
....:printkey+'=>'+r1[key]
....:
vendor=>Cisco
IP=>10.255.0.1
hostname=>london_r1
IOS=>15.4
location=>21NewGlobeWalk
model=>4451
Всловареестьспециальныйметодitems,которыйпозволяетпроходитсявциклесразупопареключ,значение:
In[8]:forkey,valueinr1.items():
....:printkey+'=>'+value
....:
vendor=>Cisco
IP=>10.255.0.1
hostname=>london_r1
IOS=>15.4
location=>21NewGlobeWalk
model=>4451
Методitems,вместословаря,возвращаетсписоккортежей:
for
125
In[9]:r1.items()
Out[9]:
[('vendor','Cisco'),
('IP','10.255.0.1'),
('hostname','london_r1'),
('IOS','15.4'),
('location','21NewGlobeWalk'),
('model','4451')]
Такжеестьметодiteritems().Онполностьюаналогиченметодуitems,новозвращаетитератор,анесписоккортежей:
In[10]:r1.iteritems()
Out[10]:<dictionary-itemiteratorat0x102daff18>
for
126
Вложенныеfor
Циклыforможновкладыватьдругвдруга.
Вэтомпримере,вспискеcommandsхранятсякоманды,которыенадовыполнитьдлякаждогоизинтерфейсоввспискеfast_int:
In[7]:commands=['switchportmodeaccess','spanning-treeportfast','spanning-tree
bpduguardenable']
In[8]:fast_int=['0/1','0/3','0/4','0/7','0/9','0/10','0/11']
In[9]:forintfinfast_int:
...:print'interfaceFastEthernet'+intf
...:forcommandincommands:
...:print'%s'%command
...:
interfaceFastEthernet0/1
switchportmodeaccess
spanning-treeportfast
spanning-treebpduguardenable
interfaceFastEthernet0/3
switchportmodeaccess
spanning-treeportfast
spanning-treebpduguardenable
interfaceFastEthernet0/4
switchportmodeaccess
spanning-treeportfast
spanning-treebpduguardenable
...
Первыйциклforпроходитсяпоинтерфейсамвспискеfast_int,авторойпокомандамвспискеcommands.
Вложенныеfor
127
Совмещениеforиif
Рассмотримпримерсовмещенияforиif.
Файлgenerate_access_port_config.py:
access_template=['switchportmodeaccess',
'switchportaccessvlan',
'spanning-treeportfast',
'spanning-treebpduguardenable']
fast_int={'access':{'0/12':'10',
'0/14':'11',
'0/16':'17',
'0/17':'150'}}
forintfinfast_int['access']:
print'interfaceFastEthernet'+intf
forcommandinaccess_template:
ifcommand.endswith('accessvlan'):
print'%s%s'%(command,fast_int['access'][intf])
else:
print'%s'%command
Комментарииккоду:
Впервомциклеforперебираютсяключивовложенномсловареfast_int['access']Текущийключ,наданныймоментцикла,хранитсявпеременнойintfВыводитсястрокаinterfaceFastEthernetсдобавлениемкнейномераинтерфейсаВовторомциклеforперебираютсякомандыизспискаaccess_templateТаккакккомандеswitchportaccessvlan,надодобавитьномерVLAN:
внутривторогоциклаforпроверяютсякомандыесликомандазаканчиваетсянаaccessvlan
выводитсякомандаикнейдобавляетсяномерVLAN,обратившиськвложенномусловарюпотекущемуключуintf
вовсехостальныхслучаях,простовыводитсякоманда
Результатвыполненияскрипта:
Совмещениеforиif
128
$pythongenerate_access_port_config.py
interfaceFastEthernet0/12
switchportmodeaccess
switchportaccessvlan10
spanning-treeportfast
spanning-treebpduguardenable
interfaceFastEthernet0/14
switchportmodeaccess
switchportaccessvlan11
spanning-treeportfast
spanning-treebpduguardenable
interfaceFastEthernet0/16
switchportmodeaccess
switchportaccessvlan17
spanning-treeportfast
spanning-treebpduguardenable
Совмещениеforиif
129
Итераторenumerate()
Иногда,приперебореобъектоввциклеfor,нужнонетолькополучитьсамобъект,ноиегопорядковыйномер.Этоможносделать,создавдополнительнуюпеременную,котораябудетрастинаединицускаждымпрохождениемцикла.
Но,гораздоудобнееэтоделатьспомощьюитератораenumerate().
Базовыйпример:
In[1]:list1=['str1','str2','str3']
In[2]:forposition,stringinenumerate(list1):
...:printposition,string
...:
0str1
1str2
2str3
enumerate()умеетсчитатьнетолькоснуля,ноислюбогозначение,котороеемууказалипослеобъекта:
In[1]:list1=['str1','str2','str3']
In[2]:forposition,stringinenumerate(list1,100):
...:printposition,string
...:
100str1
101str2
102str3
ПримериспользованияenumerateдляEEM(advanced)
Слово'advanced'взаголовке,означает,чтото,чторассматриваетсявэтомразделеещёнебыловтемахкурса.Но,вдальнейшем,такойприемможетпригодится.
ВэтомпримереиспользуетсяCiscoEEM.Есливдвухсловах,тоEEMпозволяетвыполнятькакие-тодействия(action)вответнасобытие(event).
ВыглядитappletEEMтак:
Итераторenumerate
130
eventmanagerappletFa0/1_no_shut
eventsyslogpattern"LineprotocolonInterfaceFastEthernet0/0,changedstatetodo
wn"
action1clicommand"enable"
action2clicommand"conft"
action3clicommand"interfacefa0/1"
action4clicommand"nosh"
ВEEM,вситуации,когдадействийвыполнитьнужномного,неудобнокаждыйразнабиратьactionxclicommand.Плюс,чащевсего,ужеестьготовыйкусокконфигурации,которыйдолженвыполнитьEEM.
СпомощьюпростогоскриптаPython,можносгенерироватькомандыEEM,наоснованиисуществующегоспискакоманд(файлeem.py):
importsys
config=sys.argv[1]
withopen(config,'r')asfile:
for(i,command)inenumerate(file,1):
print'action%04dclicommand"%s"'%(i,command.rstrip())
Вданномпримерекомандысчитываютсяизфайла,азатемккаждойстрокедобавляетсяприставка,котораянужнадляEEM.
КонструкцияwithикакработатьсфайламирассмотриваютсявразделеРаботасфайлами
Файлскомандамивыглядиттак(r1_config.txt):
en
conft
nointGi0/0/0.300
nointGi0/0/0.301
nointGi0/0/0.302
intrangegi0/0/0-2
channel-group1modeactive
interfacePort-channel1.300
encapsulationdot1Q300
vrfforwardingManagement
ipaddress10.16.19.35255.255.255.248
Выводбудеттаким:
Итераторenumerate
131
$pythoneem.pyr1_config.txt
action0001clicommand"en"
action0002clicommand"conft"
action0003clicommand"nointGi0/0/0.300"
action0004clicommand"nointGi0/0/0.301"
action0005clicommand"nointGi0/0/0.302"
action0006clicommand"intrangegi0/0/0-2"
action0007clicommand"channel-group1modeactive"
action0008clicommand"interfacePort-channel1.300"
action0009clicommand"encapsulationdot1Q300"
action0010clicommand"vrfforwardingManagement"
action0011clicommand"ipaddress10.16.19.35255.255.255.248"
Упростим,иуберемсчитываниефайла,чтобыбылопрощепонять.
Теперькомандыхранятсявсписке:
In[1]:list1=['en\n',
...:'conft\n',
...:'nointGi0/0/0.300\n',
...:'nointGi0/0/0.301\n',
...:'nointGi0/0/0.302\n',
...:'intrangegi0/0/0-2\n',
...:'channel-group1modeactive\n',
...:'interfacePort-channel1.300\n',
...:'encapsulationdot1Q300\n',
...:'vrfforwardingManagement\n',
...:'ipaddress10.16.19.35255.255.255.248\n']
Повторяемциклизфайла:
In[2]:for(i,command)inenumerate(list1,1):
...:print'action%04dclicommand"%s"'%(i,command.rstrip())
...:
action0001clicommand"en"
action0002clicommand"conft"
action0003clicommand"nointGi0/0/0.300"
action0004clicommand"nointGi0/0/0.301"
action0005clicommand"nointGi0/0/0.302"
action0006clicommand"intrangegi0/0/0-2"
action0007clicommand"channel-group1modeactive"
action0008clicommand"interfacePort-channel1.300"
action0009clicommand"encapsulationdot1Q300"
action0010clicommand"vrfforwardingManagement"
action0011clicommand"ipaddress10.16.19.35255.255.255.248"
Итераторenumerate
132
Итераторenumerate
133
whileЦиклwhileэтоещеоднаразновидностьциклавPython.
Вциклеwhile,какиввыраженииif,надописатьусловие.Еслиусловиеистинно,выполняютсядействиявнутриблокаwhile.Но,вотличииотif,послевыполненияwhileвозвращаетсявначалоцикла.
Прииспользованиицикловwhile,необходимообращатьвниманиенато,будетлидостигнутотакоесостояние,прикоторомусловиециклабудетложным.
Рассмотримпростойпример:
In[1]:a=5
In[2]:whilea>0:
...:printa
...:a-=1#Этазаписьравнозначнаa=a-1
...:
5
4
3
2
1
Сначаласоздаетсяпеременнаяа,созначением5.
Затем,вциклеwhileуказаноусловиеa>0.Тоесть,показначениеабольше0,будутвыполнятьсядействиявтелецикла.Вданномслучае,будетвыводитьсязначениепеременнойа.
Крометого,втелецикла,прикаждомпрохождении,значениеастановитсянаединицуменьше.
Записьa-=1можетбытьнемногонеобычной.Pythonпозволяетиспользоватьтакойформат,вместоa=a-1.
Аналогичнымобразомможнописать:a+=1,a*=2,a/=2.
Таккакзначениеауменьшается,циклнебудетбесконечнымивкакой-томоментвыражениеa>0станетложным.
Следующийпримерпостроеннаосновепримерапропарольизразделаоконструкцииif.Втомпримере,приходилосьзановозапускатьскрипт,еслипарольнесоответствовалтребованиям.
while
134
Спомощьюциклаwhile,можносделатьтак,чтоскриптсамбудетзапрашиватьпарользаново,еслионнесоответствуеттребованиям.
Файлcheck_password_with_while.py:
#-*-coding:utf-8-*-
username=raw_input('Введитеимяпользователя:')
password=raw_input('Введитепароль:')
pass_OK=False
whilenotpass_OK:
iflen(password)<8:
print'Парольслишкомкороткий\n'
password=raw_input('Введитепарольещераз:')
elifusernameinpassword:
print'Парольсодержитимяпользователя\n'
password=raw_input('Введитепарольещераз:')
else:
print'Парольдляпользователя%sустановлен'%username
pass_OK=True
Вэтомслучаециклwhileполезен,таккаконвозвращаетскриптсновавначалопроверок,позволяетснованабратьпароль,ноприэтомнетребуетперезапускасамогоскрипта.
Теперьскриптотрабатываеттак:
$pythoncheck_password_with_while.py
Введитеимяпользователя:nata
Введитепароль:nata
Парольслишкомкороткий
Введитепарольещераз:natanata
Парольсодержитимяпользователя
Введитепарольещераз:123345345345
Парольдляпользователяnataустановлен
while
135
break,continue,passВPythonестьнесколькооператоров,которыепозволяютменятьповедениецикловпоумолчанию.
Операторbreak
Операторbreakпозволяетдосрочнопрерватьцикл:
breakпрерываеттекущийциклипродолжаетвыполнениеследующихвыраженийеслииспользуетсянескольковложенныхциклов,breakпрерываетвнутреннийциклипродолжаетвыполнятьвыраженияследующиезаблокомbreakможетиспользоватьсявциклахforиwhile
Примерсцикломfor:
In[1]:fornuminrange(10):
...:ifnum<7:
...:printnum
...:else:
...:break
...:
0
1
2
3
4
5
6
Примерсцикломwhile:
In[2]:i=0
In[3]:whilei<10:
...:ifi==5:
...:break
...:else:
...:printi
...:i+=1
...:
0
1
2
3
4
break,continue,pass
136
Ещёпример:
#-*-coding:utf-8-*-
username=raw_input('Введитеимяпользователя:')
password=raw_input('Введитепароль:')
whileTrue:
iflen(password)<8:
print'Парольслишкомкороткий\n'
password=raw_input('Введитепарольещераз:')
elifusernameinpassword:
print'Парольсодержитимяпользователя\n'
password=raw_input('Введитепарольещераз:')
else:
print'Парольдляпользователя%sустановлен'%username
break
Операторcontinue
Операторcontinueвозвращаетуправлениевначалоцикла.Тоесть,continueпозволяет"перепрыгнуть"оставшиесявыражениявциклеиперейтикследующейитерации.
Примерсцикломfor:
In[4]:fornuminrange(5):
...:ifnum==3:
...:continue
...:else:
...:printnum
...:
0
1
2
4
Примерсцикломwhile:
break,continue,pass
137
In[5]:i=0
In[6]:whilei<6:
....:i+=1
....:ifi==3:
....:print"Пропускаем3"
....:continue
....:print"Этониктонеувидит"
....:else:
....:print"Текущеезначение:",i
....:
Текущеезначение:1
Текущеезначение:2
Пропускаем3
Текущеезначение:4
Текущеезначение:5
Текущеезначение:6
Операторpass
Операторpassничегонеделает.Фактическиэтотакаязаглушкадляобъектов.
Например,passможетпомочьвситуации,когданужнопрописатьструктурускрипта.Егоможноставитьвциклах,функциях,классах.Иэтонебудетвлиятьнаисполнениекода.
Примериспользованияpass:
In[6]:fornuminrange(5):
....:ifnum<3:
....:pass
....:else:
....:printnum
....:
3
4
break,continue,pass
138
for/else,while/elseВциклахforиwhileопциональноможетиспользоватьсяблокelse.
for/else
Вциклеfor:
блокelseвыполняетсявтомслучае,еслициклзавершилитерациюсписканоelseневыполняется,есливциклебылвыполненbreak
Примерциклаforсelse(блокelseвыполняетсяпослезавершенияциклаfor):
In[1]:fornuminrange(5):
....:printnum
....:else:
....:print"Числазакончились"
....:
0
1
2
3
4
Числазакончились
Примерциклаforсelseиbreakвцикле(из-заbreak,блокelseневыполняется):
In[2]:fornuminrange(5):
....:ifnum==3:
....:break
....:else:
....:printnum
....:else:
....:print"Числазакончились"
....:
0
1
2
Примерциклаforсelseиcontinueвцикле(continueневлияетнаблокelse):
for/else,while/else
139
In[3]:fornuminrange(5):
....:ifnum==3:
....:continue
....:else:
....:printnum
....:else:
....:print"Числазакончились"
....:
0
1
2
4
Числазакончились
while/else
Вциклеwhile:
блокelseвыполняетсявтомслучае,еслициклзавершилитерациюсписканоelseневыполняется,есливциклебылвыполненbreak
Примерциклаwhileсelse(блокelseвыполняетсяпослезавершенияциклаwhile):
In[4]:i=0
In[5]:whilei<5:
....:printi
....:i+=1
....:else:
....:print"Конец"
....:
0
1
2
3
4
Конец
Примерциклаwhileсelseиbreakвцикле(из-заbreak,блокelseневыполняется):
for/else,while/else
140
In[6]:i=0
In[7]:whilei<5:
....:ifi==3:
....:break
....:else:
....:printi
....:i+=1
....:else:
....:print"Конец"
....:
0
1
2
for/else,while/else
141
Работасисключениямиtry/except/else/finally
try/except
Есливыповторялипримеры,которыеиспользовалисьранее,тонавернякабылиситуации,когдавыскакивалаошибка.Скореевсего,этобылаошибкасинтаксиса,когданехватало,например,двоеточия.
Какправило,Pythonдовольнопонятнореагируетнаподобныеошибкииихможноисправить.
Но,дажеесликодсинтаксическинаписанправильно,всеравномогутвозникатьошибки.Этиошибкиназываютсяисключения(exceptions).
Примерыисключений:
In[1]:2/0
-----------------------------------------------------
ZeroDivisionError:integerdivisionormodulobyzero
In[2]:'test'+2
-----------------------------------------------------
TypeError:cannotconcatenate'str'and'int'objects
Вданномслучае,возниклодваисключения:ZeroDivisionErrorиTypeError.
Чащевсего,можнопредсказатькакогородаисключениявозникнутвовремяисполненияпрограммы.
Например,еслипрограмманавходожидаетдвачисла,анавыходевыдаетихсумму,апользовательввелвместоодногочисла,строку,появитсяошибкаTypeError,каквпримеревыше.
Pythonпозволяетработатьсисключениями.Ихможноперехватыватьивыполнятьдействия,втомслучае,есливозниклоисключение.
Когдавпрограммевозникаетисключение,онасразузавершаетработу.
Дляработысисключениямииспользуетсяконструкцияtry/except:
Работасисключениямиtry/except/else/finally
142
In[3]:try:
...:2/0
...:exceptZeroDivisionError:
...:print"Youcan'tdividebyzero"
...:
Youcan'tdividebyzero
Конструкцияtryработаеттакимобразом:
сначалавыполняютсявыражения,которыезаписанывблокеtryеслипривыполненияблокаtry,невозниклоникакихисключений,блокexceptпропускается.Ивыполняетсядальнейшийкодесливовремявыполненияблокаtry,вкаком-томесте,возниклоисключение,оставшаясячастьблокаtryпропускается
есливблокеexceptуказаноисключение,котороевозникло,выполняетсякодвблокеexceptеслиисключение,котороевозникло,неуказановблокеexcept,выполнениепрограммыпрерываетсяивыдаетсяошибка
Обратитевнимание,чтострока'Cool!'вблокеtryневыводится:
In[4]:try:
...:print"Let'sdividesomenumbers"
...:2/0
...:print'Cool!'
...:exceptZeroDivisionError:
...:print"Youcan'tdividebyzero"
...:
Let'sdividesomenumbers
Youcan'tdividebyzero
Вконструкцииtry/exceptможетбытьмногоexcept,еслинужныразныедействия,взависимостиотошибки.
Например,скриптdivide.pyделитдвачиславведенныхпользователем:
#-*-coding:utf-8-*-
try:
a=raw_input("Введитепервоечисло:")
b=raw_input("Введитевтороечисло:")
print"Результат:",int(a)/int(b)
exceptValueError:
print"Пожалуйста,вводитетолькочисла"
exceptZeroDivisionError:
print"Нанольделитьнельзя"
Работасисключениямиtry/except/else/finally
143
Примерывыполненияскрипта:
$pythondivide.py
Введитепервоечисло:3
Введитевтороечисло:1
Результат:3
$pythondivide.py
Введитепервоечисло:5
Введитевтороечисло:0
Результат:Нанольделитьнельзя
$pythondivide.py
Введитепервоечисло:qewr
Введитевтороечисло:3
Результат:Пожалуйста,вводитетолькочисла
Вданномслучае,исключениеValueErrorвозникаеткогдапользовательввелстроку,вместочисла,вовремяпереводастрокивчисло.
ИсключениеZeroDivisionErrorвозникаетвслучае,есливтороечислобылоравным0.
ЕслинетнеобходимостивыводитьразличныесообщениянаошибкиValueErrorиZeroDivisionError,можносделатьтак(файлdivide_ver2.py):
#-*-coding:utf-8-*-
try:
a=raw_input("Введитепервоечисло:")
b=raw_input("Введитевтороечисло:")
print"Результат:",int(a)/int(b)
except(ValueError,ZeroDivisionError):
print"Что-топошлонетак..."
Проверка:
$pythondivide_ver2.py
Введитепервоечисло:wer
Введитевтороечисло:4
Результат:Что-топошлонетак...
$pythondivide_ver2.py
Введитепервоечисло:5
Введитевтороечисло:0
Результат:Что-топошлонетак...
Работасисключениямиtry/except/else/finally
144
Вблокеexceptможнонеуказыватьконкретноеисключениеилиисключения.Втакомслучае,будутперехватыватьсявсеисключения.
Этоделатьнерекомендуется!
try/except/else
Вконструкцииtry/exceptестьопциональныйблокelse.Онвыполняетсявтомслучае,еслинебылоисключения.
Например,еслинеобходимовыполнятьвдальнейшемкакие-тооперациисданными,которыеввелпользователь,можнозаписатьихвблокеelse(файлdivide_ver3.py):
#-*-coding:utf-8-*-
try:
a=raw_input("Введитепервоечисло:")
b=raw_input("Введитевтороечисло:")
result=int(a)/int(b)
except(ValueError,ZeroDivisionError):
print"Что-топошлонетак..."
else:
print"Результатвквадрате:",result**2
Примервыполнения:
$pythondivide_ver3.py
Введитепервоечисло:10
Введитевтороечисло:2
Результатвквадрате:25
$pythondivide_ver3.py
Введитепервоечисло:werq
Введитевтороечисло:3
Что-топошлонетак...
try/except/finally
Блокfinallyэтоещеодинопциональныйблоквконструкцииtry.Онвыполняетсявсегда,независимооттого,былолиисключениеилинет.
Сюдаставятсядействия,которыенадовыполнитьвлюбомслучае.Например,этоможетбытьзакрытиефайла.
Файлdivide_ver4.pyсблокомfinally:
Работасисключениямиtry/except/else/finally
145
#-*-coding:utf-8-*-
try:
a=raw_input("Введитепервоечисло:")
b=raw_input("Введитевтороечисло:")
result=int(a)/int(b)
except(ValueError,ZeroDivisionError):
print"Что-топошлонетак..."
else:
print"Результатвквадрате:",result**2
finally:
print"Вотисказочкеконец,актослушал-молодец."
Проверка:
$pythondivide_ver4.py
Введитепервоечисло:10
Введитевтороечисло:2
Результатвквадрате:25
Вотисказочкеконец,актослушал-молодец.
$pythondivide_ver4.py
Введитепервоечисло:qwerewr
Введитевтороечисло:3
Что-топошлонетак...
Вотисказочкеконец,актослушал-молодец.
$pythondivide_ver4.py
Введитепервоечисло:4
Введитевтороечисло:0
Что-топошлонетак...
Вотисказочкеконец,актослушал-молодец.
Работасисключениямиtry/except/else/finally
146
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание5.1
1. ЗапроситьупользователявводIP-адресавдесятично-точечномформате.2. ОпределитькакомуклассупринадлежитIP-адрес.3. Взависимостиотклассаадреса,вывестинастандартныйпотоквывода:
'unicast'-еслиIP-адреспринадлежитклассуA,BилиC'multicast'-еслиIP-адреспринадлежитклассуD'localbroadcast'-еслиIP-адресравен255.255.255.255'unassigned'-еслиIP-адресравен0.0.0.0'unused'-вовсехостальныхслучаях
Подсказкапоклассам(диапазонзначенийпервогобайтавдесятичномформате):
A:1-127B:128-191C:192-223D:224-239
Задание5.1a
Сделатькопиюскриптазадания5.1.
Задания
147
Дополнитьскрипт:
ДобавитьпроверкувведенногоIP-адреса.Адрессчитаетсякорректнозаданным,еслион:
состоитиз4чиселразделенныхточкой,каждоечисловдиапазонеот0до255.
Еслиадресзаданнеправильно,выводитьсообщение:
'IncorrectIPv4address'
Задание5.1b
Сделатькопиюскриптазадания5.1a.
Дополнитьскрипт:
Еслиадресбылвведеннеправильно,запроситьадресснова.
Задание5.2
СписокmacсодержитMAC-адресавформатеXXXX:XXXX:XXXX.
Однако,воборудованииCiscoMAC-адресаиспользуютсявформатеXXXX.XXXX.XXXX.
Создатьскрипт,которыйпреобразуетMAC-адресавформатciscoидобавляетихвновыйсписокmac_cisco.
Усложненныйвариант:сделатьпреобразованиеводнойстрокескрипта.
mac=['aabb:cc80:7000','aabb:dd80:7340','aabb:ee80:7000','aabb:ff80:7000']
mac_cisco=[]
Задание5.3
Вскриптесделангенераторконфигурациидляaccess-портов.
Сделатьаналогичныйгенераторконфигурациидляпортовtrunk.
Втранкахситуацияусложняетсятем,чтоVLANовможетбытьмного,инадопонимать,чтоснимиделать.
Поэтомувсоответствиикаждомупортустоитсписокипервый(нулевой)элементспискауказываеткаквосприниматьномераVLAN,которыеидутдальше:
Задания
148
add-значитVLANынадобудетдобавить(командаswitchporttrunkallowedvlanadd10,20)del-значитVLANынадоудалитьизспискаразрешенных(командаswitchporttrunkallowedvlanremove17)only-значит,чтонаинтерфейседолжныостатьсяразрешеннымитолькоуказанныеVLANы(командаswitchporttrunkallowedvlan11,30)
Задачадляпортов0/1,0/2,0/4:
сгенерироватьконфигурациюнаосновешаблонаtrunk_templateсучетомключевыхсловadd,del,only
access_template=['switchportmodeaccess',
'switchportaccessvlan',
'spanning-treeportfast',
'spanning-treebpduguardenable']
trunk_template=['switchporttrunkencapsulationdot1q',
'switchportmodetrunk',
'switchporttrunkallowedvlan']
fast_int={'access':{'0/12':'10','0/14':'11','0/16':'17','0/17':'150'},
'trunk':{'0/1':['add','10','20'],
'0/2':['only','11','30'],
'0/4':['del','17']}}
forintinfast_int['access']:
print'interfaceFastEthernet'+int
forcommandinaccess_template:
ifcommand.endswith('accessvlan'):
print'%s%s'%(command,fast_int['access'][int])
else:
print'%s'%command
Задания
149
РаботасфайламиВреальнойжизни,длятогочтобыполноценноиспользоватьвсё,чторассматривалосьдоэтогораздела,надоразобратьсякакработатьсфайлами.
Приработессетевымоборудованием(инетолько),файламимогутбыть:
конфигурации(простые,неструктурированныетекстовыефайлы)работаснимирассматриваетсявэтомразделе
шаблоныконфигурацийкакправило,этокакой-тоспециальныйформатфайлов.вразделеШаблоныконфигурацийсJinjaрассматриваетсяиспользованиеJinja2длясозданияшаблоновконфигураций
файлыспараметрамиподключенийкакправило,этоструктурированныефайлы,вкаком-тоопределенномформате:YAML,JSON,CSVвразделеСериализацияданныхрассматриваетсякакработатьстакимифайлами
другиескриптыPythonвразделеМодулирассматриваетсякакработатьсмодулями(другимискриптамиPython)
Вэтомразделерассматриваетсяработаспростымитекстовымифайлами.Например,конфигурационныйфайлCisco.
Вработесфайламиестьнесколькоаспектов:
открытие/закрытиечтениезапись
Вэтомразделерассматриваетсятольконеобходимыйминимумдляработысфайлами.Подробнее,вдокументацииPython.
6.Работасфайлами
150
ОткрытиефайловДляначалаработысфайлом,егонадооткрыть.
open()
Дляоткрытияфайлов,чащевсего,используетсяфункцияopen():
file=open('file_name.txt','r')
Вфункцииopen():
'file_name.txt'-имяфайлатутможноуказыватьнетолькоимя,ноипуть(абсолютныйилиотносительный)
'r'-режимоткрытияфайла
Функцияopen()создаетобъектfile,ккоторомупотомможноприменятьразличныеметоды,дляработысним.
Режимыоткрытияфайлов:
r-открытьфайлтолькодлячтения(значениепоумолчанию)r+-открытьфайлдлячтенияизаписиw-открытьфайлдлязаписи
еслифайлсуществует,тоегосодержимоеудаляетсяеслифайлнесуществует,тосоздаетсяновый
w+-открытьфайлдлячтенияизаписиеслифайлсуществует,тоегосодержимоеудаляетсяеслифайлнесуществует,тосоздаетсяновый
a-открытьфайлдлядополнениязаписи.Данныедобавляютсявконецфайлаa+-открытьфайлдлячтенияизаписи.Данныедобавляютсявконецфайла
Обычно,возникаетпутаницасрежимамиw+иr+.Разницамеждунимивтом,чтоw+,приоткрытии,удаляетсодержимоефайла,аr+-нет.
Открытиефайлов
151
ЧтениефайловВPythonестьнесколькометодовчтенияфайла:
read()-считываетсодержимоефайлавстрокуreadline()-считываетфайлпострочноreadlines()-считываетстрокифайлаисоздаетсписокизстрок
Посмотримкаксчитыватьсодержимоефайлов,напримерефайлаr1.txt:
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
read()
Методread()-считываетвесьфайлводнустроку.
Примериспользованияметодаread():
In[1]:f=open('r1.txt')
In[2]:f.read()
Out[2]:'!\nservicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear\nservi
cetimestampslogdatetimemseclocaltimeshow-timezoneyear\nservicepassword-encrypt
ion\nservicesequence-numbers\n!\nnoipdomainlookup\n!\nipsshversion2\n!\n'
In[3]:f.read()
Out[3]:''
Приповторномчтениифайлав3строке,отображаетсяпустаястрока.Такпроисходитиз-затого,чтопривызовеметодаread(),считываетсявесьфайл.Ипослетого,какфайлбылсчитан,курсоростаетсявконцефайла.Управлятьположениемкурсораможноспомощьюметодаseek().
readline()
Чтениефайлов
152
Построчнофайлможносчитатьспомощьюметодаreadline():
In[4]:f=open('r1.txt')
In[5]:f.readline()
Out[5]:'!\n'
In[6]:f.readline()
Out[6]:'servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear\n'
Но,чащевсего,прощепройтисьпообъектуfileвцикле,неиспользуяметодыread...:
In[7]:f=open('r1.txt')
In[8]:forlineinf:
...:printline
...:
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
readlines()
Ещеодинполезныйметод-readlines().Онсчитываетстрокифайлавсписок:
Чтениефайлов
153
In[9]:f=open('r1.txt')
In[10]:f.readlines()
Out[10]:
['!\n',
'servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear\n',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear\n',
'servicepassword-encryption\n',
'servicesequence-numbers\n',
'!\n',
'noipdomainlookup\n',
'!\n',
'ipsshversion2\n',
'!\n']
Еслинужнополучитьстрокифайла,нобезпереводастрокивконце,можновоспользоватьсяметодомsplitикакразделитель,указатьсимвол\n:
In[11]:f=open('r1.txt')
In[12]:f.read().split('\n')
Out[12]:
['!',
'servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
'servicepassword-encryption',
'servicesequence-numbers',
'!',
'noipdomainlookup',
'!',
'ipsshversion2',
'!',
'']
Обратитевнимание,чтопоследнийэлементсписка-пустаястрока.
Еслипередвыполнениемsplit(),воспользоватьсяметодомrstrip(),списокбудетбезпустойстрокивконце:
Чтениефайлов
154
In[13]:f=open('r1.txt')
In[14]:f.read().rstrip().split('\n')
Out[14]:
['!',
'servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
'servicepassword-encryption',
'servicesequence-numbers',
'!',
'noipdomainlookup',
'!',
'ipsshversion2',
'!']
seek()
Досихпор,файлкаждыйразприходилосьоткрыватьзаново,чтобысноваегосчитать.Такпроисходитиз-затого,чтопослеметодовчтения,курсорнаходитсявконцефайла.Иповторноечтениевозвращаетпустуюстроку.
Чтобыещёразсчитатьинформациюизфайла,нужновоспользоватьсяметодомseek,которыйперемещаеткурсорвнеобходимоеположение.
Примероткрытияфайлаисчитываниясодержимого:
In[15]:f=open('r1.txt')
In[16]:printf.read()
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
Есливызыватьещёразметодread,возвращаетсяпустаястрока:
In[17]:printf.read()
Но,спомощьюметодаseek,можноперейтивначалофайла(0означаетначалофайла):
Чтениефайлов
155
In[18]:f.seek(0)
Послетого,как,спомощьюseek,курсорбылпереведенвначалофайла,можноопятьсчитыватьсодержимое:
In[19]:printf.read()
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
Чтениефайлов
156
ЗаписьфайловПризаписи,оченьважноопределитьсясрежимомоткрытияфайла,чтобыслучайноегонеудалить:
append-добавитьстрокивсуществующийфайлwrite-перезаписатьфайлобарежимасоздаютфайл,еслионнесуществует
Длязаписивфайлиспользуютсятакиеметоды:
write()-записатьвфайлоднустрокуwritelines()-позволяетпередаватьвкачествеаргументасписокстрок
write()
Методwriteожидаетстроку,длязаписи.
Дляпримера,возьмемсписокстроксконфигурацией:
In[1]:cfg_lines=['!',
...:'servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
...:'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
...:'servicepassword-encryption',
...:'servicesequence-numbers',
...:'!',
...:'noipdomainlookup',
...:'!',
...:'ipsshversion2',
...:'!']
Открытиефайлаr2.txtврежимедлязаписи:
In[2]:f=open('r2.txt','w')
Преобразуемсписоккомандводнубольшуюстрокуспомощьюjoin:
In[3]:cfg_lines_as_string='\n'.join(cfg_lines)
In[4]:cfg_lines_as_string
Out[4]:'!\nservicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear\nservi
cetimestampslogdatetimemseclocaltimeshow-timezoneyear\nservicepassword-encrypt
ion\nservicesequence-numbers\n!\nnoipdomainlookup\n!\nipsshversion2\n!'
Записьфайлов
157
Записьстрокивфайл:
In[5]:f.write(cfg_lines_as_string)
Аналогичноможнодобавитьстрокувручную:
In[6]:f.write('\nhostnamer2')
Послезавершенияработысфайлом,егонеобходимозакрыть:
In[7]:f.close()
Таккакipythonподдерживаеткомандуcat,можнолегкопосмотретьсодержимоефайла:
In[8]:catr2.txt
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
hostnamer2
writelines()
Методwritelines()ожидаетсписокстрок,какаргумент.
Записьспискастрокcfg_linesвфайл:
Записьфайлов
158
In[1]:cfg_lines=['!',
...:'servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
...:'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
...:'servicepassword-encryption',
...:'servicesequence-numbers',
...:'!',
...:'noipdomainlookup',
...:'!',
...:'ipsshversion2',
...:'!']
In[9]:f=open('r2.txt','w')
In[10]:f.writelines(cfg_lines)
In[11]:f.close()
In[12]:catr2.txt
!servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyearservicetimestamps
logdatetimemseclocaltimeshow-timezoneyearservicepassword-encryptionservicesequ
ence-numbers!noipdomainlookup!ipsshversion2!
Врезультате,всестрокиизсписка,записалисьводнустрокуфайла,таккаквконцестрокнебылосимвола\n.
Добавитьпереводстрокиможнопо-разному.Например,можнопростообработатьсписоквцикле:
In[13]:cfg_lines2=[]
In[14]:forlineincfg_lines:
....:cfg_lines2.append(line+'\n')
....:
In[15]:cfg_lines2
Out[15]:
['!\n',
'servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear\n',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear\n',
'servicepassword-encryption\n',
'servicesequence-numbers\n',
'!\n',
'noipdomainlookup\n',
'!\n',
'ipsshversion2\n',
Илииспользоватьlistcomprehensions:
Записьфайлов
159
In[16]:cfg_lines3=[line+'\n'forlineincfg_lines]
In[17]:cfg_lines3
Out[17]:
['!\n',
'servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear\n',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear\n',
'servicepassword-encryption\n',
'servicesequence-numbers\n',
'!\n',
'noipdomainlookup\n',
'!\n',
'ipsshversion2\n',
'!\n']
Еслилюбой,изполучившихсясписковзаписатьзанововфайл,товнемужебудутпереводыстрок:
In[18]:f=open('r2.txt','w')
In[19]:f.writelines(cfg_lines3)
In[20]:f.close()
In[21]:catr2.txt
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
Записьфайлов
160
ЗакрытиефайловВреальнойжизни,длязакрытияфайлов,чащевсего,используетсяконструкцияwith.Еёнамногоудобнейиспользовать,чемзакрыватьфайлявно.Но,таккаквжизниможновстретитьиметодclose,вэтомразделерассматриваетсякакегоиспользовать.
Послезавершенияработысфайлом,егонужнозакрыть.Внекоторыхслучаях,Pythonможетсамостоятельнозакрытьфайл.Нолучшенаэтонерассчитыватьизакрыватьфайлявно.
close()
Методcloseвстречалсявразделезаписьфайлов.Тамонбылнужендлятого,чтобысодержимоефайлабылозаписанонадиск.
Дляэтого,вPythonестьотдельныйметодflush().Но,таккак,впримересзаписьюфайлов,ненужнобылобольшевыполнятьникакихопераций,файлможнобылозакрыть.
Откроемфайлr1.txt:
In[1]:f=open('r1.txt','r')
Теперьможносчитатьсодержимое:
In[2]:printf.read()
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
Уобъектаfileестьспециальныйатрибутclosed,которыйпозволяетпроверитьзакрытфайлилинет.Еслифайлоткрыт,онвозвращаетFalse:
Закрытиефайлов
161
In[3]:f.closed
Out[3]:False
Теперьзакрываемфайлисновапроверяемclosed:
In[4]:f.close()
In[5]:f.closed
Out[5]:True
Еслипопробоватьпрочитатьфайл,возникнетисключение:
In[6]:printf.read()
------------------------------------------------------------------
ValueErrorTraceback(mostrecentcalllast)
<ipython-input-53-2c962247edc5>in<module>()
---->1printf.read()
ValueError:I/Ooperationonclosedfile
Использованиеtry/finallyдляработысфайлами
Спомощьюобработкиисключений,можно:
перехватыватьисключения,которыевозникают,припопыткепрочитатьнесуществующийфайлзакрыватьфайл,послевсехопераций,вблокеfinally
Еслипопытатьсяоткрытьдлячтенияфайл,которогонесуществует,возникнеттакоеисключение:
In[7]:f=open('r3.txt','r')
---------------------------------------------------------------------------
IOErrorTraceback(mostrecentcalllast)
<ipython-input-54-1a33581ca641>in<module>()
---->1f=open('r3.txt','r')
IOError:[Errno2]Nosuchfileordirectory:'r3.txt'
Спомощьюконструкцииtry/except,можноперехватитьэтоисключениеивывестисвоёсообщение:
Закрытиефайлов
162
In[8]:try:
....:f=open('r3.txt','r')
....:exceptIOError:
....:print'Nosuchfile'
....:
Nosuchfile
Аспомощьючастиfinally,можнозакрытьфайл,послевсехопераций:
In[9]:try:
....:f=open('r1.txt','r')
....:printf.read()
....:exceptIOError:
....:print'Nosuchfile'
....:finally:
....:f.close()
....:
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
In[10]:f.closed
Out[10]:True
Закрытиефайлов
163
КонструкцияwithКонструкцияwithназываетсяменеджерконтекста.
ВPythonсуществуетболееудобныйспособработысфайлами,чемте,которыеиспользовалисьдосихпор-конструкцияwith:
In[1]:withopen('r1.txt','r')asf:
....:forlineinf:
....:printline
....:
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
Крометого,конструкцияwithгарантируетзакрытиефайлаавтоматически.
Обратитевниманиенато,каксчитываютсястрокифайла:
forlineinf:
printline
Когдасфайломнужноработатьпострочно,лучшеиспользоватьтакойвариант.
Впредыдущемвыводе,междустрокамифайлабылилишниепустыестроки,таккакprintдобавляетещёодинпереводстроки.
Чтобыизбавитьсяотэтого,можнопоставитьзапятуювконцевыраженияprintиливызватьметодrstrip:
Конструкцияwith
164
In[2]:withopen('r1.txt','r')asf:
....:forlineinf:
....:printline.rstrip()
....:
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
In[3]:f.closed
Out[3]:True
И,конечноже,сконструкциейwithможноиспользоватьнетолькотакойпострочныйвариантсчитывания,всеметоды,которыерассматривалисьдоэтого,такжеработают:
In[4]:withopen('r1.txt','r')asf:
....:printf.read()
....:
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
Конструкцияwithможетиспользоватьсянетолькосфайлами.
Подробнееобэтомможнопочитатьпоссылке:http://stackoverflow.com/questions/3012488/what-is-the-python-with-statement-designed-for
Конструкцияwith
165
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание6.1
Аналогичнозаданию3.1обработатьстрокиизфайлаospf.txtивывестиинформациюпокаждойвтакомвиде:
Protocol:OSPF
Prefix:10.0.24.0/24
AD/Metric:110/41
Next-Hop:10.0.13.3
Lastupdate:3d18h
OutboundInterface:FastEthernet0/0
Таккакэтопервоезаданиесоткрытиемфайла,заготовкадляоткрытияфайлаужесделана.
withopen('ospf.txt','r')asf:
forlineinf:
printline
Задание6.2
Создатьскрипт,которыйбудетобрабатыватьконфигурационныйфайлconfig_sw1.txt:
Задания
166
имяфайлапередаетсякакаргументскрипту
Скриптдолженвозвращатьнастандартныйпотоквыводакомандыизпереданногоконфигурационногофайла,исключаястроки,которыеначинаютсяс'!'.
Междустрокаминедолжнобытьдополнительногосимволапереводастроки.
Задание6.2a
Сделатькопиюскриптазадания6.2.
Дополнитьскрипт:
Скриптнедолженвыводитькоманды,вкоторыхсодержатсяслова,которыеуказанывспискеignore.
ignore=['duplex','alias','Currentconfiguration']
Задание6.2b
Дополнитьскриптиззадания6.2a:
вместовыводанастандартныйпотоквывода,скриптдолжензаписатьполученныестрокивфайлconfig_sw1_cleared.txt
Приэтом,должныбытьотфильтрованыстроки,которыесодержатсявспискеignore.
Строки,которыеначинаютсяна'!'отфильтровыватьненужно.
ignore=['duplex','alias','Currentconfiguration']
Задание6.2c
Переделатьскриптиззадания6.2b:
передаватькакаргументы:имяисходногофайлаконфигурацииимяитоговогофайлаконфигурации
Внутри,скриптдолженотфильтроватьтестроки,висходномфайлеконфигурации,вкоторыхсодержатсясловаизспискаignore.Изатемзаписатьоставшиесястрокивитоговыйфайл.
Проверитьработускриптанапримерефайлаconfig_sw1.txt.
Задания
167
ignore=['duplex','alias','Currentconfiguration']
Задание6.3
СкриптдолженобрабатыватьзаписивфайлеCAM_table.txtтакимобразомчтобы:
считывалисьтолькостроки,вкоторыхуказаныMAC-адресакаждаястрока,гдеестьMAC-адрес,должнаобрабатыватьсятакимобразом,чтобынастандартныйпотоквыводабылавыведенатаблицавида:
100aabb.cc80.7000Gi0/1
200aabb.cc80.7000Gi0/2
300aabb.cc80.7000Gi0/3
100aabb.cc80.7000Gi0/4
500aabb.cc80.7000Gi0/5
200aabb.cc80.7000Gi0/6
300aabb.cc80.7000Gi0/7
Задание6.3a
Сделатькопиюскриптазадания6.3
Дополнитьскрипт:
ОтсортироватьвыводпономеруVLAN
Задание6.3b
Сделатькопиюскриптазадания6.3a
Дополнитьскрипт:
ЗапроситьупользователявводномераVLAN.ВыводитьинформациютолькопоуказанномуVLAN.
Задания
168
ФункцииФункция-этоблоккода,выполняющийопределенныедействия:
уфункцииестьимя,спомощьюкоторогоможнозапускатьэтотблоккодасколькоугоднораз
запусккодафункции,называетсявызовомфункцииприсозданиифункции,какправило,определяютсяпараметрыфункции.
параметрыфункцииопределяюткакиеаргументыфункцияможетприниматьфункциямможнопередаватьаргументы
соответственно,кодфункциибудетвыполнятьсясучетомуказанныхаргументов
Зачемнужныфункции?
Какправило,задачи,которыерешаеткод,оченьпохожиичастоимеютчто-тообщее.
Например,приработесконфигурационнымифайлами,каждыйразнадовыполнятьтакиедействия:
открытиефайлаудаление(илипропуск)строк,которыеначинаютсяназнаквосклицания(дляCisco)удаление(илипропуск)пустыхстрокудалениесимволовпереводастрокивконцестрокпреобразованиеполученногорезультатавсписок
Дальшедействиямогутотличаться,взависимостиоттого,чтонужноделать.
Частополучается,чтоестькусоккода,которыйповторяется.Конечно,егоможнокопироватьизодногоскриптавдругой.Ноэтооченьнеудобно,таккак,привнесенииизмененийвкод,нужнобудетобновитьегововсехфайлах,вкоторыеонскопирован.
Гораздопрощеиправильнейвынестиэтоткодвфункцию(этоможетбытьинесколькофункций).
Итогда,вэтомфайле,иликаком-тодругом,этафункцияпростобудетиспользоваться.
Вэтомразделерассматриваесяситуациякогдафункциянаходитсявтомжефайле.
АвразделеМодулибудетрассматриватьсякакповторноиспользоватьобъекты,которыенаходятсявдругихскриптах.
7.Функции
169
7.Функции
170
СозданиефункцийСозданиефункции:
функциисоздаютсяспомощьюзарезервированногословаdefзаdefследуютимяфункцииикруглыескобкивнутрискобокмогутуказыватьсяпараметры,которыефункцияпринимаетпослекруглыхскобокидетдвоеточиеисновойстроки,сотступом,идетблоккода,которыйвыполняетфункцияпервойстрокой,опционально,можетбытькомментарий,такназываемаяdocstringвфункцияхможетиспользоватьсяоператорreturn
ониспользуетсядляпрекращенияработыфункцииивыходаизнеечащевсего,операторreturnвозвращаеткакое-тозначение
Кодфункций,которыеиспользуютсявэтомразделе,можноскопироватьизфайлаcreate_func.py
Примерфункции:
In[1]:defopen_file(filename):
...:"""Documentationstring"""
...:withopen(filename)asf:
...:printf.read()
...:
Когдафункциясоздана,онаещёничегоневыполняет.Толькопривызывефункции,действия,которыевнейперечислены,будутвыполняться.Эточем-топохоженаACLвсетевомоборудовании:присозданииACLвконфигурации,онничегонеделаетдотехпор,поканебудеткуда-топрименен.
Вызовфункции
Привызовефункции,нужноуказатьеёимяипередатьаргументы,еслинужно.
Параметры-этопеременные,которыеиспользуются,присозданиифункции.
Аргументы-этофактическиезначения(данные),которыепередаютсяфункции,привызове.
Этафункцияожидаетимяфайла,вкачествеаргумента,изатемвыводитсодержимоефайла:
Созданиефункций
171
In[2]:open_file('r1.txt')
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
In[3]:open_file('ospf.txt')
routerospf1
router-id10.0.0.3
auto-costreference-bandwidth10000
network10.0.1.00.0.0.255area0
network10.0.2.00.0.0.255area2
network10.1.1.00.0.0.255area0
Перваястрокавопределениифункции-этоdocstring,строкадокументации.Этокомментарий,которыйиспользуетсякакописаниефункции.Егоможноотобразитьтак:
In[4]:open_file.__doc__
Out[4]:'Documentationstring'
Лучшенеленитьсяписатькраткиекомментарии,которыеописываютработуфункции.Например,описать,чтофункцияожидаетнавход,какоготипадолжныбытьаргументыичтобудетнавыходе.Крометого,лучшенаписатьпарупредложенийотом,чтоделаетфункция.Этооченьпоможет,когдачерезмесяц-двавыбудетепытатьсяпонять,чтоделаетфункция,которуювыженаписали.
Операторreturn
Операторreturnиспользуетсядляпрекращенияработыфункции,выходаизнее,и,какправило,возвратакакого-тозначения.ФункцияможетвозвращатьлюбойобъектPython.
Функцияopen_file,впримеревыше,простовыводитнастандартныйпотоквыводасодержимоефайла.Но,чащевсего,отфункциинужнополучитьрезультатеёработы.
Вданномслучае,еслиприсвоитьвыводфункциипеременнойresult,результатбудеттаким:
Созданиефункций
172
In[5]:result=open_file('ospf.txt')
routerospf1
router-id10.0.0.3
auto-costreference-bandwidth10000
network10.0.1.00.0.0.255area0
network10.0.2.00.0.0.255area2
network10.1.1.00.0.0.255area0
In[6]:printresult
None
ПеременнаяresultравнаNone.Такполучилосьиз-затого,чтофункцияничегоневозвращает.Онапростовыводитсообщениенастандартныйпотоквывода.
Длятого,чтобыфункциявозвращалазначение,котороепотомможно,например,присвоитьпеременной,используетсяоператорreturn:
In[7]:defopen_file(filename):
...:"""Documentationstring"""
...:withopen(filename)asf:
...:returnf.read()
...:
In[8]:result=open_file('r1.txt')
In[9]:printresult
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
Теперьвпеременойresultнаходитсясодержимоефайла.
Вреальнойжизни,практическивсегда,функциябудетвозвращатькакое-тозначение.Вместестем,можноиспользоватьвыражениеprint,чтобыдополнительновыводитькакие-тосообщения.
Ещёодинважныйаспектработыоператораreturn:выражения,которыеидутпослеreturn,невыполняются.
Тоесть,вфункцииниже,строка"Done"небудетвыводиться,таккаконастоитпослеreturn:
Созданиефункций
173
In[10]:defopen_file(filename):
...:print"Readingfile",filename
...:withopen(filename)asf:
...:returnf.read()
...:print"Done"
...:
In[11]:result=open_file('r1.txt')
Readingfiler1.txt
Созданиефункций
174
Пространстваимен.ОбластивидимостиУпеременныхвPythonестьобластьвидимости.Взависимостиотместавкоде,гдепеременнаябылаопределена,определяетсяиобластьвидимости,тоесть,гдепеременнаябудетдоступна.
Прииспользованииименпеременныхвпрограмме,Pythonкаждыйраз,ищет,создаетилиизменяетэтиименавсоответствующемпространствеимен.Пространствоимен,котороедоступновкаждыймомент,зависитотобластивкоторойнаходитсякод.
УPythonестьправилоLEGB,которымонпользуетсяприпоискепеременных.
Например,есливнутрифункции,выполняетсяобращениекименипеременной,Pythonищетпеременнуювтакомпорядкепообластямвидимости(допервогосовпадения):
L(local)-влокальной(внутрифункции)E(enclosing)-влокальнойобластиобъемлющихфункций(этотефункции,внутрикоторыхнаходитсянашафункция)G(global)-вглобальной(вскрипте)B(built-in)-ввстроенной(зарезервированныезначенияPython)
Соответственноестьлокальныеиглобальныепеременные:
локальныепеременные:переменные,которыеопределенывнутрифункцииэтипеременныестановятсянедоступнымипослевыходаизфункции
глобальныепеременныепеременные,которыеопределенывнефункцииэтипеременные'глобальны'тольковпределахмодуля
например,чтобыонибылидоступнывдругоммодуле,ихнадоимпортировать
Примерлокальнойиглобальнойпеременнойresult:
Пространстваимен.Областивидимости
175
In[1]:result='teststring'
In[2]:defopen_file(filename):
...:withopen(filename)asf:
...:result=f.read()
...:returnresult
...:
In[3]:open_file('r1.txt')
Out[3]:'!\nservicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear\nservi
cetimestampslogdatetimemseclocaltimeshow-timezoneyear\nservicepassword-encrypt
ion\nservicesequence-numbers\n!\nnoipdomainlookup\n!\nipsshversion2\n!\n'
In[4]:result
Out[4]:'teststring'
Обратитевнимание,чтопеременнаяresultпо-прежнемуосталасьравной'teststring'.Несмотрянато,чтовнутрифункцииейприсвоеносодержимоефайла.
Пространстваимен.Областивидимости
176
ПараметрыиаргументыфункцийЦельсозданияфункции,какправило,заключаетсявтом,чтобывынестикусоккода,которыйвыполняетопределеннуюзадачу,вотдельныйобъект.Этопозволяетиспользоватьэтоткусоккодамногократно,несоздаваяегозанововпрограмме.
Какправило,функциядолжнавыполнятькакие-тодействиясвходящимизначениямиинавыходевыдаватьрезультат.
Приработесфункциями,важноразличать:
параметры-этопеременные,которыеиспользуются,присозданиифункции.аргументы-этофактическиезначения(данные),которыепередаютсяфункции,привызове.
Кодфункций,которыеиспользуютсявэтомразделе,можноскопироватьизфайлаfunc_params_args.py
Длятогочтобыфункциямоглаприниматьвходящиезначения,еенужносоздатьспараметрами:
In[1]:defdelete_exclamation_from_cfg(in_cfg,out_cfg):
...:withopen(in_cfg)asin_file:
...:result=in_file.readlines()
...:withopen(out_cfg,'w')asout_file:
...:forlineinresult:
...:ifnotline.startswith('!'):
...:out_file.write(line)
...:
Вданномслучае,уфункцииdelete_exclamation_from_cfgдвапараметра:in_cfgиout_cfg.
Функцияоткрываетфайлin_cfg,читаетсодержимоевсписок;затемоткрываетфайлout_cfgизаписываетвнеготолькотестроки,которыененачинаютсяназнаквосклицания.
Вданномслучаефункцияничегоневозвращает.
Файлr1.txtбудетиспользоватьсякакпервыйаргумент(in_cfg):
Параметрыиаргументыфункций
177
In[2]:catr1.txt
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
Примериспользованияфункцииdelete_exclamation_from_cfg:
In[3]:delete_exclamation_from_cfg('r1.txt','result.txt')
Файлresult.txtвыглядиттак:
In[4]:catresult.txt
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
noipdomainlookup
ipsshversion2
Притакомопределениифункции,надообязательнопередатьобааргумента.Еслипередатьтолькоодинаргумент,возникнетошибка.Аналогично,возникнетошибка,еслипередатьтриибольшеаргументов:
In[5]:delete_exclamation_from_cfg('r1.txt')
---------------------------------------------------------------------------
TypeErrorTraceback(mostrecentcalllast)
<ipython-input-7-66ae381f1c4f>in<module>()
---->1delete_exclamation_from_cfg('r1.txt')
TypeError:delete_exclamation_from_cfg()takesexactly2arguments(1given)
Параметрыиаргументыфункций
178
ТипыпараметровфункцииПрисозданиифункции,можноуказать,какиеаргументынужнопередаватьобязательно,акакиенет.
Соответственно,функцияможетбытьсозданаспараметрами:
обязательныминеобязательными(опциональными,параметрамисозначениемпоумолчанию)
Кодфункций,которыеиспользуютсявэтомразделе,можноскопироватьизфайлаfunc_params_types.py
Обязательныепараметры
Обязательныепараметры-определяюткакиеаргументынужнопередатьфункцииобязательно.Приэтом,ихнужнопередатьровностолько,сколькоуказанопараметровфункции(нельзяуказатьбольшееилименьшееколичествоаргументов)
Функциясобязательнымипараметрами:
In[1]:defcfg_to_list(cfg_file,delete_exclamation):
....:result=[]
....:withopen(cfg_file)asf:
....:forlineinf:
....:ifdelete_exclamationandline.startswith('!'):
....:pass
....:else:
....:result.append(line.rstrip())
....:returnresult
Функцияcfg_to_listожидаетдвааргумента:cfg_fileиdelete_exclamation.
Внутри,онаоткрываетфайлcfg_fileдлячтения,проходитсяповсемстроками,еслиаргументdelete_exclamationистинаистроканачинаетсясвосклицательногознака,строкапропускается.Операторpassозначает,чтоничегоневыполняется.
Вовсехостальныхслучаях,встрокесправаудаляютсясимволыпереводастрокиистрокадобавляетсявсловарьresult.
Примервызовафункции:
Типыпараметров
179
In[2]:cfg_to_list('r1.txt',True)
Out[2]:
['servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
'servicepassword-encryption',
'servicesequence-numbers',
'noipdomainlookup',
'ipsshversion2']
Таккакаргументуdelete_exclamationпереданозначениеTrue,витоговомсловаренетстроксвосклицательнымизнаками.
Вызовфункции,созначениемFalseдляаргументаdelete_exclamation:
In[3]:cfg_to_list('r1.txt',False)
Out[3]:
['!',
'servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
'servicepassword-encryption',
'servicesequence-numbers',
'!',
'noipdomainlookup',
'!',
'ipsshversion2',
'!']
Необязательныепараметры(параметрысозначениемпоумолчанию)
Присозданиифункции,можноуказыватьзначениепоумолчаниюдляпараметра:
In[4]:defcfg_to_list(cfg_file,delete_exclamation=True):
....:result=[]
....:withopen(cfg_file)asf:
....:forlineinf:
....:ifdelete_exclamationandline.startswith('!'):
....:pass
....:else:
....:result.append(line.rstrip())
....:returnresult
....:
Таккактеперьупараметраdelete_exclamationзначениепоумолчаниюравноTrue,соответствующийаргументможнонеуказыватьпривызовефункции,еслизначениепоумолчаниюподходит:
Типыпараметров
180
In[5]:cfg_to_list('r1.txt')
Out[5]:
['servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
'servicepassword-encryption',
'servicesequence-numbers',
'noipdomainlookup',
'ipsshversion2']
Но,можноиуказать,еслинужнопоменятьзначениепоумолчанию:
In[6]:cfg_to_list('r1.txt',False)
Out[6]:
['!',
'servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
'servicepassword-encryption',
'servicesequence-numbers',
'!',
'noipdomainlookup',
'!',
'ipsshversion2',
'!']
Типыпараметров
181
ТипыаргументовфункцииПривызовефункцииаргументыможнопередаватьдвумяспособами:
какпозиционные-передаютсявтомжепорядке,вкоторомониопределены,присозданиифункции.Тоесть,порядокпередачиаргументов,определяеткакоезначениеполучиткаждыйкакключевые-передаютсясуказаниемимениаргументаиегозначения.Втакомслучае,аргументымогутбытьуказанывлюбомпорядке,таккакихимяуказываетсяявно.
Позицонныеиключевыеаргументымогутбытьсмешаны,привызовефункции.Тоесть,можноиспользоватьобаспособа,припередачеаргументоводнойитойжефункции.Приэтом,сначаладолжныидтипозиционныеаргументы,атолькопотом-ключевые.
Кодфункций,которыеиспользуютсявэтомразделе,можноскопироватьизфайлаfunc_args_types.py
Посмотримнаразныеспособыпередачиаргументов,напримерефункции:
In[1]:defcfg_to_list(cfg_file,delete_exclamation):
....:result=[]
....:withopen(cfg_file)asf:
....:forlineinf:
....:ifdelete_exclamationandline.startswith('!'):
....:pass
....:else:
....:result.append(line.rstrip())
....:returnresult
....:
Позиционныеаргументы
Позиционныеаргументы,привызовефункции,надопередатьвправильномпорядке(поэтомуонииназываютсяпозиционные)
Типыаргументов
182
In[2]:cfg_to_list('r1.txt',False)
Out[2]:
['!',
'servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
'servicepassword-encryption',
'servicesequence-numbers',
'!',
'noipdomainlookup',
'!',
'',
'',
'ipsshversion2',
'!']
Еслипривызовефункциипоменятьаргументыместами,скореевсего,возникнетошибка,взависимостиотконкретнойфункции.
Вслучаесфункциейcfg_to_list,получитсятакойрезультат:
In[3]:cfg_to_list(False,'r1.txt')
---------------------------------------------------------------------------
TypeErrorTraceback(mostrecentcalllast)
<ipython-input-18-e6da7e2657eb>in<module>()
---->1cfg_to_list(False,'r1.txt')
<ipython-input-15-21a013e5e92c>incfg_to_list(cfg_file,delete_exclamation)
1defcfg_to_list(cfg_file,delete_exclamation):
2result=[]
---->3withopen(cfg_file)asf:
4forlineinf:
5ifdelete_exclamationandline.startswith('!'):
TypeError:coercingtoUnicode:needstringorbuffer,boolfound
Ключевыеаргументы
Ключевыеаргументы:
передаютсясуказаниемимениаргументазасчетэтого,онимогутпередаватьсявлюбомпорядке
Еслипередатьобааргумента,какключевые,можнопередаватьихвлюбомпорядке:
Типыаргументов
183
In[4]:cfg_to_list(delete_exclamation=False,cfg_file='r1.txt')
Out[4]:
['!',
'servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
'servicepassword-encryption',
'servicesequence-numbers',
'!',
'noipdomainlookup',
'!',
'ipsshversion2',
'!']
Но,обратитевнимание,чтовсегдасначаладолжныидтипозиционныеаргументы,азатемключевые.
Еслисделатьнаоборот,возникнетошибка:
In[5]:cfg_to_list(delete_exclamation=False,'r1.txt')
File"<ipython-input-19-5efdee7ce6dd>",line1
cfg_to_list(delete_exclamation=False,'r1.txt')
SyntaxError:non-keywordargafterkeywordarg
Новтакойкомбинацииможно:
In[6]:cfg_to_list('r1.txt',delete_exclamation=True)
Out[6]:
['servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
'servicepassword-encryption',
'servicesequence-numbers',
'noipdomainlookup',
'ipsshversion2']
Вреальнойжизни,зачастуюнамногопонятнейиудобнейуказыватьфлаги,такиекакdelete_exclamation,какключевойаргумент.Еслизадатьхорошееназваниепараметра,засчетуказанияегоимени,сразубудетпонятно,чтоименноделаетэтотаргумент.
Например,вфункцииcfg_to_list,понятно,чтоаргументdelete_exclamationприводиткудалениювосклицательныхзнаков.
Типыаргументов
184
АргументыпеременнойдлиныИногда,необходимосделатьтак,чтобыфункцияпринималанефиксированноеколичествоаргументов,алюбое.Длятакогослучая,вPythonможносоздаватьфункциюсоспециальнымпараметром,которыйпринимаетаргументыпеременнойдлины.Такойпараметрможетбыть,какключевым,такипозиционным.
Дажеесливынебудетеиспользоватьэтотприемвсвоихскриптах,естьбольшаявероятность,что,вывстретитееговчужомкоде.
Позиционныеаргументыпеременнойдлины
Параметр,которыйпринимаетпозиционныеаргументыпеременнойдлины,создаетсядобавлениемпередименемпараметразвездочки.Имяпараметраможетбытьлюбым,но,подоговоренности,чащевсего,используютимя*args
Примерфункции:
In[1]:defsum_arg(a,*args):
....:printa,args
....:returna+sum(args)
....:
Функцияsum_argсозданасдвумяпараметрами:
параметрaеслипередаетсякакпозиционныйаргумент,долженидтипервымеслипередаетсякакключевойаргумент,топорядокневажен
параметр*args-ожидаетаргументыпеременнойдлинысюдапопадутвсеостальныеаргументыввидекортежаэтиаргументымогутотсутствовать
Вызовфункциюсразнымколичествомаргументов:
Аргументыпеременнойдлины
185
In[2]:sum_arg(1,10,20,30)
1(10,20,30)
Out[2]:61
In[3]:sum_arg(1,10)
1(10,)
Out[3]:11
In[4]:sum_arg(1)
1()
Out[4]:1
Можносоздатьитакуюфункцию:
In[5]:defsum_arg(*args):
....:printarg
....:returnsum(arg)
....:
In[6]:sum_arg(1,10,20,30)
(1,10,20,30)
Out[6]:61
In[7]:sum_arg()
()
Out[7]:0
Ключевыеаргументыпеременнойдлины
Параметр,которыйпринимаетключевыеаргументыпеременнойдлины,создаетсядобавлениемпередименемпараметрадвухзвездочек.Имяпараметраможетбытьлюбым,но,подоговоренности,чащевсего,используютимя**kwargs(отkeywordarguments).
Примерфункции:
In[8]:defsum_arg(a,**kwargs):
....:printa,kwargs
....:returna+sum(kwargs.values())
....:
Функцияsum_argсозданасдвумяпараметрами:
параметрaеслипередаетсякакпозиционныйаргумент,долженидтипервымеслипередаетсякакключевойаргумент,топорядокневажен
Аргументыпеременнойдлины
186
параметр**kwargs-ожидаетключевыеаргументыпеременнойдлинысюдапопадутвсеостальныеключевыеаргументыввидесловаряэтиаргументымогутотсутствовать
Вызовфункциюсразнымколичествомключевыхаргументов:
In[9]:sum_arg(a=10,b=10,c=20,d=30)
10{'c':20,'b':10,'d':30}
Out[9]:70
In[10]:sum_arg(b=10,c=20,d=30,a=10)
10{'c':20,'b':10,'d':30}
Out[10]:70
Обратитевнимание,что,хотяaможноуказыватькакпозиционныйаргумент,нельзяуказыватьпозиционныйаргументпослеключевого:
In[11]:sum_arg(10,b=10,c=20,d=30)
10{'c':20,'b':10,'d':30}
Out[11]:70
In[12]:sum_arg(b=10,c=20,d=30,10)
File"<ipython-input-6-71c121dc2cf7>",line1
sum_arg(b=10,c=20,d=30,10)
SyntaxError:non-keywordargafterkeywordarg
Аргументыпеременнойдлины
187
РаспаковкааргументовВPython,выражения*argsи**kwargsпозволяютвыполнятьещёоднузадачу-распаковкуаргументов.
Досихпор,мывызываливсефункциивручную.И,соответственно,передаваливсенужныеаргументы.
Но,вреальнойжизни,какправило,данныенеобходимопередаватьвфункциюпрограммно.Ичастоданныеидутввидекакого-тообъектаPython.
Распаковкапозиционныхаргументов
Дляпримера,используемфункциюconfig_interface(файлfunc_args_var_unpacking.py):
defconfig_interface(intf_name,ip_address,cidr_mask):
interface='interface%s'
no_shut='noshutdown'
ip_addr='ipaddress%s%s'
result=[]
result.append(interface%intf_name)
result.append(no_shut)
mask_bits=int(cidr_mask.split('/')[-1])
bin_mask='1'*mask_bits+'0'*(32-mask_bits)
dec_mask='.'.join([str(int(bin_mask[i:i+8],2))foriin[0,8,16,24]])
result.append(ip_addr%(ip_address,dec_mask))
returnresult
Функцияожидаеткакаргумент:
intf_name-имяинтерфейсаip_address-IP-адресcidr_mask-маскувформатеCIDR(допускаетсяиформат/24ипросто24)
Навыходе,онавыдаетсписокстрок,длянастройкиинтерфейса.
Например:
Распаковкааргументов
188
In[1]:config_interface('Fa0/1','10.0.1.1','/25')
Out[1]:['interfaceFa0/1','noshutdown','ipaddress10.0.1.1255.255.255.128']
In[2]:config_interface('Fa0/3','10.0.0.1','/18')
Out[2]:['interfaceFa0/3','noshutdown','ipaddress10.0.0.1255.255.192.0']
In[3]:config_interface('Fa0/3','10.0.0.1','/32')
Out[3]:['interfaceFa0/3','noshutdown','ipaddress10.0.0.1255.255.255.255']
In[4]:config_interface('Fa0/3','10.0.0.1','/30')
Out[4]:['interfaceFa0/3','noshutdown','ipaddress10.0.0.1255.255.255.252']
In[5]:config_interface('Fa0/3','10.0.0.1','30')
Out[5]:['interfaceFa0/3','noshutdown','ipaddress10.0.0.1255.255.255.252']
Допустим,теперьнужновызватьфункциюипередатьейинформацию,котораябылаполученаиздругогоисточника,напримеризБД.
Например,списокinterfaces_info,вкоторомнаходятсяпараметрыдлянастройкиинтерфейсов:
In[6]:interfaces_info=[['Fa0/1','10.0.1.1','/24'],
....:['Fa0/2','10.0.2.1','/24'],
....:['Fa0/3','10.0.3.1','/24'],
....:['Fa0/4','10.0.4.1','/24'],
....:['Lo0','10.0.0.1','/32']]
Еслипройтисьпоспискувциклеипередаватьвложенныйсписок,какаргументфункции,возникнетошибка:
In[7]:forinfoininterfaces_info:
....:printconfig_interface(info)
....:
---------------------------------------------------------------------------
TypeErrorTraceback(mostrecentcalllast)
<ipython-input-32-fb83ecc1fbcf>in<module>()
1forinfoininterfaces_info:
---->2printconfig_interface(info)
3
TypeError:config_interface()takesexactly3arguments(1given)
Ошибкавполнелогичная:функцияожидаеттриаргумента,аейпередан1аргумент-список.
Втакойситуации,пригодитсяраспаковкааргументов.Достаточнодобавить*передпередачейсписка,какаргумента,иошибкиуженебудет:
Распаковкааргументов
189
In[8]:forinfoininterfaces_info:
....:printconfig_interface(*info)
....:
['interfaceFa0/1','noshutdown','ipaddress10.0.1.1255.255.255.0']
['interfaceFa0/2','noshutdown','ipaddress10.0.2.1255.255.255.0']
['interfaceFa0/3','noshutdown','ipaddress10.0.3.1255.255.255.0']
['interfaceFa0/4','noshutdown','ipaddress10.0.4.1255.255.255.0']
['interfaceLo0','noshutdown','ipaddress10.0.0.1255.255.255.255']
Pythonсам'распакует'списокinfoипередаствфункциюэлементысписка,какаргументы.
Такимжеобразомможнораспаковыватьикортеж.
Распаковкаключевыхаргументов
Аналогичнымобразом,можнораспаковыватьсловарь,чтобыпередатьегокакключевыеаргументы.
Функцияconfig_to_list:
defconfig_to_list(cfg_file,delete_excl=True,
delete_empty=True,strip_end=True):
result=[]
withopen(cfg_file)asf:
forlineinf:
ifstrip_end:
line=line.rstrip()
ifdelete_emptyandnotline:
pass
elifdelete_exclandline.startswith('!'):
pass
else:
result.append(line)
returnresult
Функцияберетфайлсконфигурацией,убираетчастьстрокивозвращаетостальныестрокикаксписок.
Веськодфункцииможновставитьвipythonспомощьюкоманды%cpaste.
Примериспользования:
Распаковкааргументов
190
In[9]:config_to_list('r1.txt')
Out[9]:
['servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
'servicepassword-encryption',
'servicesequence-numbers',
'noipdomainlookup',
'ipsshversion2']
Списоксловарейcfg,вкоторыхуказаноимяфайлаивсеаргументы:
In[10]:cfg=[dict(cfg_file='r1.txt',delete_excl=True,delete_empty=True,strip_end=
True),
....:dict(cfg_file='r2.txt',delete_excl=False,delete_empty=True,strip_en
d=True),
....:dict(cfg_file='r3.txt',delete_excl=True,delete_empty=False,strip_en
d=True),
....:dict(cfg_file='r4.txt',delete_excl=True,delete_empty=True,strip_end=
False)]
Еслипередатьсловарьфункцииconfig_to_list,возникнетошибка:
In[11]:fordincfg:
....:printconfig_to_list(d)
....:
---------------------------------------------------------------------------
TypeErrorTraceback(mostrecentcalllast)
<ipython-input-40-1affbd99c2f5>in<module>()
1fordincfg:
---->2printconfig_to_list(d)
3
<ipython-input-35-6337ba2bfe7a>inconfig_to_list(cfg_file,delete_excl,delete_empty,
strip_end)
2delete_empty=True,strip_end=True):
3result=[]
---->4withopen(cfg_file)asf:
5forlineinf:
6ifstrip_end:
TypeError:coercingtoUnicode:needstringorbuffer,dictfound
Ошибкатакая,таккаквсепараметры,кромеименифайла,опциональны.Инастадииоткрытияфайла,возникаетошибка,таккаквместофайла,передансловарь.
Еслидобавить**передпередачейсловаряфункции,функциянормальноотработает:
Распаковкааргументов
191
In[12]:fordincfg:
...:printconfig_to_list(**d)
...:
['servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear','servicetimes
tampslogdatetimemseclocaltimeshow-timezoneyear','servicepassword-encryption',
'servicesequence-numbers','noipdomainlookup','ipsshversion2']
['!','servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear','service
timestampslogdatetimemseclocaltimeshow-timezoneyear','servicepassword-encrypti
on','servicesequence-numbers','!','noipdomainlookup','!','ipsshversion2',
'!']
['servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear','servicetimes
tampslogdatetimemseclocaltimeshow-timezoneyear','servicepassword-encryption',
'servicesequence-numbers','','','','ipsshversion2','']
['servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear\n','servicetim
estampslogdatetimemseclocaltimeshow-timezoneyear\n','servicepassword-encryptio
n\n','servicesequence-numbers\n','noipdomainlookup\n','ipsshversion2\n']
Pythonраспаковываетсловарьипередаетеговфункциюкакключевыеаргументы.
Распаковкааргументов
192
Примериспользованияключевыхаргументовпеременнойдлиныираспаковкиаргументов
Спомощьюаргументовпеременнойдлиныираспаковкиаргументов,можнопередаватьаргументымеждуфункциями.Посмотримнапримере.
Функцияconfig_to_list(файлkwargs_example.py):
defconfig_to_list(cfg_file,delete_excl=True,
delete_empty=True,strip_end=True):
result=[]
withopen(cfg_file)asf:
forlineinf:
ifstrip_end:
line=line.rstrip()
ifdelete_emptyandnotline:
pass
elifdelete_exclandline.startswith('!'):
pass
else:
result.append(line)
returnresult
Веськодфункцииможновставитьвipythonспомощьюкоманды%cpaste.
Функцияберетфайлсконфигурацией,убираетчастьстрокивозвращаетостальныестрокикаксписок.
Вызовфункциивipython:
In[1]:config_to_list('r1.txt')
Out[1]:
['servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
'servicepassword-encryption',
'servicesequence-numbers',
'noipdomainlookup',
'ipsshversion2']
Поумолчанию,изконфигурацииубираютсяпустыестроки,переводстрокивконцестрокистроки,которыеначинаютсяназнаквосклицания.
Вызовфункциисозначениемdelete_empty=False:
Примериспользования
193
In[2]:config_to_list('r1.txt',delete_empty=False)
Out[2]:
['servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear',
'servicetimestampslogdatetimemseclocaltimeshow-timezoneyear',
'servicepassword-encryption',
'servicesequence-numbers',
'noipdomainlookup',
'',
'',
'ipsshversion2']
Теперьпустыестрокипоявилисьвсписке.
Сделаем'оберточную'функциюclear_cfg_and_write_to_file,котораяберетфайлконфигурации,спомощьюфункцииconfig_to_list,удаляетлишниестрокиизатемзаписываетстрокивуказанныйфайл.
Но,приэтом,мынехотимтерятьвозможностьуправлятьтем,какиестрокибудутотброшены.Тоесть,необходимочтобыфункцияclear_cfg_and_write_to_fileподдерживалатежепараметры,чтоифункцияconfig_to_list.
Конечно,можнопростопродублироватьвсепараметрыфункцииипередатьихвфункциюconfig_to_list:
defclear_cfg_and_write_to_file(cfg,to_file,delete_excl=True,
delete_empty=True,strip_end=True):
cfg_as_list=config_to_list(cfg,delete_excl=delete_excl,
delete_empty=delete_empty,strip_end=strip_end)
withopen(to_file,'w')asf:
f.write('\n'.join(cfg_as_list))
Но,есливоспользоватьсявозможностьюPythonприниматьаргументыпеременнойдлины,можносделатьфункциюclear_cfg_and_write_to_fileтакой:
defclear_cfg_and_write_to_file(cfg,to_file,**kwargs):
cfg_as_list=config_to_list(cfg,**kwargs)
withopen(to_file,'w')asf:
f.write('\n'.join(cfg_as_list))
Вфункцииclear_cfg_and_write_to_fileявнопрописаныеёаргументы,авсёостальноепопадетвпеременнуюkwargs.Затемпеременнаяkwargsпередается,какаргумент,вфункциюconfig_to_list.Но,таккакпеременнаяkwargsэтословарь,еёнадораспаковать,припередачефункцииconfig_to_list.
Примериспользования
194
Такфункцияclear_cfg_and_write_to_fileвыглядитпрощеипонятней.И,главное,втакомварианте,вфункциюconfig_to_listможнодобавлятьаргументы,безнеобходимостидублироватьихвфункцииclear_cfg_and_write_to_file.
Вэтомпримере,**kwargsиспользуетсяидлятого,чтобыуказать,чтофункцияclear_cfg_and_write_to_fileможетприниматьаргументыпеременнойдлины,идлятого,чтобы'распаковать'словарьkwargs,когдамыпередаемеговфункциюconfig_to_list.
Примериспользования
195
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание7.1
Создатьфункцию,котораягенерируетконфигурациюдляaccess-портов.
Параметрaccessожидает,какаргумент,словарьaccess-портов,вида:
{'FastEthernet0/12':10,
'FastEthernet0/14':11,
'FastEthernet0/16':17,
'FastEthernet0/17':150}
Функциядолжнавозвращатьсписоквсехпортовврежимеaccessсконфигурациейнаосновешаблонаaccess_template.
Вконцестроквспискенедолжнобытьсимволапереводастроки.
Примеритоговогосписка:
Задания
196
[
'interfaceFastEthernet0/12',
'switchportmodeaccess',
'switchportaccessvlan10',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable',
'interfaceFastEthernet0/17',
'switchportmodeaccess',
'switchportaccessvlan150',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable',
...]
Проверитьработуфункциинапримересловаряaccess_dict.
defgenerate_access_config(access):
"""
access-словарьaccess-портов,
длякоторыхнеобходимосгенерироватьконфигурацию,вида:
{'FastEthernet0/12':10,
'FastEthernet0/14':11,
'FastEthernet0/16':17}
Возвращаетсписоквсехпортовврежимеaccessсконфигурациейнаосновешаблона
"""
access_template=['switchportmodeaccess',
'switchportaccessvlan',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
access_dict={'FastEthernet0/12':10,
'FastEthernet0/14':11,
'FastEthernet0/16':17,
'FastEthernet0/17':150}
Задание7.1a
Сделатькопиюскриптазадания7.1.
Дополнитьскрипт:
ввестидополнительныйпараметр,которыйконтролируетбудетлинастроенport-security
имяпараметра'psecurity'
Задания
197
поумолчаниюзначениеFalse
Проверитьработуфункциинапримересловаряaccess_dict,сгенерациейконфигурацииport-securityибез.
defgenerate_access_config(access):
"""
access-словарьaccess-портов,
длякоторыхнеобходимосгенерироватьконфигурацию,вида:
{'FastEthernet0/12':10,
'FastEthernet0/14':11,
'FastEthernet0/16':17}
psecurity-контролируетнужналинастройкаPortSecurity.ПоумолчаниюзначениеF
alse
-еслизначениеTrue,тонастройкавыполняетсясдобавлениемшаблонаport_secu
rity
-еслизначениеFalse,тонастройканевыполняется
Возвращаетсписоквсехкоманд,которыебылисгенерированынаосновешаблона
"""
access_template=['switchportmodeaccess',
'switchportaccessvlan',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
port_security=['switchportport-securitymaximum2',
'switchportport-securityviolationrestrict',
'switchportport-security']
access_dict={'FastEthernet0/12':10,
'FastEthernet0/14':11,
'FastEthernet0/16':17,
'FastEthernet0/17':150}
Задание7.1b
Сделатькопиюскриптазадания7.1a.
Изменитьскрипттакимобразом,чтобыфункциявозвращаланесписоккоманд,асловарь:
ключи:именаинтерфейсов,вида'FastEthernet0/12'значения:списоккоманд,которыйнадовыполнитьнаэтоминтерфейсе:
Задания
198
['switchportmodeaccess',
'switchportaccessvlan10',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
Проверитьработуфункциинапримересловаряaccess_dict,сгенерациейконфигурацииport-securityибез.
defgenerate_access_config(access):
"""
access-словарьaccess-портов,
длякоторыхнеобходимосгенерироватьконфигурацию,вида:
{'FastEthernet0/12':10,
'FastEthernet0/14':11,
'FastEthernet0/16':17}
psecurity-контролируетнужналинастройкаPortSecurity.ПоумолчаниюзначениеF
alse
-еслизначениеTrue,тонастройкавыполняетсясдобавлениемшаблонаport_secu
rity
-еслизначениеFalse,тонастройканевыполняется
Функциявозвращаетсловарь:
-ключи:именаинтерфейсов,вида'FastEthernet0/1'
-значения:списоккоманд,которыйнадовыполнитьнаэтоминтерфейсе
"""
access_template=['switchportmodeaccess',
'switchportaccessvlan',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
port_security=['switchportport-securitymaximum2',
'switchportport-securityviolationrestrict',
'switchportport-security']
access_dict={'FastEthernet0/12':10,
'FastEthernet0/14':11,
'FastEthernet0/16':17,
'FastEthernet0/17':150}
Задание7.2
Создатьфункцию,котораягенерируетконфигурациюдляtrunk-портов.
Параметрtrunk-этословарьtrunk-портов.
Задания
199
Словарьtrunkимееттакойформат(тестовыйсловарьtrunk_dictужесоздан):
{'FastEthernet0/1':[10,20],
'FastEthernet0/2':[11,30],
'FastEthernet0/4':[17]}
Функциядолжнавозвращатьсписоккомандсконфигурациейнаосновеуказанныхпортовишаблонаtrunk_template.
Вконцестроквспискенедолжнобытьсимволапереводастроки.
Проверитьработуфункциинапримересловаряtrunk_dict.
defgenerate_trunk_config(trunk):
"""
trunk-словарьtrunk-портовдлякоторыхнеобходимосгенерироватьконфигурацию.
Возвращаетсписоквсехкоманд,которыебылисгенерированынаосновешаблона
"""
trunk_template=['switchporttrunkencapsulationdot1q',
'switchportmodetrunk',
'switchporttrunknativevlan999',
'switchporttrunkallowedvlan']
trunk_dict={'FastEthernet0/1':[10,20,30],
'FastEthernet0/2':[11,30],
'FastEthernet0/4':[17]}
Задание7.2a
Сделатькопиюскриптазадания7.2
Изменитьскрипттакимобразом,чтобыфункциявозвращаланесписоккоманд,асловарь:
ключи:именаинтерфейсов,вида'FastEthernet0/1'значения:списоккоманд,которыйнадовыполнитьнаэтоминтерфейсе
Проверитьработуфункциинапримересловаряtrunk_dict.
Задания
200
defgenerate_trunk_config(trunk):
"""
trunk-словарьtrunk-портов,
длякоторыхнеобходимосгенерироватьконфигурацию,вида:
{'FastEthernet0/1':[10,20],
'FastEthernet0/2':[11,30],
'FastEthernet0/4':[17]}
Возвращаетсловарь:
-ключи:именаинтерфейсов,вида'FastEthernet0/1'
-значения:списоккоманд,которыйнадовыполнитьнаэтоминтерфейсе
"""
trunk_template=['switchporttrunkencapsulationdot1q',
'switchportmodetrunk',
'switchporttrunknativevlan999',
'switchporttrunkallowedvlan']
trunk_dict={'FastEthernet0/1':[10,20,30],
'FastEthernet0/2':[11,30],
'FastEthernet0/4':[17]}
Задание7.3
Создатьфункциюget_int_vlan_map,котораяобрабатываетконфигурационныйфайлкоммутатораивозвращаетдваобъекта:
словарьпортовврежимеaccess,гдеключиномерапортов,азначенияaccessVLAN:
{'FastEthernet0/12':10,
'FastEthernet0/14':11,
'FastEthernet0/16':17}
словарьпортовврежимеtrunk,гдеключиномерапортов,азначениясписокразрешенныхVLAN:
{'FastEthernet0/1':[10,20],
'FastEthernet0/2':[11,30],
'FastEthernet0/4':[17]}
Функцияожидаетвкачествеаргументаимяконфигурационногофайла.
Проверитьработуфункциинапримерефайлаconfig_sw1.txt
Задание7.3a
Задания
201
Сделатькопиюскриптазадания7.3.
Дополнитьскрипт:
добавитьподдержкуконфигурации,когданастройкаaccess-портавыглядиттак:
interfaceFastEthernet0/20
switchportmodeaccess
duplexauto
Тоесть,портнаходитсявVLAN1
Втакомслучае,всловарьпортовдолжнадобавлятьсяинформация,чтопортвVLAN1
Примерсловаря:
{'FastEthernet0/12':10,
'FastEthernet0/14':11,
'FastEthernet0/20':1}
Функцияожидаетвкачествеаргументаимяконфигурационногофайла.
Проверитьработуфункциинапримерефайлаconfig_sw2.txt
Задание7.4
Создатьфункцию,котораяобрабатываетконфигурационныйфайлкоммутатораивозвращаетсловарь:
Всекомандыверхнегоуровня(глобальногорежимаконфигурации),будутключами.Еслиукомандыверхнегоуровняестьподкоманды,онидолжныбытьвзначенииусоответствующегоключа,ввидесписка(пробелывначалеможнооставлять).Еслиукомандыверхнегоуровнянетподкоманд,тозначениебудетпустымсписком
Функцияожидаетвкачествеаргументаимяконфигурационногофайла.
Проверитьработуфункциинапримерефайлаconfig_sw1.txt
Приобработкеконфигурационногофайла,надоигнорироватьстроки,которыеначинаютсяс'!',атакжестрокивкоторыхсодержатсясловаизспискаignore.
Дляпроверкинадолиигнорироватьстроку,использоватьфункциюignore_command.
Задания
202
ignore=['duplex','alias','Currentconfiguration']
defignore_command(command,ignore):
"""
Функцияпроверяетсодержитсяливкомандесловоизспискаignore.
command-строка.Команда,которуюнадопроверить
ignore-список.Списокслов
ВозвращаетTrue,есливкомандесодержитсясловоизспискаignore,False-еслине
т
"""
ignore_command=False
forwordinignore:
ifwordincommand:
returnTrue
returnignore_command
defconfig_to_dict(config):
"""
config-имяконфигурационногофайлакоммутатора
Возвращаетсловарь:
-Всекомандыверхнегоуровня(глобальногорежимаконфигурации),будутключами.
-Еслиукомандыверхнегоуровняестьподкоманды,
онидолжныбытьвзначенииусоответствующегоключа,ввидесписка(пробелывнач
алеможнооставлять).
-Еслиукомандыверхнегоуровнянетподкоманд,тозначениебудетпустымсписком
"""
pass
Задание7.4a
Задачатакаяже,какизадании7.4.Проверитьработуфункциинадонапримерефайлаconfig_r1.txt
Обратитевниманиенаконфигурационныйфайл.Внеместьразделысбольшейвложенностью,например,разделы:
interfaceEthernet0/3.100routerbgp100
Надочтобыфункцияconfig_to_dictобрабатываласледующийуровеньвложенности.Приэтом,непривязываяськконкретнымразделам.Онадолжнабытьуниверсальной,исработать,еслиэтобудутдругиеразделы.
Теперь:
Задания
203
еслиуровня2,токомандыверхнегоуровнябудутключамисловаря,акомандыподуровней-списками;еслиуровня3,тосамыйвложенныйдолженбытьсписком,аостальные-словарями.
НапримереinterfaceEthernet0/3.100
{'interfaceEthernet0/3.100':{
'encapsulationdot1Q100':[],
'xconnect10.2.2.212100encapsulationmpls':
['backuppeer10.4.4.414100',
'backupdelay11']}}
ignore=['duplex','alias','Currentconfiguration']
defcheck_ignore(command,ignore):
"""
Функцияпроверяетсодержитсяливкомандесловоизспискаignore.
command-строка.Команда,которуюнадопроверить
ignore-список.Списокслов
ВозвращаетTrue,есливкомандесодержитсясловоизспискаignore,False-еслине
т
"""
ignore_command=False
forwordinignore:
ifwordincommand:
ignore_command=True
returnignore_command
defconfig_to_dict(config):
"""
config-имяконфигурационногофайла
"""
pass
Задания
204
МодулиМодульвPythonэтообычныйтекстовыйфайлскодомPythonирасширением.py.Онпозволяетлогическиупорядочитьисгруппироватькод.
Разделениенамодулиможетбыть,например,потакойлогике:
разделениеданных,форматированияилогикикодагруппировкафункцийидругихобъектовпофункционалу
Модулихорошитем,чтопозволяютповторноиспользоватьуженаписанныйкодинекопироватьего(например,некопироватькогда-тонаписаннуюфункцию).
8.Модули
205
ИмпортмодуляВPythonестьнесколькоспособовимпортамодуля:
importmodule
importmoduleas
frommoduleimportobject
frommoduleimport*
importmodule
Вариантimportmodule:
In[1]:dir()
Out[1]:
['In',
'Out',
...
'exit',
'get_ipython',
'quit']
In[2]:importos
In[3]:dir()
Out[3]:
['In',
'Out',
...
'exit',
'get_ipython',
'os',
'quit']
Послеимпорта,модульosпоявилсяввыводеdir().Этозначит,чтоонтеперьвтекущемименномпространстве.
Чтобывызватькакую-тофункциюилиметодизмодуляos,надоуказатьos.изатемимяобъекта:
In[4]:os.getlogin()
Out[4]:'natasha'
Импортмодуля
206
Этотспособимпортахороштем,чтообъектымодулянепопадаютвименноепространствотекущейпрограммы.Тоесть,еслисоздатьфункциюсименемgetlogin(),онанебудетконфликтоватьсаналогичнойфункциеймодуляos.
Есливименифайласодержитсяточка,стандартныйспособимпортированиянебудетработать.Длятакихслучаев,используетсядругойспособ.
importmoduleas
Конструкцияimportmoduleasпозволяетимпортироватьмодульподдругимименем(какправило,болеекоротким):
In[1]:importsubprocessassp
In[2]:sp.check_output('ping-c2-n8.8.8.8',shell=True)
Out[2]:'PING8.8.8.8(8.8.8.8):56databytes\n64bytesfrom8.8.8.8:icmp_seq=0ttl=
48time=49.880ms\n64bytesfrom8.8.8.8:icmp_seq=1ttl=48time=46.875ms\n\n---8.8.
8.8pingstatistics---\n2packetstransmitted,2packetsreceived,0.0%packetloss\n
round-tripmin/avg/max/stddev=46.875/48.377/49.880/1.503ms\n'
frommoduleimportobject
Вариантfrommoduleimportobjectудобноиспользовать,когдаизвсегомодулянужнытолькоодна-двефункции:
In[1]:fromosimportgetlogin,getcwd
Теперьэтифункциидоступнывтекущемименномпространстве:
In[2]:dir()
Out[2]:
['In',
'Out',
...
'exit',
'get_ipython',
'getcwd',
'getlogin',
'quit']
Ихможновызыватьбезименимодуля:
Импортмодуля
207
In[3]:getlogin()
Out[3]:'natasha'
In[4]:getcwd()
Out[4]:'/Users/natasha/Desktop/Py_net_eng/code_test'
frommoduleimport*
Вариантfrommoduleimport*импортируетвсеименамодулявтекущееименноепространство:
In[1]:fromosimport*
In[2]:dir()
Out[2]:
['EX_CANTCREAT',
'EX_CONFIG',
...
'wait',
'wait3',
'wait4',
'waitpid',
'walk',
'write']
In[3]:len(dir())
Out[3]:218
Вмодулеosоченьмногообъектов,поэтомувыводсокращен.Вконцеуказанадлинаспискаиментекущегоименногопространства.
Такойвариантимпорталучшенеиспользовать.Притакомимпорте,покодунепонятно,чтокакая-тофункциявзятаизмодуляos,например.Этозаметноусложняетпониманиекода.
Импортмодуля
208
СозданиесвоихмодулейТаккакмодульэтопростофайлсрасширение.pyикодомPython,мыможемлегкосоздатьнесколькосвоихмодулей.
Например,разделимскриптизразделаСовмещениеforиifнанесколькочастей:шаблоныпортов,данныеиформированиекомандбудутвразныхфайлах.
Файлsw_int_templates.py:
access_template=['switchportmodeaccess',
'switchportaccessvlan',
'spanning-treeportfast',
'spanning-treebpduguardenable']
trunk_template=['switchporttrunkencapsulationdot1q',
'switchportmodetrunk',
'switchporttrunkallowedvlan']
l3int_template=['noswitchport','ipaddress']
Файлsw_data.py:
sw1_fast_int={
'access':{
'0/12':'10',
'0/14':'11',
'0/16':'17'}}
Совмещаемвсёвместевфайлеgenerate_sw_int_cfg.py:
Созданиесвоихмодулей
209
importsw_int_templates
fromsw_dataimportsw1_fast_int
defgenerate_access_cfg(sw_dict):
result=[]
forintfinsw_dict['access']:
result.append('interfaceFastEthernet'+intf)
forcommandinsw_int_templates.access_template:
ifcommand.endswith('accessvlan'):
result.append('%s%s'%(command,sw_dict['access'][intf]))
else:
result.append('%s'%command)
returnresult
print'\n'.join(generate_access_cfg(sw1_fast_int))
Впервыхдвухстрокахимпортируютсяобъектыиздругихфайлов:
importsw_int_templates-импортвсегоизфайлапримериспользованияодногоизшаблонов:sw_int_templates.access_template
fromsw_dataimportsw1_fast_int-измодуляsw_dataимпортируетсятолькоsw1_fast_int
притакомимпорте,можнонапрямуюобращатьсякимениsw1_fast_int
Результатвыполненияскрипта:
$pythongenerate_sw_int_cfg.py
interfaceFastEthernet0/12
switchportmodeaccess
switchportaccessvlan10
spanning-treeportfast
spanning-treebpduguardenable
interfaceFastEthernet0/14
switchportmodeaccess
switchportaccessvlan11
spanning-treeportfast
spanning-treebpduguardenable
interfaceFastEthernet0/16
switchportmodeaccess
switchportaccessvlan17
spanning-treeportfast
spanning-treebpduguardenable
if__name__=="__main__"
Созданиесвоихмодулей
210
Иногда,скрипт,которыйвысоздали,можетвыполнятьсяисамостоятельно,иможетбытьимпортированкакмодуль,другимскриптом.
Добавимещёодинскрипт,кпредыдущемупримеру,которыйбудетимпортироватьфункциюизфайлаgenerate_sw_int_cfg.py.
Файлsw_cfg_templates.pyсшаблонамиконфигурации:
basic_cfg="""
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
"""
lines_cfg="""
!
linecon0
loggingsynchronous
historysize100
linevty04
loggingsynchronous
historysize100
transportinputssh
!
"""
Вфайлеgenerate_sw_cfg.pyимпортируютсяшаблоныизsw_cfg_templates.pyифункцииизпредыдущихфайлов:
fromsw_dataimportsw1_fast_int
fromgenerate_sw_int_cfgimportgenerate_access_cfg
fromsw_cfg_templatesimportbasic_cfg,lines_cfg
printbasic_cfg
print'\n'.join(generate_access_cfg(sw1_fast_int))
printlines_cfg
Врезультате,должныотобразитьсятакиечастиконфигурации,попорядку:шаблонbasic_cfg,настройкаинтерфейсов,шаблонlines_cfg.
Созданиесвоихмодулей
211
Обратитевнимание,чтоизфайламожноимпортироватьнесколькообъектов:
fromsw_cfg_templatesimportbasic_cfg,lines_cfg
Результатвыполнения:
Созданиесвоихмодулей
212
$pythongenerate_sw_cfg.py
interfaceFastEthernet0/12
switchportmodeaccess
switchportaccessvlan10
spanning-treeportfast
spanning-treebpduguardenable
interfaceFastEthernet0/14
switchportmodeaccess
switchportaccessvlan11
spanning-treeportfast
spanning-treebpduguardenable
interfaceFastEthernet0/16
switchportmodeaccess
switchportaccessvlan17
spanning-treeportfast
spanning-treebpduguardenable
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
interfaceFastEthernet0/12
switchportmodeaccess
switchportaccessvlan10
spanning-treeportfast
spanning-treebpduguardenable
interfaceFastEthernet0/14
switchportmodeaccess
switchportaccessvlan11
spanning-treeportfast
spanning-treebpduguardenable
interfaceFastEthernet0/16
switchportmodeaccess
switchportaccessvlan17
spanning-treeportfast
spanning-treebpduguardenable
!
linecon0
loggingsynchronous
historysize100
linevty04
loggingsynchronous
historysize100
transportinputssh
!
Созданиесвоихмодулей
213
Полученныйвыводнесовсемправильный:передстрокамишаблонаbasic_cfg,идетлишняяконфигурацияинтерфейсов.
Такполучилосьиз-застрокиprintвфайлеgenerate_sw_int_cfg.py:
print'\n'.join(generate_access_cfg(sw1_fast_int))
Когдаскриптимпортируеткакой-томодуль,всё,чтонаходитсявмодуле,выполняется.И,таккаквданномслучае,вфайлеgenerate_sw_int_cfg.pyестьстрокасprint,настандартныйпотоквыводапопадаетрезультатвыполненияэтоговыражения,призапускефайлаgenerate_sw_int_cfg.py.
ВPythonестьспециальныйприем,которыйпозволяетуказать,чтокакой-токоддолженвыполняться,толькокогдафайлзапускаетсянапрямую.
Файлgenerate_sw_int_cfg2.py:
importsw_int_templates
fromsw_dataimportsw1_fast_int
defgenerate_access_cfg(sw_dict):
result=[]
forintfinsw_dict['access']:
result.append('interfaceFastEthernet'+intf)
forcommandinsw_int_templates.access_template:
ifcommand.endswith('accessvlan'):
result.append('%s%s'%(command,sw_dict['access'][intf]))
else:
result.append('%s'%command)
returnresult
if__name__=="__main__":
print'\n'.join(generate_access_cfg(sw1_fast_int))
Обратитевниманиеназапись:
if__name__=="__main__":
print'\n'.join(generate_access_cfg(sw1_fast_int))
Переменная__name__этоспециальнаяпеременная,котораявыставляетсяравной"__main__",еслифайлзапускаетсякакосновнаяпрограмма.Ивыставляетсяравнойименимодуля,еслимодульимпортируется.
Такимобразом,условиеif__name__=="__main__"проверяетбыллифайлзапущеннапрямую.
Созданиесвоихмодулей
214
Изменитевфайлеgenerate_sw_cfg.pyстроку:
fromgenerate_sw_int_cfgimportgenerate_access_cfg
настроку:
fromgenerate_sw_int_cfg2importgenerate_access_cfg
Ипопробуйтезапуститьскрипт:
$pythongenerate_sw_cfg.py
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
interfaceFastEthernet0/12
switchportmodeaccess
switchportaccessvlan10
spanning-treeportfast
spanning-treebpduguardenable
interfaceFastEthernet0/14
switchportmodeaccess
switchportaccessvlan11
spanning-treeportfast
spanning-treebpduguardenable
interfaceFastEthernet0/16
switchportmodeaccess
switchportaccessvlan17
spanning-treeportfast
spanning-treebpduguardenable
!
linecon0
loggingsynchronous
historysize100
linevty04
loggingsynchronous
historysize100
transportinputssh
!
Теперьprintизфайлаgenerate_sw_int_cfg2.pyневыводится.
Созданиесвоихмодулей
215
Созданиесвоихмодулей
216
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание8.1
Создатьотдельныйфайлmy_func.py.
Перенестивнегофункциииззаданий:
7.1или7.1a7.27.3или7.3a
Создатьфайлmain.py:
Походузаданияимпортироватьнужныефункцииизфайлаmy_func.Файлmain.pyдолженожидатькакаргументимяконфигурационногофайлакоммутатора.Имяконфигурационногофайлапередатькакаргументфункцииget_int_vlan_map(иззадания7.3-7.3a)
Навыходефункции,мыдолжныполучитькортеждвухсловарей.Словари,соответственно,надопередатьфункциям:
generate_access_config(иззадания7.1-7.1a)generate_trunk_config(иззадания7.2)
Этифункции,всвоюочередь,возвращаютсписоксострокамиготовойконфигурациикоторуюнадозаписатьвфайлresult.txtввидестандартного
Задания
217
конфига(тоесть,строк)
Задание8.2
Создатьфункциюparse_cdp_neighbors,котораяобрабатываетвыводкомандыshowcdpneighbors.
Функцияожидает,какаргумент,выводкомандыоднойстрокой.
Функциядолжнавозвращатьсловарь,которыйописываетсоединениямеждуустройствами.
Например,есликакаргументбылпередантакойвывод:
R4>showcdpneighbors
DeviceIDLocalIntrfceHoldtmeCapabilityPlatformPortID
R5Fa0/1122RSI2811Fa0/1
R6Fa0/2143RSI2811Fa0/0
Функциядолжнавернутьтакойсловарь:
{('R4','Fa0/1'):('R5','Fa0/1'),
('R4','Fa0/2'):('R6','Fa0/0')}
Проверитьработуфункциинасодержимомфайлаsw1_sh_cdp_neighbors.txt
Задание8.2a
Длявыполненияэтогозадания,долженбытьустановленgraphviz:
apt-getinstallgraphviz
Имодульpythonдляработысgraphviz:
pipinstallgraphviz
Спомощьюфункцииparse_cdp_neighborsиззадания8.2ифункцииdraw_topologyизфайлаdraw_network_graph.py,сгенерироватьтопологию,котораясоответствуетвыводукомандыshcdpneighborвфайлеsw1_sh_cdp_neighbors.txt
Некопироватькодфункцийparse_cdp_neighborsиdraw_topology.
Витоге,долженбытьсгенерированоизображениетопологии.Результатдолженвыглядетьтакже,каксхемавфайлеtask_8_2a_topology.svg
Задания
218
Задание8.2b
Длявыполненияэтогозадания,долженбытьустановленgraphviz:
apt-getinstallgraphviz
Имодульpythonдляработысgraphviz:
pipinstallgraphviz
Спомощьюфункцииparse_cdp_neighborsиззадания8.2ифункцииdraw_topologyизфайлаdraw_network_graph.py,сгенерироватьтопологию,котораясоответствуетвыводукомандыshcdpneighborизфайлов:
sh_cdp_n_sw1.txtsh_cdp_n_r1.txtsh_cdp_n_r2.txtsh_cdp_n_r3.txt
Некопироватькодфункцийparse_cdp_neighborsиdraw_topology.
Витоге,долженбытьсгенерированоизображениетопологии.Результатдолженвыглядетьтакже,каксхемавфайлеtask_8_2b_topology.svg
Задания
219
Задания
220
РегулярныевыраженияРегулярноевыражениеэтопоследовательностьизобычныхиспециальныхсимволов.Этапоследовательностьзадаетшаблон,которыйпозжеиспользуетсядляпоискаподстрок.
Приработессетевымоборудованием,регулярныевыражениямогутиспользоваться,например,дляобработкивыводакомандshow.Например,еслисовсегооборудованиянадособратьинформациюпроверсиюОСиuptime,можнополучитьэтуинформациюизвыводаshowversion,обработавегоспомощьюрегулярныхвыражений
Всамомсетевомоборудовании,регулярныевыраженияможноиспользоватьдляфильтрациивыводалюбыхкомандshowиmore.Или,например,дляфильтрациитаблицыBGP.
Отличныйсайтдлятестированиярегулярныхвыражений:regex101.com
9.Регулярныевыражения
221
МодульreВPythonдляработысрегулярнымивыражениямииспользуетсямодульre.
Соответственно,дляначалаработысрегулярнымивыражениямивPython,надоимпортироватьмодульre.
Вэтомразделеиспользуетсяпримерпоискаподстрокивстроке,безспециальныхсимволов,чтобысосредоточитьсянавозможностяхмодуляre.Вследующихразделахбудутиспользоватьсяболеесложныепримеры.
Основныефункциимодуляre:
match()-ищетпоследовательностьвначалестрокиsearch()-ищетпервоесовпадениесшаблономfindall()-ищетвсесовпадениясшаблоном.Выдаетрезультирующиестрокиввидеспискаfinditer()-ищетвсесовпадениясшаблоном.Выдаетитераторcompile()-компилируетрегулярноевыражение.Кэтомуобъектузатемможноприменятьвсеперечисленныефункции
search()
Функцияsearch():
используетсядляпоискаподстроки,котораясоответствуетшаблонувозвращаетобъектMatch,еслиподстроканайденавозвращаетNone,еслиподстроканенайдена
Поискподстрокивстроке:
In[1]:importre
In[2]:line='00:09:BB:3D:D6:5810.1.10.286250dhcp-snooping10FastEth
ernet0/1'
In[3]:printre.search('dhcp',line)
<_sre.SRE_Matchobjectat0x10edeb9f0>
In[4]:printre.search('dhcpd',line)
None
ИзобъектаMatchможнополучитьнескольковариантовполезнойинформации.
Модульre
222
Например,спомощьюметодаspan(),можнополучитьчисла,указывающиеначалоиконецподстроки:
In[5]:match=re.search('dhcp',line)
In[6]:match.span()
Out[6]:(49,53)
In[7]:line[49:53]
Out[7]:'dhcp'
Методgroup()позволяетполучитьподстроку,котораясоответствуетшаблону:
In[15]:match.group()
Out[15]:'dhcp'
Важныймоментвиспользованиифункцииsearch(),точтоонаищеттолькопервоесовпадениевстроке,котороесоответствуетшаблону:
In[16]:line2='testdhcp,test2dhcp2'
In[17]:match=re.search('dhcp',line2)
In[18]:match.group()
Out[18]:'dhcp'
In[19]:match.span()
Out[19]:(5,9)
findall()
Длятогочтобынайтивсесовпадения,можноиспользоватьфункциюfindall():
In[20]:line2='testdhcp,test2dhcp2'
In[21]:match=re.findall('dhcp',line2)
In[22]:printmatch
['dhcp','dhcp']
Особенностьфункцииfindall()втом,чтоонавозвращаетсписокподстрок,которыесоответствуютшаблону,анеобъектMatch.Поэтомунельзявызватьметоды,которыеиспользовалисьвфункцииsearch().
Модульre
223
finditer()
Длятогочтобыполучитьвсесовпадения,ноприэтом,получитьсовпаденияввидеобъектаMatch(),можноиспользоватьфункциюfinditer():
In[23]:line2='testdhcp,test2dhcp2'
In[24]:match=re.finditer('dhcp',line2)
In[25]:printmatch
<callable-iteratorobjectat0x10efd2cd0>
In[26]:foriinmatch:
....:printi.span()
....:
(5,9)
(17,21)
In[27]:line2[5:9]
Out[27]:'dhcp'
In[28]:line2[17:21]
Out[28]:'dhcp'
Можновоспользоватьсяиметодамиstart(),end()(такудобнееполучитьпозицииподстрок):
In[29]:line2='testdhcp,test2dhcp2'
In[30]:match=re.finditer('dhcp',line2)
In[31]:foriinmatch:
....:b=i.start()
....:e=i.end()
....:printline2[b:e]
....:
dhcp
dhcp
compile()
ВPythonестьвозможностьзаранеескомпилироватьрегулярноевыражение,азатемиспользоватьего.Этоособеннополезновтехслучаях,когдарегулярноевыражениемногоиспользуетсявскрипте.
Примеркомпиляциирегулярноговыраженияиегоиспользования:
Модульre
224
In[32]:line2='testdhcp,test2dhcp2'
In[33]:regex=re.compile('dhcp')
In[34]:match=regex.finditer(line2)
In[35]:foriinmatch:
....:b=i.start()
....:e=i.end()
....:printline2[b:e]
....:
dhcp
dhcp
Модульre
225
СпециальныесимволыПолностьювозможностирегулярныхвыраженийпроявляютсяприиспользованииспециальныхсимволов.
Специальныесимволы:
.-любойсимвол,кромесимволановойстроки(опцияmпозволяетвключитьисимволновойстроки) -началостроки$-конецстроки[abc]-любойсимволвскобках[^abc]-любойсимвол,крометех,чтовскобкахa|b-элементaилиb(regex)-выражениерассматриваетсякакодинэлемент.Текст,которыйсовпалсвыражением,запоминается
Повторение:
regex*-нольилиболееповторенийпредшествующегоэлементаregex+-одинилиболееповторенийпредшествующегоэлементаregex?-нольилиодноповторениепредшествующегоэлементаregex{n}-ровноnповторенийпредшествующегоэлементаregex{n,m}-отnдоmповторенийпредшествующегоэлементаregex{n,}-nилиболееповторенийпредшествующегоэлемента
Предопределенныенаборысимволов:
\d-любаяцифра\D-любоенечисловоезначение\s-whitespace(\t\n\r\f\v)\S-все,кромеwhitespace\w-любаябукваилицифра\W-все,кромебуквицифр
Посмотримнапримерыиспользованияспециальныхсимволов:
In[1]:importre
In[2]:line="FastEthernet0/110.0.12.1YESmanualup
up"
Специальныесимволы
226
Точкаобозначаетлюбойсимвол,поэтомувстрокеlineнайдено3совпадениясрегулярнымвыражением.0:
In[3]:printre.findall('.0',line)
['t0','10','.0']
Символ означаетначалостроки.Выражению Fсоотвествуеттолькооднаподстрока:
In[4]:printre.findall('^F',line)
['F']
Выражению .aсоответствуетподстрока'Fa':
In[5]:printre.findall('^.a',line)
['Fa']
Символ$обозначаетконецстроки:
In[6]:printre.findall('up$',line)
['up']
In[7]:printre.findall('up',line)
['up','up']
Символы,которыеперечисленывквадратныхскобках,означают,чтолюбойизэтихсимволовбудетсовпадением.Такимобразомможноописыватьразныерегистры:
In[8]:printre.findall('[Ff]ast',line)
['Fast']
In[9]:printre.findall('[Ff]ast[Ee]thernet',line)
['FastEthernet']
Еслипослеоткрывающейсяквадратнойскобки,указансимвол ,совпадениембудетлюбойсимвол,кромеуказанныхвскобках(вданномслучае,всё,кромебуквипробела):
In[10]:printre.findall('[^a-zA-Z]',line)
['0','/','1','1','0','.','0','.','1','2','.','1']
Вертикальнаячертаработаеткак'или':
Специальныесимволы
227
In[11]:printre.findall('up|down',line)
['up','up']
Специальныесимволы
228
ЖадностьрегулярныхвыраженийПоумолчанию,символыповторенияврегулярныхвыраженияхжадные(greedy).Этозначит,чторезультирующаяподстрока,котораясоответствуетшаблону,будетнаиболеедлинной.
Примержадногоповедения:
In[1]:importre
In[2]:line='<textline>sometext>'
In[3]:match=re.search('<.*>',line)
In[4]:match.group()
Out[4]:'<textline>sometext>'
Тоесть,вданомслучаевыражениезахватиломаксимальновозможныйкусоксимволов,заключенныйв<>.
Еслинужноотключитьжадность,достаточнодобавитьзнаквопросапослесимволовповторения:
In[5]:line='<textline>sometext>'
In[6]:match=re.search('<.*?>',line)
In[7]:match.group()
Out[7]:'<textline>'
Обратитевнимание,чтожадностькасаетсяименносимволовповторения
Жадностьрегулярныхвыражений
229
Группировкавыражений
Нумерованныегруппы
Спомощьюопределениягруппэлементоввшаблоне,можноизолироватьчаститекста,которыесоответствуютшаблону.
Группаопределяетсяпомещениемвыражениявкруглыескобки().
Внутривыражения,группынумеруютсяслеванаправо,начинаяс1.Затемкгруппамможнообращатьсяпономерамиполучатьтекст,которыесоответствуетвыражениювгруппе.
Примериспользованиягрупп:
In[8]:line="FastEthernet0/110.0.12.1YESmanualup
up"
In[9]:match=re.search('(\S+)\s+([\w\.]+)\s+.*',line)
Вданномпримереуказаныдвегруппы:
перваягруппа-любыесимволы,кромеwhitespaces.Этагруппанежаднаявтораягруппа-любаябукваилицифра(символ\w)илиточка.Этагруппанежадная
Вторуюгруппуможнобылоописатьтакже,какипервую.Другойвариантсделанпростодляпримера
Теперьможнообращатьсякгруппампономеру.Группа0этострока,котораясоответствуетвсемушаблону:
In[10]:match.group(0)
Out[10]:'FastEthernet0/110.0.12.1YESmanualup
up'
In[11]:match.group(1)
Out[11]:'FastEthernet0/1'
In[12]:match.group(2)
Out[12]:'10.0.12.1'
Длявыводавсехподстрок,которыесоответствуютуказаннымгруппам,используетсяметодgroups:
Группировкавыражений
230
In[13]:match.groups()
Out[13]:('FastEthernet0/1','10.0.12.1')
Именованныегруппы
Когдавыражениесложное,неоченьудобноопределятьномергруппы.Плюс,придополнениивыражения,можетполучитьсятак,чтопорядокгруппизменился.Ипридетсяизменитьикод,которыйссылаетсянагруппы.
Именованныегруппыпозволяютзадаватьгруппеимя.
Синтаксисименованнойгруппы(?P<name>regex):
In[14]:line="FastEthernet0/110.0.12.1YESmanualup
up"
In[15]:match=re.search('(?P<intf>\S+)\s+(?P<address>[\d\.]+)\s+',line)
Теперькэтимгруппамможнообращатьсяпоимени:
In[15]:match.group('intf')
Out[15]:'FastEthernet0/1'
In[16]:match.group('address')
Out[16]:'10.0.12.1'
Такжеоченьполезното,чтоспомощьюметодаgroupdict(),можнополучитьсловарь,гдеключи-именагрупп,азначения-подстроки,которыеимсоответствуют:
In[17]:match.groupdict()
Out[17]:{'address':'10.0.12.1','intf':'FastEthernet0/1'}
И,втакомслучае,можнодобавитьгруппыврегулярноевыражениеиполагатьсянаихимя,аненапорядок:
In[18]:match=re.search('(?P<intf>\S+)\s+(?P<address>[\d\.]+)\s+[\w\s]+(?P<status>
up|down|administrativelydown)\s+(?P<protocol>up|down)',line)
In[19]:match.groupdict()
Out[19]:
{'address':'10.0.12.1',
'intf':'FastEthernet0/1',
'protocol':'up',
'status':'up'}
Группировкавыражений
231
Группировкавыражений
232
Разборвыводакомандыshowipdhcpsnoopingспомощьюименованныхгрупп
Рассмотримещеодинпримериспользованияименованныхгрупп.Вэтомпримере,задачавтом,чтобыполучитьизвыводакомандыshowipdhcpsnoopingbindingполя:MAC-адрес,IP-адрес,VLANиинтерфейс.
Вфайлеdhcp_snooping.txtнаходитсявыводкомандыshowipdhcpsnoopingbinding:
MacAddressIpAddressLease(sec)TypeVLANInterface
----------------------------------------------------------------------------
----
00:09:BB:3D:D6:5810.1.10.286250dhcp-snooping10FastEthernet0/1
00:04:A3:3E:5B:6910.1.5.263951dhcp-snooping5FastEthernet0/1
0
00:05:B3:7E:9B:6010.1.5.463253dhcp-snooping5FastEthernet0/9
00:09:BC:3F:A6:5010.1.10.676260dhcp-snooping10FastEthernet0/3
Totalnumberofbindings:4
Дляначала,попробуемразобратьоднустроку:
In[1]:line='00:09:BB:3D:D6:5810.1.10.286250dhcp-snooping10FastEthernet0
/1'
Врегулярномвыражении,именованныегруппыиспользуютсядлятехчастейвывода,которыенужнозапомнить:
In[2]:match=re.search('(?P<mac>.+?)+(?P<ip>.*?)+(\d+)+([\w-]+)+(?P<vlan>\d+)+
(?P<int>.*$)',line)
Комментариикрегулярномувыражению:
(?P<mac>.+?)+-вгруппусименем'mac'попадаютлюбыесимволы.Но,таккаквыражениенежадное,аследомуказано,чтодолжныидтипробелы(одинилиболее),получается,чтовыражениеописываетпоследовательностьлюбыхсимволов,допробела(?P<ip>.*?)+-тутаналогично,последовательностьлюбыхсимволовдопробела.Имягруппы'ip'(\d+)+-числоваяпоследовательность(однаилиболеецифр),азатемодинилиболеепробелов
сюдапопадетзначениеLease([\w-]+)+-буквыили-,вколичествеодногоилиболее
Разборвыводакомандыshowipdhcpsnoopingспомощьюименованныхгрупп
233
сюдапопадаеттипсоответствия(вданномслучае,всеониdhcp-snooping)(?P<vlan>\d+)+-именованнаягруппа'vlan'.Сюдапопадаюттолькочисловыепоследовательности,соднимилиболеесимволами(?P<int>.*$)-именованнаягруппа'int'.Сюдапопадаютлюбыесимволы,которыенаходятсявконцестроки(впредыдущемвыраженииэтагруппаограниченапробелом)
Обратитевнимание,чтодляпервыхдвухгруппэлементовотключенажадность.
Дляостальныхжадностьможнонеотключать,таккаквнихболеечеткоуказаныкакиеименносимволыдолжныбыть.
Врезультате,методgroupdictвернеттакойсловарь:
In[3]:match.groupdict()
Out[3]:
{'int':'FastEthernet0/1',
'ip':'10.1.10.2',
'mac':'00:09:BB:3D:D6:58',
'vlan':'10'}
Таккакрегулярноевыражениеотработалокакнужно,можносоздаватьскрипт.Вскрипте,перебираютсявсестрокифайлаdhcp_snooping.txtинастандартныйпотоквывода,выводитсяинформацияобустройствах.
Файлparse_dhcp_snooping.py:
#-*-coding:utf-8-*-
importre
regex=re.compile('(?P<mac>.+?)+(?P<ip>.*?)+(\d+)+([\w-]+)+(?P<vlan>\d+)+(?P<int
>.*$)')
result=[]
withopen('dhcp_snooping.txt')asdata:
forlineindata:
ifline[0].isdigit():
result.append(regex.search(line).groupdict())
print"Ккоммутаторуподключено%dустройства"%len(result)
fornum,compinenumerate(result,1):
print"Параметрыустройства%s:"%num
forkeyincomp:
print"\t%s:\t%s"%(key,comp[key])
Результатвыполнения:
Разборвыводакомандыshowipdhcpsnoopingспомощьюименованныхгрупп
234
$pythonparse_dhcp_snooping.py
Ккоммутаторуподключено4устройства
Параметрыустройства1:
int:FastEthernet0/1
ip:10.1.10.2
mac:00:09:BB:3D:D6:58
vlan:10
Параметрыустройства2:
int:FastEthernet0/10
ip:10.1.5.2
mac:00:04:A3:3E:5B:69
vlan:5
Параметрыустройства3:
int:FastEthernet0/9
ip:10.1.5.4
mac:00:05:B3:7E:9B:60
vlan:5
Параметрыустройства4:
int:FastEthernet0/3
ip:10.1.10.6
mac:00:09:BC:3F:A6:50
vlan:10
Разборвыводакомандыshowipdhcpsnoopingспомощьюименованныхгрупп
235
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание9.1
Создатьскрипт,которыйбудетожидатьдвааргумента:
1. имяфайла,вкоторомнаходитсявыводкомандыshow2. регулярноевыражение
Врезультатевыполненияскрипта,настандартныйпотоквыводадолжныбытьвыведенытестрокиизфайласвыводомкомандыshow,вкоторыхбылонайденосовпадениесрегулярнымвыражением.
Проверитьработускриптанапримеревыводакомандыshipintbr(файлsh_ip_int_br.txt).Например,попробуйтевывестиинформациютолькопоинтерфейсуFastEthernet0/1.
Вданномслучае,скриптбудетработатькакфильтрincludeвCLICisco.Выможетепопробоватьиспользоватьрегулярныевыражениядляфильтрациивыводакомандshow.
Пример:shipintbr|includeup+up
Задание9.1a
Задания
236
Напишитерегулярноевыражение,котороеотобразитстрокисинтерфейсами0/1и0/3извыводаshipintbr.
Проверьтерегулярноевыражение,используяскрипт,которыйбылсозданвзадании9.1,ифайлsh_ip_int_br.txt.
Задание9.1b
Переделайтерегулярноевыражениеиззадания9.1aтакимобразом,чтобыоно,по-прежнему,отображалострокисинтерфейсами0/1и0/3,но,приэтом,врегулярномвыражениибылонеболее7символов(несчитаякавычкивокругрегулярноговыражения).
Проверьтерегулярноевыражение,используяскрипт,которыйбылсозданвзадании9.1,ифайлsh_ip_int_br.txt.
Задание9.1c
Проверитьработускриптаиззадания9.1ирегулярноговыраженияиззадания9.1aили9.1bнавыводеshipintbrизфайлаsh_ip_int_br_switch.txt.
Если,врезультатевыполненияскрипта,быливыведенынетолькострокисинтерфейсами0/1и0/3,исправитьрегулярноевыражение.Врезультате,должнывыводитьсятолькострокисинтерфейсами0/1и0/3.
Задание9.2
Создатьфункциюreturn_match,котораяожидаетдвааргумента:
имяфайла,вкоторомнаходитсявыводкомандыshowрегулярноевыражение
Функциядолжнаобрабатыватьвыводкомандыshowпострочноивозвращатьсписокподстрок,которыесовпалисрегулярнымвыражением(невсюстроку,гдебылонайденосовпадение,атолькотуподстроку,котораясовпаласвыражением).
Проверитьработуфункциинапримеревыводакомандыshipintbr(файлsh_ip_int_br.txt).ВывестисписоквсехIP-адресовизвыводакоманды.
Соответственно,регулярноевыражениедолжноописыватьподстрокусIP-адресом(тоесть,совпадениемдолженбытьIP-адрес).
Задания
237
Обратитевнимание,чтовданномслучае,мыможемнепроверятькорректностьIP-адреса,диапазоныадресовитакдалее,таккакмыобрабатываемвыводкоманды,аневводпользователя.
Задание9.3
Создатьфункциюparse_cfg,котораяожидаеткакаргументимяфайла,вкоторомнаходитсяконфигурацияустройства.
ФункциядолжнаобрабатыватьконфигурациюивозвращатьIP-адресаимаски,которыенастроенынаинтерфейсах,ввидеспискакортежей:
первыйэлементкортежа-IP-адресвторойэлементкортежа-маска
Например(взятыпроизвольныеадреса):[('10.0.1.1','255.255.255.0'),('10.0.2.1','255.255.255.0')]
Дляполучениятакогорезультата,используйтерегулярныевыражения.
Проверитьработуфункциинапримерефайлаconfig_r1.txt.
Обратитевнимание,чтовданномслучае,мыможемнепроверятькорректностьIP-адреса,диапазоныадресовитакдалее,таккакмыобрабатываемконфигурацию,аневводпользователя.
Задание9.3a
Переделатьфункциюparse_cfgиззадания9.3такимобразом,чтобыонавозвращаласловарь:
ключ:имяинтерфейсазначение:кортежсдвумястроками:
IP-адресмаска
Например(взятыпроизвольныеадреса):
{'FastEthernet0/1':('10.0.1.1','255.255.255.0'),
'FastEthernet0/2':('10.0.2.1','255.255.255.0')}
Дляполучениятакогорезультата,используйтерегулярныевыражения.
Проверитьработуфункциинапримерефайлаconfig_r1.txt.
Задания
238
Задание9.3b
Проверитьработуфункцииparse_cfgиззадания9.3aнаконфигурацииconfig_r2.txt.
Обратитевнимание,чтонаинтерфейсеe0/1назначеныдваIP-адреса:
interfaceEthernet0/1
ipaddress10.255.2.2255.255.255.0
ipaddress10.254.2.2255.255.255.0secondary
Авсловаре,которыйвозвращаетфункцияparse_cfg,интерфейсуEthernet0/1соответствуеттолькоодинизних(второй).
Переделайтефункциюparse_cfgиззадания9.3aтакимобразом,чтобыонавозвращаласписоккортежейдлякаждогоинтерфейса.Еслинаинтерфейсеназначентолькоодинадрес,вспискебудетодинкортеж.ЕслиженаинтерфейсенастроенынесколькоIP-адресов,товспискебудетнесколькокортежей.
Проверьтефункциюнаконфигурацииconfig_r2.txtиубедитесь,чтоинтерфейсуEthernet0/1соответствуетсписокиздвухкортежей.
Задание9.4
Создатьфункциюparse_sh_ip_int_br,котораяожидаеткакаргументимяфайла,вкоторомнаходитсявыводкомандыshow
Функциядолжнаобрабатыватьвыводкомандыshowipintbrивозвращатьтакиеполя:
InterfaceIP-AddressStatusProtocol
Информациядолжнавозвращатьсяввидеспискакортежей:[('FastEthernet0/0','10.0.1.1','up','up'),('FastEthernet0/1','10.0.2.1','up','up'),('FastEthernet0/2','unassigned','up','up')]
Дляполучениятакогорезультата,используйтерегулярныевыражения.
Проверитьработуфункциинапримерефайлаsh_ip_int_br_2.txt.
Задание9.4a
Создатьфункциюconvert_to_dict,котораяожидаетдвааргумента:
Задания
239
списоксназваниямиполейсписоккортежейсрезультатамиотработкифункцииparse_sh_ip_int_brиззадания9.4
Функциявозвращаетрезультатввидеспискасловарей(порядокполейможетбытьдругой):[{'interface':'FastEthernet0/0','status':'up','protocol':'up','address':'10.0.1.1'},{'interface':'FastEthernet0/1','status':'up','protocol':'up','address':'10.0.2.1'}]
Проверитьработуфункциинапримерефайлаsh_ip_int_br_2.txt:
первыйаргумент-списокheadersвторойаргумент-результат,которыйвозвращаетфункцииparse_showизпрошлогозадания.
Функциюparse_sh_ip_int_brненужнокопировать.Надоимпортироватьилисамуфункцию,ииспользоватьтожерегулярноевыражение,чтоивзадании9.4,илиимпортироватьрезультатвыполненияфункцииparse_show.
headers=['interface','address','status','protocol']
Задания
240
СериализацияданныхСериализацияданных-этосохранениеданныхвкаком-тоформате.Чащевсего,этосохранениевкаком-тоструктурированномформате.
Например,этомогутбыть:
файлывформатеYAMLилиJSONфайлывформатеCSVбазаданных
Крометого,Pythonпозволяетзаписыватьобъектысамогоязыка(этотаспектвкурсенерассматривается,но,есливаминтересно,посмотритенамодульPickle).
ВэтомразделерассматриваютсяформатыCSV,JSON,YAML,авследующемразделе-базыданных.
ДлячегомогутпригодитсяформатыYAML,JSON,CSV:
увасмогутбытьданныеоIP-адресахиподобнойинформации,которуюнужнообработать,втаблицах
таблицуможноэкспортироватьвформатCSVиобрабатыватьеёспомощьюPython
управляющийсофтможетвозвращатьданныевJSON.Соответственно,преобразовавэтиданныевобъектPython,снимиможноработатьиделать,чтоугодноYAMLоченьудобноиспользоватьдляописанияпараметров,таккакунегодовольноприятныйсинтаксис
например,этомогутбытьпараметрынастройкиразличныхобъектов(IP-адреса,VLANидр)какминимум,знаниеформатаYAMLпригодитсяприиспользованииAnsible
ДлякаждогоизэтихформатоввPythonестьмодуль,которыйсущественноупрощаетработусними.
Вэтомразделе,рассматриваютсятолькобазовыеоперациичтенияизаписи,бездополнительныхпараметров.Подробнее,можнопочитатьвдокументациимодулей.Крометого,насайтеPyMOTWоченьхорошорасписываютсявсемодулиPython,которыевходятвстандартнуюбиблиотеку(устанавливаются,вместессамимPython):
CSVJSON
10.Сериализацияданных
241
РаботасфайламивформатеCSVCSV(comma-separatedvalue)-этоформатпредставлениятабличныхданных(например,этомогутбытьданныеизтаблицы,илиданныеизБД).
Вэтомформате,каждаястрокафайла-этострокатаблицы.Но,несмотрянаназваниеформата,разделителемможетбытьнетолькозапятая.
И,хотяуформатовсдругимразделителемможетбытьисобственноеназвание,например,TSV(tabseparatedvalues),темнеменееподформатомCSVпонимают,какправило,любыеразделители.
ПримерфайлавформатеCSV(sw_data.csv):
hostname,vendor,model,location
sw1,Cisco,3750,London
sw2,Cisco,3850,Liverpool
sw3,Cisco,3650,Liverpool
sw4,Cisco,3650,London
ВстандартнойбиблиотекеPythonестьмодульcsv,которыйпозволяетработатьсфайламивCSVформате.
Чтение
Примериспользованиямодуляcsv(файлcsv_read.py):
importcsv
withopen('sw_data.csv')asf:
reader=csv.reader(f)
forrowinreader:
printrow
Выводбудеттаким:
$pythoncsv_read.py
['hostname','vendor','model','location']
['sw1','Cisco','3750','London']
['sw2','Cisco','3850','Liverpool']
['sw3','Cisco','3650','Liverpool']
['sw4','Cisco','3650','London']
CSV
243
Впервомспискенаходятсяназваниястолбцов,авостальных,соответствующиезначения.
Иногдаврезультатеобработки,гораздоудобнейполучитьсловари,вкоторыхключи-этоназваниястолбцов,азначения-значениястолбцов.
ДляэтоговмодулеестьDictReader(файлcsv_read_dict.py):
importcsv
withopen('sw_data.csv')asf:
reader=csv.DictReader(f)
forrowinreader:
printrow
Выводбудеттаким:
$pythoncsv_read_dict.py
{'model':'3750','hostname':'sw1','vendor':'Cisco','location':'London'}
{'model':'3850','hostname':'sw2','vendor':'Cisco','location':'Liverpool'}
{'model':'3650','hostname':'sw3','vendor':'Cisco','location':'Liverpool'}
{'model':'3650','hostname':'sw4','vendor':'Cisco','location':'London'}
Обратитевнимание,чтосамreaderэтоитератор.Поэтому,еслипростовывестиreader,товыводбудеттаким:
In[1]:importcsv
In[2]:withopen('sw_data.csv')asf:
...:reader=csv.reader(f)
...:printreader
...:
<_csv.readerobjectat0x10385b050>
Но,еслинужновсеобъектыпередатькуда-тодальше,егоможнопревратитьвсписоктакимобразом:
In[3]:withopen('sw_data.csv')asf:
...:reader=csv.reader(f)
...:printlist(reader)
...:
[['hostname','vendor','model','location'],['sw1','Cisco','3750','London'],['sw
2','Cisco','3850','Liverpool'],['sw3','Cisco','3650','Liverpool'],['sw4','Cis
co','3650','London']]
CSV
244
Запись
Аналогичнымобразом,спомощьюмодуляcsv,можноизаписатьфайлвформатеCSV(файлcsv_write.py):
importcsv
data=[['hostname','vendor','model','location'],
['sw1','Cisco','3750','London,Beststr'],
['sw2','Cisco','3850','Liverpool,Betterstr'],
['sw3','Cisco','3650','Liverpool,Betterstr'],
['sw4','Cisco','3650','London,Beststr']]
withopen('sw_data_new.csv','w')asf:
writer=csv.writer(f)
forrowindata:
writer.writerow(row)
withopen('sw_data_new.csv')asf:
printf.read()
Впримеревыше,строкиизспискасначалазаписываютсявфайл,азатем,содержимоефайлавыводитсянастандартныйпотоквывода.
Выводбудеттаким:
$pythoncsv_write.py
hostname,vendor,model,location
sw1,Cisco,3750,"London,Beststr"
sw2,Cisco,3850,"Liverpool,Betterstr"
sw3,Cisco,3650,"Liverpool,Betterstr"
sw4,Cisco,3650,"London,Beststr"
Обратитевниманиенаинтереснуюособенность:последнеезначение,взятовкавычки,аостальныестроки-нет.
Такполучилосьиз-затого,чтовпоследнейстрокеестьзапятая.Икавычкиуказываютнато,чтоименноявляетсяцелойстрокой.Когдазапятаянаходятсявкавычках,модульcsvневоспринимаетеёкакразделитель.
Иногда,лучше,чтобывсестрокибыливкавычках.Конечно,вэтомслучае,достаточнопростойпример,нокогдавстрокахбольшезначений,токавычкипозволяютуказатьгденачинаетсяизаканчиваетсязначение.
Модульcsvпозволяетуправлятьэтим.Длятого,чтобывсестрокизаписывалисьвфайлcsvскавычками,надоизменитьскрипттакимобразом(файлcsv_write_ver2.py):
CSV
245
importcsv
data=[['hostname','vendor','model','location'],
['sw1','Cisco','3750','London,Beststr'],
['sw2','Cisco','3850','Liverpool,Betterstr'],
['sw3','Cisco','3650','Liverpool,Betterstr'],
['sw4','Cisco','3650','London,Beststr']]
withopen('sw_data_new.csv','w')asf:
writer=csv.writer(f,quoting=csv.QUOTE_NONNUMERIC)
forrowindata:
writer.writerow(row)
withopen('sw_data_new.csv')asf:
printf.read()
Теперьвыводбудеттаким:
$pythoncsv_write_ver2.py
"hostname","vendor","model","location"
"sw1","Cisco","3750","London,Beststr"
"sw2","Cisco","3850","Liverpool,Betterstr"
"sw3","Cisco","3650","Liverpool,Betterstr"
"sw4","Cisco","3650","London,Beststr"
Теперьвсезначенияскавычками.И,таккакномермоделизаданкакстрока,визначальномсписке,тутонтожевкавычках.
Указаниеразделителя
Иногда,вкачестверазделителяиспользуютсядругиезначения.Втакомслучае,должнабытьвозможностьподсказатьмодулю,какойименноразделительиспользовать.
Например,есливфайлеиспользуетсяразделитель;(файлsw_data2.csv):
hostname;vendor;model;location
sw1;Cisco;3750;London
sw2;Cisco;3850;Liverpool
sw3;Cisco;3650;Liverpool
sw4;Cisco;3650;London
Достаточнопростоуказатькакойразделительиспользуетсявreader(файлcsv_read_delimiter.py):
CSV
246
importcsv
withopen('sw_data2.csv')asf:
reader=csv.reader(f,delimiter=';')
forrowinreader:
printrow
CSV
247
РаботасфайламивформатеJSONJSON(JavaScriptObjectNotation)-этотекстовыйформатдляхраненияиобменаданными.
JSONпосинтаксисуоченьпохожнасловаривPython.Идостаточноудобендлявосприятия.
КакивслучаесCSV,вPythonестьмодуль,которыйпозволяетлегкозаписыватьичитатьданныевформатеJSON.
Чтение
Файлsw_templates.json:
{
"access":[
"switchportmodeaccess",
"switchportaccessvlan",
"switchportnonegotiate",
"spanning-treeportfast",
"spanning-treebpduguardenable"
],
"trunk":[
"switchporttrunkencapsulationdot1q",
"switchportmodetrunk",
"switchporttrunknativevlan999",
"switchporttrunkallowedvlan"
]
}
Длячтения,вмодулеjsonестьдваметода:
json.load()-методсчитываетфайливозвращаетобъектыPythonjson.loads()-методсчитываетстрокувформатеJSONивозвращаетобъектыPython
json.load()
ЧтениефайлавобъектPython:
JSON
248
In[1]:importjson
In[2]:withopen('sw_templates.json')asf:
...:templates=json.load(f)
...:
In[3]:templates
Out[3]:
{u'access':[u'switchportmodeaccess',
u'switchportaccessvlan',
u'switchportnonegotiate',
u'spanning-treeportfast',
u'spanning-treebpduguardenable'],
u'trunk':[u'switchporttrunkencapsulationdot1q',
u'switchportmodetrunk',
u'switchporttrunknativevlan999',
u'switchporttrunkallowedvlan']}
In[4]:forsection,commandsintemplates.items():
...:printsection
...:print'\n'.join(commands)
...:
access
switchportmodeaccess
switchportaccessvlan
switchportnonegotiate
spanning-treeportfast
spanning-treebpduguardenable
trunk
switchporttrunkencapsulationdot1q
switchportmodetrunk
switchporttrunknativevlan999
switchporttrunkallowedvlan
Результатчтения-словарь.Обратитевнимание,чтопричтенииизфайлавформатеJSON,строкибудутвunicode.
МынебудемрассматриватьработусUnicode.Хорошийдокументоработесunicode:UnicodeHowTo.
json.loads()
СчитываниестрокивформатеJSONвобъектPython:
JSON
249
In[5]:withopen('sw_templates.json')asf:
...:templates=json.loads(f.read())
...:
In[6]:templates
Out[6]:
{u'access':[u'switchportmodeaccess',
u'switchportaccessvlan',
u'switchportnonegotiate',
u'spanning-treeportfast',
u'spanning-treebpduguardenable'],
u'trunk':[u'switchporttrunkencapsulationdot1q',
u'switchportmodetrunk',
u'switchporttrunknativevlan999',
u'switchporttrunkallowedvlan']}
Запись
ЗаписьфайлавформатеJSONтакжеосуществляетсядостаточнолегко.
ДлязаписиинформациивформатеJSONвмодулеjsonтакжедваметода:
json.dump()-методзаписываетобъектPythonвфайл,вформатеJSONjson.dumps()-методвозвращаетстрокувформатеJSON
json.dump()
ЗаписьобъектаPythonвфайл:
JSON
250
In[7]:trunk_template=['switchporttrunkencapsulationdot1q',
...:'switchportmodetrunk',
...:'switchporttrunknativevlan999',
...:'switchporttrunkallowedvlan']
...:
...:
...:access_template=['switchportmodeaccess',
...:'switchportaccessvlan',
...:'switchportnonegotiate',
...:'spanning-treeportfast',
...:'spanning-treebpduguardenable']
...:
...:to_json={'trunk':trunk_template,'access':access_template}
...:
In[8]:withopen('sw_templates.json','w')asf:
...:json.dump(to_json,f)
...:
In[9]:catsw_templates.json
{"access":["switchportmodeaccess","switchportaccessvlan","switchportnonegotiat
e","spanning-treeportfast","spanning-treebpduguardenable"],"trunk":["switchport
trunkencapsulationdot1q","switchportmodetrunk","switchporttrunknativevlan99
9","switchporttrunkallowedvlan"]}
КогданужнозаписатьинформациювформатеJSONвфайл,лучшеиспользоватьметодdump.
json.dumps()
ПреобразованиеобъектавстрокувформатеJSON:
In[10]:withopen('sw_templates.json','w')asf:
...:f.write(json.dumps(to_json))
...:
In[11]:catsw_templates.json
{"access":["switchportmodeaccess","switchportaccessvlan","switchportnonegotiat
e","spanning-treeportfast","spanning-treebpduguardenable"],"trunk":["switchport
trunkencapsulationdot1q","switchportmodetrunk","switchporttrunknativevlan99
9","switchporttrunkallowedvlan"]}
Методjson.dumps()подходитдляситуаций,когданадовернутьстрокувформатеJSON.
Дополнительныепараметрыметодовзаписи
JSON
251
Методамdumpиdumpsможнопередаватьдополнительныепараметрыдляуправленияформатомвывода.
Поумолчаниюэтиметодызаписываютинформациювкомпактномпредставлении.Какправило,когдаданныеиспользуютсядругимипрограммами,визуальноепредставлениеданныхневажно.Еслижеданныевфайленужнобудетсчитатьчеловеку,такойформатнеоченьудобновоспринимать.
Ксчастью,модульjsonпозволяетуправлятьподобнымивещами.
Передавдополнительныепараметрыметодуdump(илиметодуdumps),можнополучитьболееудобныйдлячтениевывод(файлjson_write_ver2.py):
In[13]:withopen('sw_templates.json','w')asf:
...:json.dump(to_json,f,sort_keys=True,indent=2)
...:
Теперьсодержимоефайлаsw_templates.jsonвыглядиттак:
In[14]:catsw_templates.json
{
"access":[
"switchportmodeaccess",
"switchportaccessvlan",
"switchportnonegotiate",
"spanning-treeportfast",
"spanning-treebpduguardenable"
],
"trunk":[
"switchporttrunkencapsulationdot1q",
"switchportmodetrunk",
"switchporttrunknativevlan999",
"switchporttrunkallowedvlan"
]
}
Изменениетипаданных
Ещеодинважныйаспектпреобразованияданныхвформатjson:данныеневсегдабудуттогожетипа,чтоисходныеданныевPython.
Такаяситуацияужевозникаласостроками-онибыливформатеunicode.Крометого,призаписивJSONкортежей,онипревращаютсявсписки.
Например:
JSON
252
In[15]:importjson
In[16]:trunk_template=('switchporttrunkencapsulationdot1q',
...:'switchportmodetrunk',
...:'switchporttrunknativevlan999',
...:'switchporttrunkallowedvlan')
In[17]:printtype(trunk_template)
<type'tuple'>
In[18]:withopen('trunk_template.json','w')asf:
...:json.dump(trunk_template,f,sort_keys=True,indent=2)
...:
In[19]:cattrunk_template.json
[
"switchporttrunkencapsulationdot1q",
"switchportmodetrunk",
"switchporttrunknativevlan999",
"switchporttrunkallowedvlan"
]
In[20]:templates=json.load(open('trunk_template.json'))
In[21]:type(templates)
Out[21]:list
In[22]:printtemplates
[u'switchporttrunkencapsulationdot1q',u'switchportmodetrunk',u'switchporttrunk
nativevlan999',u'switchporttrunkallowedvlan']
Ограничениепотипамданных
ВформатJSONнельзязаписатьсловарьукоторогоключи-кортежи:
In[23]:to_json={('trunk','cisco'):trunk_template,'access':access_template}
In[24]:withopen('sw_templates.json','w')asf:
...:json.dump(to_json,f)
...:
...
TypeError:key('trunk','cisco')isnotastring
Но,спомощьюдополнительногопараметраможноигнорироватьподобныеключи:
JSON
253
In[25]:to_json={('trunk','cisco'):trunk_template,'access':access_template}
In[26]:withopen('sw_templates.json','w')asf:
...:json.dump(to_json,f,skipkeys=True)
...:
...:
In[27]:catsw_templates.json
{"access":["switchportmodeaccess","switchportaccessvlan","switchportnonegotiat
e","spanning-treeportfast","spanning-treebpduguardenable"]}
JSON
254
РаботасфайламивформатеYAMLYAML(YAMLAin'tMarkupLanguage)-ещеодинтекстовыйформатдлязаписиданных.
YAMLболееприятендлявосприятиячеловеком,чемJSON,поэтомуегочастоиспользуютдляописаниясценариеввПО.Например,вAnsible.
СинтаксисYAML
КакиPython,YAMLиспользуетотступыдляуказанияструктурыдокумента.НовYAMLможноиспользоватьтолькопробелыинельзяиспользоватьзнакитабуляции.
ЕщеоднасхожестьсPython:комментарииначинаютсяссимвола#ипродолжаютсядоконцастроки.
ПройдемсяпотомукаквYAMLзаписываютсяразличныеформатыданных.
Список
Списокможетбытьзаписанводнустроку:
[switchportmodeaccess,switchportaccessvlan,switchportnonegotiate,spanning-tree
portfast,spanning-treebpduguardenable]
Иликаждыйэлементспискавсвоейстроке:
-switchportmodeaccess
-switchportaccessvlan
-switchportnonegotiate
-spanning-treeportfast
-spanning-treebpduguardenable
Когдасписокзаписантакимблоком,каждаястрокадолжнаначинатьсяс-(минусаипробела).Ивсестрокивспискедолжныбытьнаодномуровнеотступа.
Словарь
Словарьтакжеможетбытьзаписанводнустроку:
{vlan:100,name:IT}
YAML
255
Илиблоком:
vlan:100
name:IT
Строки
СтрокивYAMLнеобязательнобратьвкавычки.Этоудобно,ноиногдавсёжеследуетиспользоватькавычки.Например,когдавстрокеиспользуетсякакой-тоспециальныйсимвол(специальныйдляYAML).
Такуюстроку,например,нужновзятьвкавычки,чтобыонабылакорректновоспринятаYAML:
command:"shinterface|includeQueueingstrategy:"
Комбинацияэлементов
Словарь,вкотороместьдваключа:accessиtrunk.Значения,которыесоответствуютэтимключам-спискикоманд:
access:
-switchportmodeaccess
-switchportaccessvlan
-switchportnonegotiate
-spanning-treeportfast
-spanning-treebpduguardenable
trunk:
-switchporttrunkencapsulationdot1q
-switchportmodetrunk
-switchporttrunknativevlan999
-switchporttrunkallowedvlan
Списоксловарей:
YAML
256
-BS:1550
IT:791
id:11
name:Liverpool
to_id:1
to_name:LONDON
-BS:1510
IT:793
id:12
name:Bristol
to_id:1
to_name:LONDON
-BS:1650
IT:892
id:14
name:Coventry
to_id:2
to_name:Manchester
МодульPyYAML
ДляработысYAMLвPythonиспользуетсямодульPyYAML.Онневходитвстандартнуюбиблиотекумодулей,поэтомуегонужноустановить:
pipinstallpyyaml
Работасниманалогичнамодулямcsvиjson.
ЧтениеизYAML
ПопробуемпреобразоватьданныеизфайлаYAMLвобъектыPython.
Файлinfo.yaml:
YAML
257
-BS:1550
IT:791
id:11
name:Liverpool
to_id:1
to_name:LONDON
-BS:1510
IT:793
id:12
name:Bristol
to_id:1
to_name:LONDON
-BS:1650
IT:892
id:14
name:Coventry
to_id:2
to_name:Manchester
ЧтениеизYAMLвыполняетсяоченьпросто:
In[1]:importyaml
In[2]:withopen('info.yaml')asf:
...:templates=yaml.load(f)
...:
In[3]:templates
Out[3]:
[{'BS':1550,
'IT':791,
'id':11,
'name':'Liverpool',
'to_id':1,
'to_name':'LONDON'},
{'BS':1510,
'IT':793,
'id':12,
'name':'Bristol',
'to_id':1,
'to_name':'LONDON'},
{'BS':1650,
'IT':892,
'id':14,
'name':'Coventry',
'to_id':2,
'to_name':'Manchester'}]
YAML
258
ФорматYAMLоченьудобендляхраненияразличныхпараметров.Особенно,еслионизаполняютсявручную.
ЗаписьвYAML
ЗаписьобъектовPythonвYAML(файлyaml_write.py):
importyaml
trunk_template=['switchporttrunkencapsulationdot1q',
'switchportmodetrunk',
'switchporttrunknativevlan999',
'switchporttrunkallowedvlan']
access_template=['switchportmodeaccess',
'switchportaccessvlan',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
to_yaml={'trunk':trunk_template,'access':access_template}
withopen('sw_templates.yaml','w')asf:
f.write(yaml.dump(to_yaml))
withopen('sw_templates.yaml')asf:
printf.read()
Файлsw_templates.yamlвыглядиттакимобразом:
access:[switchportmodeaccess,switchportaccessvlan,switchportnonegotiate,spann
ing-tree
portfast,spanning-treebpduguardenable]
trunk:[switchporttrunkencapsulationdot1q,switchportmodetrunk,switchporttrunk
nativevlan999,switchporttrunkallowedvlan]
Тоесть,поумолчанию,списокзаписалсяводнустроку.Этоможноизменить.
Длятого,чтобыизменитьформатзаписи,надодобавитьпараметрdefault_flow_style=False(файлyaml_write_ver2.py):
YAML
259
importyaml
trunk_template=['switchporttrunkencapsulationdot1q',
'switchportmodetrunk',
'switchporttrunknativevlan999',
'switchporttrunkallowedvlan']
access_template=['switchportmodeaccess',
'switchportaccessvlan',
'switchportnonegotiate',
'spanning-treeportfast',
'spanning-treebpduguardenable']
to_yaml={'trunk':trunk_template,'access':access_template}
withopen('sw_templates.yaml','w')asf:
f.write(yaml.dump(to_yaml,default_flow_style=False))
withopen('sw_templates.yaml')asf:
printf.read()
Теперьсодержимоефайлаsw_templates.yamlвыглядиттакимобразом:
access:
-switchportmodeaccess
-switchportaccessvlan
-switchportnonegotiate
-spanning-treeportfast
-spanning-treebpduguardenable
trunk:
-switchporttrunkencapsulationdot1q
-switchportmodetrunk
-switchporttrunknativevlan999
-switchporttrunkallowedvlan
YAML
260
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание10.1
Вэтомзаданиинужно:
взятьсодержимоенесколькихфайловсвыводомкомандыshversionраспарситьвыводкомандыспомощьюрегулярныхвыраженийиполучитьинформациюобустройствезаписатьполученнуюинформациювфайлвCSVформате
Длявыполнениязаданиянужносоздатьдвефункции.
Функцияparse_sh_version:
ожидаетаргументoutputвкоторомнаходитсявыводкомандыshversionобрабатываетвывод,спомощьюрегулярныхвыраженийвозвращаеткортежизтрёхэлементов:
ios-вформате"12.4(5)T"image-вформате"flash:c2800-advipservicesk9-mz.124-5.T.bin"uptime-вформате"5days,3hours,3minutes"
Функцияwrite_to_csv:
ожидаетдвааргумента:
Задания
261
имяфайла,вкоторыйбудетзаписанаинформациявформатеCSVданныеввидеспискасписков,где:
первыйсписок-заголовкистолбцов,остальныесписки-содержимое
функциязаписываетсодержимоевфайл,вформатеCSVиничегоневозвращает
Остальноесодержимоескриптаможетбытьвскрипте,аможетбытьвещёоднойфункции.
Скриптдолжен:
обработатьинформациюизкаждогофайласвыводомshversion:sh_version_r1.txt,sh_version_r2.txt,sh_version_r3.txt
спомощьюфункцииparse_sh_version,изкаждоговыводадолжнабытьполученаинформацияios,image,uptimeизименифайланужнополучитьимяхостапослеэтоговсяинформациядолжнабытьзаписанавфайлrouters_inventory.csv
Вскрипте,спомощьюмодуляglob,создансписокфайлов,имякоторыхначинаетсянаsh_vers.Выможетераскомментироватьстрокуprintsh_version_files,чтобыпосмотретьсодержимоесписка.
Крометого,создансписокзаголовков(headers),которыйдолженбытьзаписанвCSV.
importglob
sh_version_files=glob.glob('sh_vers*')
#printsh_version_files
headers=['hostname','ios','image','uptime']
Задание10.2
Создатьфункции:
generate_access_config-генерируетконфигурациюдляaccess-портов,наосновесловарейaccessиpsecurityизфайлаsw_templates.yamlgenerate_trunk_config-генерируетконфигурациюдляtrunk-портов,наосновесловаряtrunkизфайлаsw_templates.yamlgenerate_mngmt_config-генерируетконфигурациюменеджментнастроек,наосновесловаряmngmtизфайлаtemplates.yamlgenerate_ospf_config-генерируетконфигурациюospf,наосновесловаряospfизфайлаtemplates.yamlgenerate_alias_config-генерируетконфигурациюalias,наосновесловаряaliasиз
Задания
262
файлаtemplates.yamlgenerate_switch_config-генерируетконфигурациюкоммутатора,взависимостиотпереданныхпараметров,используетдляэтогоостальныефункции
importyaml
access_dict={'FastEthernet0/12':10,
'FastEthernet0/14':11,
'FastEthernet0/16':17,
'FastEthernet0/17':150}
trunk_dict={'FastEthernet0/1':[10,20,30],
'FastEthernet0/2':[11,30],
'FastEthernet0/4':[17]}
defgenerate_access_config(access,psecurity=False):
"""
access-словарьaccess-портов,
длякоторыхнеобходимосгенерироватьконфигурацию,вида:
{'FastEthernet0/12':10,
'FastEthernet0/14':11,
'FastEthernet0/16':17}
psecurity-контролируетнужналинастройкаPortSecurity.ПоумолчаниюзначениеF
alse
-еслизначениеTrue,тонастройкавыполняетсясдобавлениемшаблонаport_secu
rity
-еслизначениеFalse,тонастройканевыполняется
Возвращаетсписоквсехкоманд,которыебылисгенерированынаосновешаблона
"""
pass
defgenerate_trunk_config(trunk):
"""
trunk-словарьtrunk-портов,
длякоторыхнеобходимосгенерироватьконфигурацию,вида:
{'FastEthernet0/1':[10,20],
'FastEthernet0/2':[11,30],
'FastEthernet0/4':[17]}
Возвращаетсписоквсехкоманд,которыебылисгенерированынаосновешаблона
"""
pass
defgenerate_ospf_config(filename):
"""
filename-имяфайлавформатеYAML,вкоторомнаходитсяшаблонospf.
Возвращаетсписоквсехкоманд,которыебылисгенерированынаосновешаблона
"""
Задания
263
templates=yaml.load(open(filename))
defgenerate_mngmt_config(filename):
"""
filename-имяфайлавформатеYAML,вкоторомнаходитсяшаблонmngmt.
Возвращаетсписоквсехкоманд,которыебылисгенерированынаосновешаблона
"""
pass
defgenerate_alias_config(filename):
"""
filename-имяфайлавформатеYAML,вкоторомнаходитсяшаблонalias.
Возвращаетсписоквсехкоманд,которыебылисгенерированынаосновешаблона
"""
pass
defgenerate_switch_config(access=True,psecurity=False,trunk=True,
ospf=True,mngmt=True,alias=False):
"""
Аргументыконтролируюткакиенастройкинадовыполнить.
Поумолчанию,будетнастроеновсе,кромеpsecurityиalias.
Возвращаетсписоквсехкоманд,которыебылисгенерированынаосновешаблона
"""
pass
#Сгенерироватьконфигурациидляразныхкоммутаторов:
sw1=generate_switch_config()
sw2=generate_switch_config(psecurity=True,alias=True)
sw3=generate_switch_config(ospf=False)
Задание10.3
Создатьфункциюparse_sh_cdp_neighbors,котораяобрабатываетвыводкомандыshowcdpneighbors.
Функцияожидает,какаргумент,выводкомандыоднойстрокой.
Функциядолжнавозвращатьсловарь,которыйописываетсоединениямеждуустройствами.
Например,есликакаргументбылпередантакойвывод:
Задания
264
R4>showcdpneighbors
DeviceIDLocalIntrfceHoldtmeCapabilityPlatformPortID
R5Fa0/1122RSI2811Fa0/1
R6Fa0/2143RSI2811Fa0/0
Функциядолжнавернутьтакойсловарь:
{'R4':{'Fa0/1':{'R5':'Fa0/1'},
'Fa0/2':{'R6':'Fa0/0'}}}
Проверитьработуфункциинасодержимомфайлаsh_cdp_n_sw1.txt
Задание10.3a
Спомощьюфункцииparse_sh_cdp_neighborsиззадания10.3,обработатьвыводкомандыshcdpneighborизфайлов:
sh_cdp_n_sw1.txtsh_cdp_n_r1.txtsh_cdp_n_r2.txtsh_cdp_n_r3.txtsh_cdp_n_r4.txtsh_cdp_n_r5.txtsh_cdp_n_r6.txt
Объединитьвсесловари,которыевозвращаетфункцияparse_sh_cdp_neighbors,водинсловарьtopologyизаписатьегосодержимоевфайлtopology.yaml.
Структурасловаряtopologyдолжнабытьтакой:
{'R4':{'Fa0/1':{'R5':'Fa0/1'},
'Fa0/2':{'R6':'Fa0/0'}},
'R5':{'Fa0/1':{'R4':'Fa0/1'}},
'R6':{'Fa0/0':{'R4':'Fa0/2'}}}
Некопироватькодфункцииparse_sh_cdp_neighbors
Задание10.3b
Переделатьфункциональностьскриптаиззадания10.3a,вфункциюgenerate_topology_from_cdp.
Задания
265
Функцияgenerate_topology_from_cdpдолжнабытьсозданаспараметрами:
list_of_files-списокфайловизкоторыхнадосчитатьвыводкомандыshcdpneighborsave_to_file-этотпараметруправляеттем,будетлизаписанвфайл,итоговыйсловарь
значениепоумолчанию-Truetopology_filename-имяфайла,вкоторыйсохранитсятопология.
поумолчанию,должноиспользоватьсяимяtopology.yaml.топологиясохраняетсятолько,еслиаргументsave_to_fileуказанравнымTrue
Функциявозвращаетсловарь,которыйописываеттопологию.Словарьдолженбытьвтомжеформате,чтоивзадании10.3a.
Проверитьработуфункцииgenerate_topology_from_cdpнафайлах:
sh_cdp_n_sw1.txtsh_cdp_n_r1.txtsh_cdp_n_r2.txtsh_cdp_n_r3.txtsh_cdp_n_r4.txtsh_cdp_n_r5.txtsh_cdp_n_r6.txt
Записатьполученныйсловарьвфайлtopology.yaml.
Некопироватькодфункцииparse_sh_cdp_neighbors
Задание10.3c
Спомощьюфункцииdraw_topologyизфайлаdraw_network_graph.pyсгенерироватьтопологию,котораясоответствуетописаниювфайлеtopology.yaml
Обратитевниманиенато,какойформатданныхожидаетфункцияdraw_topology.Описаниетопологииизфайлаtopology.yamlнужнопреобразоватьсоответствующимобразом,чтобыиспользоватьфункциюdraw_topology.
Длярешениязаданияможносоздатьлюбыевспомогательныефункции.
Витоге,должнобытьсгенерированоизображениетопологии.Результатдолженвыглядетьтакже,каксхемавфайлеtask_10_3c_topology.svg
Некопироватькодфункцииdraw_topology.
Длявыполненияэтогозадания,долженбытьустановленgraphviz:pipinstallgraphviz
Задания
266
Задания
267
РаботасбазамиданныхИспользованиебазданных-этоещеодинспособхраненияинформации.
Базаданных(БД)-этоданные,которыехранятсявсоответствиисопределеннойсхемой.Вэтойсхемекаким-тообразомописанысоотношениямеждуданными.
ЯзыкБД(лингвистическиесредства)-используетсядляописанияструктурыБД,управленияданными(добавление,изменение,удаление,получение),управленияправамидоступакБДиееобъектам,управлениятранзакциями.
Системауправлениябазамиданных(СУБД)-этопрограммныесредства,которыедаютвозможностьуправлятьБД.СУБДдолжныподдерживатьсоответствующийязык(языки)дляуправленияБД.
Вразговорнойречи(ноивлитературетакже)частоможновстретитьиспользованиетерминаБД,вместотерминаСУБД.Этонесовсемкорректно,но,темнеменее,встречаетсядовольночасто.
11.Работасбазамиданных
268
SQLSQL(structuredquerylanguage)-используетсядляописанияструктурыБД,управленияданными(добавление,изменение,удаление,получение),управленияправамидоступакБДиееобъектам,управлениятранзакциями.
ЯзыкSQLподразделяетсянатакиекатегории:
DDL(DataDefinitionLanguage)-языкописанияданныхDML(DataManipulationLanguage)-языкманипулированияданнымиDCL(DataControlLanguage)-языкопределениядоступакданнымTCL(TransactionControlLanguage)-языкуправлениятранзакциями
Вкаждойкатегорииестьсвоиоператоры(перечисленыневсеоператоры):
DDLCREATE-созданиеновойтаблицы,СУБД,схемыALTER-изменениесуществующейтаблицы,колонкиDROP-удалениесуществующихобъектовизСУБД
DMLSELECT-выборданныхINSERT-добавлениеновыхданныхUPDATE-обновлениесуществующихданныхDELETE-удалениеданных
DCLGRANT-предоставлениепользователямразрешенияначтение/записьопределенныхобъектоввСУБДREVOKE-отзывранеепредоставленыхразрешений
TCLCOMMITTransaction-применениетранзакцииROLLBACKTransaction-откатвсехизмененийсделанныхвтекущейтранзакции
SQLиPython
ДляработысреляционнойСУБДвPythonможноиспользоватьдваподхода:
работатьсбиблиотекой,котораясоответствуетконкретнойСУБДииспользоватьдляработысБДязыкSQL
Например,дляработысSQLiteиспользуетсямодульsqlite3работатьсORM,котораяиспользуетобъектно-ориентированныйподходдля
SQL
269
работысБДНапример,SQLAlchemy
SQL
270
SQLiteSQLite—встраиваемаявпроцессреализацияSQL-машины.
СловоSQL-серверздесьнеиспользуем,потомучтокактаковойсервертамненужен—весьфункционал,которыйвстраиваетсявSQL-сервер,реализованвнутрибиблиотеки(и,соответственно,внутрипрограммы,котораяеёиспользует).
Напрактике,SQLiteчастоиспользуетсякаквстроеннаяСУБДвприложениях.
SQLiteCLI
ВкомплектепоставкиSQLiteидёттакжеутилитадляработысSQLiteвкоманднойстроке.Утилитапредставленаввидеисполняемогофайлаsqlite3(sqlite3.exeдляWindows)исеепомощьюможновручнуювыполнятькомандыSQL.
СпомощьюэтойутилитыоченьудобнопроверятьправильностькомандSQL,атакжевцеломзнакомитьсясязыкомSQL.
ПопробуемспомощьюэтойутилитыразобратьсясбазовымикомандамиSQL,которыепонадобятсядляработысБД.
ДляначаларазберемсякаксоздаватьБД.
ЕсливыиспользуетеLinuxилиMacOS,то,скореевсего,sqlite3установлен.ЕсливыиспользуетеWindows,томожноскачатьsqlite3тут.
ДлятогочтобысоздатьБД(илиоткрытьужесозданную)надопростовызватьsqlite3такимобразом:
$sqlite3testDB.db
SQLiteversion3.7.132012-06-1102:05:22
Enter".help"forinstructions
EnterSQLstatementsterminatedwitha";"
sqlite>
Внутриsqlite3можновыполнятькомандыSQLили,такназываемые,метакоманды(илиdot-команды).
Метакоманды
SQLite
271
Кметакомандамотносятсянесколькоспециальныхкоманд,дляработысSQLite.Ониотносятсятолькокутилитеsqlite3,анекSQLязыку.Вконцеэтихкоманд;ставитьненужно.
Примерыметакоманд:
.help-подсказкасоспискомвсехметакоманд.exitили.quit-выходизсессииsqlite3.databases-показываетприсоединенныеБД.tables-показываетдоступныетаблицы
Примерывыполнения:
sqlite>.help
.backup?DB?FILEBackupDB(default"main")toFILE
.bailON|OFFStopafterhittinganerror.DefaultOFF
.databasesListnamesandfilesofattacheddatabases
...
sqlite>.databases
seqnamefile
---------------------------------------------
0main/home/nata/py_for_ne/db/db_article/testDB.db
SQLite
272
ОсновыSQL(вsqlite3CLI)ВэтомразделерассматриваетсясинтаксисязыкаSQL.
ЕсливызнакомысбазовымсинтаксисомSQL,этотразделможнопропуститьисразуперейтикразделу"Модульsqlite3".
ОсновыSQL(вsqlite3CLI)
273
CREATE
Операторcreateпозволяетсоздаватьтаблицы.
Создадимтаблицуswitch,вкоторойхранитсяинформацияокоммутаторах:
sqlite>CREATEtableswitch(
...>mactextprimarykey,
...>hostnametext,
...>modeltext,
...>locationtext
...>);
Аналогичноможнобылосоздатьтаблицуитакимобразом:
sqlite>createtableswitch(mactextprimarykey,hostnametext,modeltext,location
text);
Вданномпримеремыописалитаблицуswitch:определиликакиеполябудутвтаблицеизначениякакоготипабудутвнихнаходиться.
Крометого,полеmacявляетсяпервичнымключом.Этоавтоматическизначит,что:
поледолжнобытьуникальнымвнемнеможетнаходитьсязначениеNULL
Вэтомпримереэтовполнелогично,таккакMAC-адресдолженбытьуникальным.
Наданныймоментзаписейвтаблиценет,естьтолькоееопределение.Просмотретьопределениеможнотакойкомандой:
sqlite>.schemaswitch
CREATETABLEswitch(
mactextprimarykey,
hostnametext,
modeltext,
locationtext
);
CREATE
274
DROP
Операторdropудаляеттаблицувместесосхемойивсемиданными.
Удалитьтаблицуможнотак:
sqlite>DROPtableswitch;
DROP
275
INSERT
Операторinsertиспользуетсядлядобавленияданныхвтаблицу.
Естьнескольковариантовдобавлениязаписей,взависимостиоттого,вселиполябудутзаполненыибудутлиониидтипопорядкуопределенияполейилинет.
Еслиуказываютсязначениядлявсехполей,добавитьзаписьможнотакимобразом(порядокполейдолженсоблюдаться):
sqlite>INSERTintoswitchvalues('0000.AAAA.CCCC','sw1','Cisco3750','London,Gre
enStr');
Еслинужноуказатьневсеполя,илиуказатьихвпроизвольномпорядке,используетсятакаязапись:
sqlite>INSERTintoswitch(mac,model,location,hostname)
...>values('0000.BBBB.CCCC','Cisco3850','London,GreenStr','sw5');
INSERT
276
SELECT
Операторselectпозволяетзапрашиватьинформациювтаблице.
Например:
sqlite>SELECT*fromswitch;
0000.AAAA.CCCC|sw1|Cisco3750|London,GreenStr
0000.BBBB.CCCC|sw5|Cisco3850|London,GreenStr
select*означает,чтонужновывестивсеполятаблицы.Следом,указываетсяизкакойтаблицызапрашиваютсяданные:fromswitch.
Вданномслучае,вотображениитаблицынехватаетназванияполей.Включитьэтоможноспомощьюкоманды.headersON.
sqlite>.headersON
sqlite>SELECT*fromswitch;
mac|hostname|model|location
0000.AAAA.CCCC|sw1|Cisco3750|London,GreenStr
0000.BBBB.CCCC|sw5|Cisco3850|London,GreenStr
Теперьотобразилисьзаголовки,новцелом,отображениенеоченьприятное.Хотелосьбы,чтобывсевыводилосьввидеколонок.Заформатированиевыводаотвечаеткоманда.mode.
Режим.modecolumnвключаетотображениеввидеколонок:
sqlite>.modecolumn
sqlite>SELECT*fromswitch;
machostnamemodellocation
---------------------------------------------------
0000.AAAA.CCCCsw1Cisco3750London,GreenStr
0000.BBBB.CCCCsw5Cisco3850London,GreenStr
Прижелании,можновыставитьиширинуколонок.Дляэтогоиспользуетсякоманда.width.Например,попробуйтевыставить.width20.
Еслинужносделатьтак,чтобыэтипараметрыиспользовалисьпоумолчанию,добавьтеихвфайл.sqlitercвдомашнемкаталогепользователя,подкоторымвыработаете.
Например,чтобывыводзаголовковстолбцовивывобстолбцамииспользовалисьпоумолчанию,файл.sqlitercдолженвыглядетьтак:
SELECT
277
.headerson
.modecolumn
SELECT
278
WHERE
ОператорWHEREиспользуетсядляуточнениязапроса.Спомощьюэтогооператораможноуказыватьопределенныеусловия,покоторымотбираютсяданные.Еслиусловиевыполнено,возвращаетсясоответствующеезначениеизтаблицы,еслинет,невозвращается.
Например,таблицаswitchвыглядиттак:
sqlite>SELECT*fromswitch;
machostnamemodellocationmngmt_ipmngmt_vidmng
mt_vname
--------------------------------------------------------------------------
--------
0000.DDDD.DDDDsw1Cisco3850London,GreenStr10.255.0.1255MNG
MT
0000.BBBB.CCCCsw5Cisco3850London,GreenStr10.255.0.5255MNG
MT
0000.2222.CCCCsw2Cisco3750London,GreenStr10.255.0.2255MNG
MT
0000.3333.CCCCsw3Cisco3750London,GreenStr10.255.0.3255MNG
MT
0000.4444.CCCCsw4Cisco3650London,GreenStr10.255.0.4255MNG
MT
Показатьтолькотекоммутаторы,моделькоторых3750:
sqlite>SELECT*fromswitchWHEREmodel='Cisco3750';
machostnamemodellocationmngmt_ipmngmt_vidmng
mt_vname
--------------------------------------------------------------------------
--------
0000.2222.CCCCsw2Cisco3750London,GreenStr10.255.0.2255MNG
MT
0000.3333.CCCCsw3Cisco3750London,GreenStr10.255.0.3255MNG
MT
Операторwhereпозволяетуказыватьнетолькоконкретноезначениеполя.Еслидобавитькнемуоператорlike,можноуказыватьшаблонполя.
LIKEспомощьюсимволов_и%указываетначтодолжнобытьпохожезначение:
_-обозначаетодинсимволиличисло%-обозначаетноль,одинилимногосимволов
WHERE
279
Например,еслиполеmodelзаписановразномформате,спомощьюпредыдущегозапроса,неполучитсявывестинужныекоммутаторы.
Например,есливтаблицеполеmodelзаписановразномформате:
sqlite>SELECT*fromswitch;
machostnamemodellocationmngmt_ipmngmt_vidmng
mt_vname
--------------------------------------------------------------------------
--------
0000.DDDD.DDDDsw1Cisco3850London,GreenStr10.255.0.1255MNG
MT
0000.BBBB.CCCCsw5Cisco3850London,GreenStr10.255.0.5255MNG
MT
0000.2222.CCCCsw2C3750London,GreenStr10.255.0.2255MNG
MT
0000.3333.CCCCsw3Cisco3750London,GreenStr10.255.0.3255MNG
MT
0000.4444.CCCCsw4Cisco3650London,GreenStr10.255.0.4255MNG
MT
ВтакомвариантепредыдущийзапроссоператоромWHEREнепоможет:
sqlite>SELECT*fromswitchWHEREmodel='Cisco3750';
machostnamemodellocationmngmt_ipmngmt_vidmng
mt_vname
--------------------------------------------------------------------------
--------
0000.3333.CCCCsw3Cisco3750London,GreenStr10.255.0.3255MNG
MT
Но,есливместесоператоромWHEREиспользоватьоператорLIKE:
sqlite>SELECT*fromswitchWHEREmodelLIKE'%3750';
machostnamemodellocationmngmt_ipmngmt_vidmng
mt_vname
--------------------------------------------------------------------------
--------
0000.2222.CCCCsw2C3750London,GreenStr10.255.0.2255MNG
MT
0000.3333.CCCCsw3Cisco3750London,GreenStr10.255.0.3255MNG
MT
WHERE
280
ALTER
Операторalterпозволяетменятьсуществующуютаблицу:добавлятьновыеколонкиилипереименовыватьтаблицу.
Добавимвтаблицуновыеполя:
mngmt_ip-IP-адрескоммутаторавменеджментVLANmngmt_vid-VLANID(номерVLAN)дляменеджментVLANmngmt_vname-ИмяVLAN,которыйиспользуетсядляменеджмента
ДобавлениезаписейспомощьюкомандыALTER:
sqlite>ALTERtableswitchADDCOLUMNmngmt_iptext;
sqlite>ALTERtableswitchADDCOLUMNmngmt_vidvarchar(10);
sqlite>ALTERtableswitchADDCOLUMNmngmt_vnametext;
Теперьтаблицавыглядиттак(новыеполяустановленывзначениеNULL):
sqlite>SELECT*fromswitch;
machostnamemodellocationmngmt_ipmngmt_vidmng
mt_vname
--------------------------------------------------------------------------
--------
0000.AAAA.CCCCsw1Cisco3750London,GreenStr
0000.BBBB.CCCCsw5Cisco3850London,GreenStr
ALTER
281
UPDATE
Операторupdateиспользуетсядляизменениясуществующейзаписитаблицы.
Обычно,updateиспользуетсявместесоператоромwhere,чтобыуточнитькакуюименнозаписьнеобходимоизменить.
Например,предположим,чтоsw1былзамененсмодели3750намодель3850.Соответственно,изменилосьнетолькополемодель,ноиполеMAC-адрес.
Внесениеизменений:
sqlite>UPDATEswitchsetmodel='Cisco3850'wherehostname='sw1';
sqlite>UPDATEswitchsetmac='0000.DDDD.DDDD'wherehostname='sw1';
Результатбудеттаким:
sqlite>SELECT*fromswitch;
machostnamemodellocationmngmt_ipmngmt_vidmng
mt_vname
--------------------------------------------------------------------------
--------
0000.DDDD.DDDDsw1Cisco3850London,GreenStr10.255.0.1255MNG
MT
0000.BBBB.CCCCsw5Cisco3850London,GreenStr10.255.0.5255MNG
MT
Обновлениенесколькихполей:
sqlite>UPDATEswitchsetmac='0000.EEEE.EEEE',model='Cisco3750'wherehostname
='sw1';
sqlite>select*fromswitch;
machostnamemodellocationmngmt_ipmngmt_vidmng
mt_vname
--------------------------------------------------------------------------
--------
0000.EEEE.EEEEsw1Cisco3750London,GreenStr10.255.0.1255MNG
MT
0000.BBBB.CCCCsw5Cisco3850London,GreenStr10.255.0.5255MNG
MT
UPDATE
282
REPLACE
Операторreplaceиспользуетсядлядобавленияилизаменыданныхвтаблице.
ОператорreplaceможетподдерживатьсяневовсехСУБД.
Когдавозникаетнарушениеусловияуникальностиполя,выражениесоператоромreplace:
удаляетсуществующуюстроку,котораявызваланарушениедобавляетновуюстроку
Увыраженияreplaceестьдвавида:
sqlite>INSERTORREPLACEINTOswitch
...>VALUES('0000.DDDD.CCCC','sw4','Cisco3850','London,GreenStr');
Илиболеекороткийвариант:
sqlite>REPLACEINTOswitch
...>VALUES('0000.DDDD.CCCC','sw4','Cisco3850','London,GreenStr');
Примериспользованияоператораreplace
Примертаблицы:
sqlite>createtableswitch(mactextprimarykey,hostnametext,modeltext,location
text);
sqlite>INSERTintoswitchvalues('0000.AAAA.CCCC','sw1','Cisco3750','London,Gre
enStr');
sqlite>INSERTintoswitchvalues('0000.BBBB.CCCC','sw2','Cisco3850','London,Gre
enStr');
sqlite>INSERTintoswitchvalues('0000.CCCC.CCCC','sw3','Cisco3850','London,Gre
enStr');
sqlite>select*fromswitch;
machostnamemodellocation
---------------------------------------------------
0000.AAAA.CCCCsw1Cisco3750London,GreenStr
0000.BBBB.CCCCsw2Cisco3850London,GreenStr
0000.CCCC.CCCCsw3Cisco3850London,GreenStr
Придобавлениизаписи,длякоторойневозникаетнарушенияуникальностиполя,replaceработаеткакобычныйinsert:
REPLACE
283
sqlite>replaceintoswitchvalues('0000.DDDD.CCCC','sw4','Cisco3850','London,Gr
eenStr');
sqlite>select*fromswitch;
machostnamemodellocation
---------------------------------------------------
0000.AAAA.CCCCsw1Cisco3750London,GreenStr
0000.BBBB.CCCCsw2Cisco3850London,GreenStr
0000.CCCC.CCCCsw3Cisco3850London,GreenStr
0000.DDDD.CCCCsw4Cisco3850London,GreenStr
Но,есливозникаетнарушение,выполняетсязамена:
sqlite>insertorreplaceintoswitchvalues('0000.DDDD.CCCC','sw5','Cisco3850','
London,GreenStr');
sqlite>select*fromswitch;
machostnamemodellocation
---------------------------------------------------
0000.AAAA.CCCCsw1Cisco3750London,GreenStr
0000.BBBB.CCCCsw2Cisco3850London,GreenStr
0000.CCCC.CCCCsw3Cisco3850London,GreenStr
0000.DDDD.CCCCsw5Cisco3850London,GreenStr
ВданномслучаеMAC-адресвновойзаписисовпадаетсужесуществующей,поэтомупроисходитзамена.
Еслибылиуказаныневсеполя,вновойзаписибудуттолькотеполя,которыебылиуказаны:
sqlite>replaceintoswitch(mac,hostname,model)
...>values('0000.DDDD.CCCC','sw5','Cisco3850');
sqlite>select*fromswitch;
machostnamemodellocation
---------------------------------------------------
0000.AAAA.CCCCsw1Cisco3750London,GreenStr
0000.BBBB.CCCCsw2Cisco3850London,GreenStr
0000.CCCC.CCCCsw3Cisco3850London,GreenStr
0000.DDDD.CCCCsw5Cisco3850
Этосвязаностем,чтоreplaceсначалаудаляетсуществующуюзапись.
REPLACE
284
DELETE
Операторdeleteиспользуетсядляудалениязаписей.
Какправило,ониспользуетсявместесоператоромwhere.
Например:
sqlite>DELETEfromswitchwherehostname='sw4';
DELETE
285
ORDERBY
Иещеодинполезныйоператор-ORDERBY.Ониспользуетсядлясортировкивыводапоопределенномуполю,повозрастаниюилиубыванию.
Например,выведемвсезаписивтаблицеswitchиотсортируемихпоименикоммутаторов:
sqlite>SELECT*fromswitchORDERBYhostnameASC;
machostnamemodellocationmngmt_ipmngmt_vidmng
mt_vname
--------------------------------------------------------------------------
--------
0000.DDDD.DDDDsw1Cisco3850London,GreenStr10.255.0.1255MNG
MT
0000.2222.CCCCsw2C3750London,GreenStr10.255.0.2255MNG
MT
0000.3333.CCCCsw3Cisco3750London,GreenStr10.255.0.3255MNG
MT
0000.BBBB.CCCCsw5Cisco3850London,GreenStr10.255.0.5255MNG
MT
Поумолчаниюсортировкавыполняетсяповозрастанию,поэтомувзапросеможнобылонеуказыватьпараметрASC.
ORDERBY
286
Модульsqlite3ДляработысSQLiteвPythonиспользуетсямодульsqlite3.
Connection
ОбъектConnection-этоподключениекконкретнойБД.Можносказать,чтоэтотобъектпредставляетБД.
Примерсозданияподключения:
importsqlite3
connection=sqlite3.connect('dhcp_snooping.db')
Cursor
Послесозданиясоединения,надосоздатьобъектCursor-этоосновнойспособработысБД.
СоздаетсякурсоризсоединениясБД:
connection=sqlite3.connect('dhcp_snooping.db')
cursor=connection.cursor()
ВыполнениекомандSQL
ДлявыполнениякомандSQLвмодулеестьнесколькометодов:
execute()-методдлявыполненияодноговыраженияSQLexecutemany()-методпозволяетвыполнитьодновыражениеSQLдляпоследовательностипараметров(илидляитератора)executescript()-методпозволяетвыполнитьнескольковыраженийSQLзаодинраз
Методexecute
МетодexecuteпозволяетвыполнитьоднукомандуSQL.
Сначаланадосоздатьсоединениеикурсор:
Модульsqlite3
287
In[1]:importsqlite3
In[2]:connection=sqlite3.connect('sw_inventory.db')
In[3]:cursor=connection.cursor()
Созданиетаблицыswitchспомощьюметодаexecute:
In[4]:cursor.execute("createtableswitch(mactextprimarykey,hostnametext,mode
ltext,locationtext)")
Out[4]:<sqlite3.Cursorat0x1085be880>
ВыраженияSQLмогутбытьпараметризированы-вместоданныхможноподставлятьспециальныезначения.ЗасчетэтогоможноиспользоватьоднуитужекомандуSQLдляпередачиразныхданных.
Например,таблицуswitchнужнозаполнитьданнымиизспискаdata:
In[5]:data=[
...:('0000.AAAA.CCCC','sw1','Cisco3750','London,GreenStr'),
...:('0000.BBBB.CCCC','sw2','Cisco3780','London,GreenStr'),
...:('0000.AAAA.DDDD','sw3','Cisco2960','London,GreenStr'),
...:('0011.AAAA.CCCC','sw4','Cisco3750','London,GreenStr')]
Дляэтогоможноиспользоватьзапросвида:
In[6]:query="INSERTintoswitchvalues(?,?,?,?)"
Знакивопросавкомандеиспользуютсядляподстановкиданных,которыебудутпередаватьсяметодуexecute.
Теперьможнопередатьданныетакимобразом:
In[7]:forrowindata:
...:cursor.execute(query,row)
...:
Второйаргумент,которыйпередаетсяметодуexecute,долженбытькортежем.Еслинужнопередатькортежсоднимэлементом,используетсязапись(value,).
Чтобыизмененияприменились,нужновыполнитьcommit(обратитевнимание,чтометодcommitвызываетсяусоединения):
Модульsqlite3
288
In[8]:connection.commit()
Теперь,призапросеизкоманднойстрокиsqlite3,можноувидетьэтистрокивтаблицеswitch:
$sqlite3sw_inventory.db
sqlite>select*fromswitch;
machostnamemodellocation
---------------------------------------------------
0000.AAAA.CCCCsw1Cisco3750London,GreenStr
0000.BBBB.CCCCsw2Cisco3780London,GreenStr
0000.AAAA.DDDDsw3Cisco2960London,GreenStr
0011.AAAA.CCCCsw4Cisco3750London,GreenStr
Методexecutemany
МетодexecutemanyпозволяетвыполнитьоднукомандуSQLдляпоследовательностипараметров(илидляитератора).
Спомощьюметодаexecutemany,втаблицуswitchможнодобавитьаналогичныйсписокданныходнойкомандой.
Например,втаблицуswitchнадодобавитьданныеизспискаdata2:
In[9]:data2=[
...:('0000.1111.0001','sw5','Cisco3750','London,GreenStr'),
...:('0000.1111.0002','sw6','Cisco3750','London,GreenStr'),
...:('0000.1111.0003','sw7','Cisco3750','London,GreenStr'),
...:('0000.1111.0004','sw8','Cisco3750','London,GreenStr')]
Дляэтогонужноиспользоватьаналогичныйзапросвида:
In[10]:query="INSERTintoswitchvalues(?,?,?,?)"
Теперьможнопередатьданныеметодуexecutemany:
In[11]:cursor.executemany(query,data2)
Out[11]:<sqlite3.Cursorat0x10ee5e810>
In[12]:connection.commit()
Послевыполненияcommit,данныедоступнывтаблице:
Модульsqlite3
289
sqlite>select*fromswitch;
machostnamemodellocation
---------------------------------------------------
0000.AAAA.CCCCsw1Cisco3750London,GreenStr
0000.BBBB.CCCCsw2Cisco3780London,GreenStr
0000.AAAA.DDDDsw3Cisco2960London,GreenStr
0011.AAAA.CCCCsw4Cisco3750London,GreenStr
0000.1111.0001sw5Cisco3750London,GreenStr
0000.1111.0002sw6Cisco3750London,GreenStr
0000.1111.0003sw7Cisco3750London,GreenStr
0000.1111.0004sw8Cisco3750London,GreenStr
МетодexecutemanyподставилсоответствующиекортеживкомандуSQLивседанныедобавилисьвтаблицу.
Методexecutescript
МетодexecutescriptпозволяетвыполнитьнескольковыраженийSQLзаодинраз.
Особенноудобноиспользоватьэтотметодприсозданиитаблиц:
In[14]:connection=sqlite3.connect('new_db.db')
In[15]:cursor=connection.cursor()
In[16]:cursor.executescript("""
...:createtableswitches(
...:hostnametextprimarykey,
...:locationtext
...:);
...:
...:createtabledhcp(
...:mactextprimarykey,
...:iptext,
...:vlantext,
...:interfacetext,
...:switchtextnotnullreferencesswitches(hostname)
...:);
...:""")
Out[16]:<sqlite3.Cursorat0x10efd67a0>
Получениерезультатовзапроса
Дляполучениярезультатовзапросавsqlite3естьнесколькоспособов:
использованиеметодовfetch...()-взависимостиотметодавозвращаютсяодна,несколькоиливсестроки
Модульsqlite3
290
использованиекурсоракакитератора-возвращаетсяитератор
Методfetchone
Методfetchoneвозвращаетоднустрокуданных.
Примерполученияинформацииизбазыданныхsw_inventory.db:
In[1]:importsqlite3
In[2]:connection=sqlite3.connect('sw_inventory.db')
In[3]:cursor=connection.cursor()
In[4]:cursor.execute('select*fromswitch')
Out[4]:<sqlite3.Cursorat0x104eda810>
In[5]:cursor.fetchone()
Out[5]:(u'0000.AAAA.CCCC',u'sw1',u'Cisco3750',u'London,GreenStr')
Обратитевнимание,чтохотязапросSQLподразумевает,чтозапрашивалосьвсёсодержимоетаблицы,методfetchoneвернултолькооднустроку.
Еслиповторновызватьметод,онвернетследующуюстроку:
In[6]:printcursor.fetchone()
(u'0000.BBBB.CCCC',u'sw2',u'Cisco3780',u'London,GreenStr')
Аналогичнымобразомметодбудетвозвращатьследующиестроки.Послеобработкивсехстрок,методначинаетвозвращатьNone.
Засчетэтого,методможноиспользоватьвцикле,например,так:
Модульsqlite3
291
In[7]:cursor.execute('select*fromswitch')
Out[7]:<sqlite3.Cursorat0x104eda810>
In[8]:whileTrue:
...:next_row=cursor.fetchone()
...:ifnext_row:
...:printnext_row
...:else:
...:break
...:
(u'0000.AAAA.CCCC',u'sw1',u'Cisco3750',u'London,GreenStr')
(u'0000.BBBB.CCCC',u'sw2',u'Cisco3780',u'London,GreenStr')
(u'0000.AAAA.DDDD',u'sw3',u'Cisco2960',u'London,GreenStr')
(u'0011.AAAA.CCCC',u'sw4',u'Cisco3750',u'London,GreenStr')
(u'0000.1111.0001',u'sw5',u'Cisco3750',u'London,GreenStr')
(u'0000.1111.0002',u'sw6',u'Cisco3750',u'London,GreenStr')
(u'0000.1111.0003',u'sw7',u'Cisco3750',u'London,GreenStr')
(u'0000.1111.0004',u'sw8',u'Cisco3750',u'London,GreenStr')
Методfetchmany
Методfetchmanyвозвращаетвозвращаетсписокстрокданных.
Синтаксисметода:
cursor.fetchmany([size=cursor.arraysize])
Спомощьюпараметраsize,можноуказыватькакоеколичествостроквозвращается.Поумолчанию,параметрsizeравензначениюcursor.arraysize:
In[9]:printcursor.arraysize
1
Например,такимобразомможновозвращатьпотристрокииззапроса:
Модульsqlite3
292
In[10]:cursor.execute('select*fromswitch')
Out[10]:<sqlite3.Cursorat0x104eda810>
In[11]:whileTrue:
...:three_rows=cursor.fetchmany(3)
...:ifthree_rows:
...:printthree_rows
...:else:
...:break
...:
[(u'0000.AAAA.CCCC',u'sw1',u'Cisco3750',u'London,GreenStr'),
(u'0000.BBBB.CCCC',u'sw2',u'Cisco3780',u'London,GreenStr')
(u'0000.AAAA.DDDD',u'sw3',u'Cisco2960',u'London,GreenStr')]
[(u'0011.AAAA.CCCC',u'sw4',u'Cisco3750',u'London,GreenStr'),
(u'0000.1111.0001',u'sw5',u'Cisco3750',u'London,GreenStr'),
(u'0000.1111.0002',u'sw6',u'Cisco3750',u'London,GreenStr')]
[(u'0000.1111.0003',u'sw7',u'Cisco3750',u'London,GreenStr'),
(u'0000.1111.0004',u'sw8',u'Cisco3750',u'London,GreenStr')]
Методвыдаетнужноеколичествострок,аеслистрокосталосьменьшечемпараметрsize,тооставшиесястроки.
Методfetchall
Методfetchallвозвращаетвсестрокиввидесписка:
In[12]:cursor.execute('select*fromswitch')
Out[12]:<sqlite3.Cursorat0x104eda810>
In[13]:cursor.fetchall()
Out[13]:
[(u'0000.AAAA.CCCC',u'sw1',u'Cisco3750',u'London,GreenStr'),
(u'0000.BBBB.CCCC',u'sw2',u'Cisco3780',u'London,GreenStr'),
(u'0000.AAAA.DDDD',u'sw3',u'Cisco2960',u'London,GreenStr'),
(u'0011.AAAA.CCCC',u'sw4',u'Cisco3750',u'London,GreenStr'),
(u'0000.1111.0001',u'sw5',u'Cisco3750',u'London,GreenStr'),
(u'0000.1111.0002',u'sw6',u'Cisco3750',u'London,GreenStr'),
(u'0000.1111.0003',u'sw7',u'Cisco3750',u'London,GreenStr'),
(u'0000.1111.0004',u'sw8',u'Cisco3750',u'London,GreenStr')]
Важныйаспектработыметода-онвозвращаетвсеоставшиесястроки.
Тоесть,еслидометодаfetchall,использовался,например,методfetchone,тометодfetchallвернетоставшиесястрокизапроса:
Модульsqlite3
293
In[14]:cursor.execute('select*fromswitch')
Out[14]:<sqlite3.Cursorat0x104eda810>
In[15]:cursor.fetchone()
Out[15]:(u'0000.AAAA.CCCC',u'sw1',u'Cisco3750',u'London,GreenStr')
In[16]:cursor.fetchone()
Out[16]:(u'0000.BBBB.CCCC',u'sw2',u'Cisco3780',u'London,GreenStr')
In[17]:cursor.fetchall()
Out[17]:
[(u'0000.AAAA.DDDD',u'sw3',u'Cisco2960',u'London,GreenStr'),
(u'0011.AAAA.CCCC',u'sw4',u'Cisco3750',u'London,GreenStr'),
(u'0000.1111.0001',u'sw5',u'Cisco3750',u'London,GreenStr'),
(u'0000.1111.0002',u'sw6',u'Cisco3750',u'London,GreenStr'),
(u'0000.1111.0003',u'sw7',u'Cisco3750',u'London,GreenStr'),
(u'0000.1111.0004',u'sw8',u'Cisco3750',u'London,GreenStr')]
Методfetchmany,вэтомаспекте,работаетаналогично.
Cursorкакитератор
Еслинужнопострочнообрабатыватьрезультирующиестроки,лучшеиспользоватькурсоркакитератор.Приэтомненужноиспользоватьметодыfetch.
Прииспользованииметодовexecute,возвращаетсякурсор.А,таккаккурсорможноиспользоватькакитератор,можноиспользоватьего,например,вциклеfor:
In[18]:result=cursor.execute('select*fromswitch')
In[19]:forrowinresult:
...:printrow
...:
(u'0000.AAAA.CCCC',u'sw1',u'Cisco3750',u'London,GreenStr')
(u'0000.BBBB.CCCC',u'sw2',u'Cisco3780',u'London,GreenStr')
(u'0000.AAAA.DDDD',u'sw3',u'Cisco2960',u'London,GreenStr')
(u'0011.AAAA.CCCC',u'sw4',u'Cisco3750',u'London,GreenStr')
(u'0000.1111.0001',u'sw5',u'Cisco3750',u'London,GreenStr')
(u'0000.1111.0002',u'sw6',u'Cisco3750',u'London,GreenStr')
(u'0000.1111.0003',u'sw7',u'Cisco3750',u'London,GreenStr')
(u'0000.1111.0004',u'sw8',u'Cisco3750',u'London,GreenStr')
И,конечноже,аналогичныйвариантотработаетибезприсваиванияпеременной:
Модульsqlite3
294
In[20]:forrowincursor.execute('select*fromswitch'):
...:printrow
...:
(u'0000.AAAA.CCCC',u'sw1',u'Cisco3750',u'London,GreenStr')
(u'0000.BBBB.CCCC',u'sw2',u'Cisco3780',u'London,GreenStr')
(u'0000.AAAA.DDDD',u'sw3',u'Cisco2960',u'London,GreenStr')
(u'0011.AAAA.CCCC',u'sw4',u'Cisco3750',u'London,GreenStr')
(u'0000.1111.0001',u'sw5',u'Cisco3750',u'London,GreenStr')
(u'0000.1111.0002',u'sw6',u'Cisco3750',u'London,GreenStr')
(u'0000.1111.0003',u'sw7',u'Cisco3750',u'London,GreenStr')
(u'0000.1111.0004',u'sw8',u'Cisco3750',u'London,GreenStr')
Использованиемодуляsqlite3
Безявногосозданиякурсора
МетодыexecuteдоступныивобъектеConnection.Приихиспользованиикурсорсоздается,нонеявно.Однако,методыfetchвConnectionнедоступны.
Но,еслииспользоватькурсор,которыйвозвращаютметодыexecute,какитератор,методыfetchмогутинепонадобиться.
Примеритоговогоскрипта(файлcreate_sw_inventory_ver1.py):
#-*-coding:utf-8-*-
importsqlite3
data=[('0000.AAAA.CCCC','sw1','Cisco3750','London,GreenStr'),
('0000.BBBB.CCCC','sw2','Cisco3780','London,GreenStr'),
('0000.AAAA.DDDD','sw3','Cisco2960','London,GreenStr'),
('0011.AAAA.CCCC','sw4','Cisco3750','London,GreenStr')]
con=sqlite3.connect('sw_inventory2.db')
con.execute("createtableswitch(mactextprimarykey,hostnametext,modeltext,loc
ationtext)")
query="INSERTintoswitchvalues(?,?,?,?)"
con.executemany(query,data)
con.commit()
forrowincon.execute("select*fromswitch"):
printrow
con.close()
Результатвыполнениябудеттаким:
Модульsqlite3
295
$pythoncreate_sw_inventory_ver1.py
(u'0000.AAAA.CCCC',u'sw1',u'Cisco3750',u'London,GreenStr')
(u'0000.BBBB.CCCC',u'sw2',u'Cisco3780',u'London,GreenStr')
(u'0000.AAAA.DDDD',u'sw3',u'Cisco2960',u'London,GreenStr')
(u'0011.AAAA.CCCC',u'sw4',u'Cisco3750',u'London,GreenStr')
Обработкаисключений
Посмотримнапримериспользованияметодаexecute,привозникновенииошибки.
Втаблицеswitchполеmacдолжнобытьуникальным.И,еслипопытатьсязаписатьпересекающийсяMAC-адрес,возникнетошибка:
In[22]:con=sqlite3.connect('sw_inventory2.db')
In[23]:query="INSERTintoswitchvalues('0000.AAAA.DDDD','sw7','Cisco2960','L
ondon,GreenStr')"
In[24]:con.execute(query)
---------------------------------------------------------------------------
IntegrityErrorTraceback(mostrecentcalllast)
<ipython-input-56-ad34d83a8a84>in<module>()
---->1con.execute(query)
IntegrityError:UNIQUEconstraintfailed:switch.mac
Соответственно,можноперехватитьисключение:
In[25]:try:
...:con.execute(query)
...:exceptsqlite3.IntegrityErrorase:
...:print"Erroroccured:",e
...:
Erroroccured:UNIQUEconstraintfailed:switch.mac
Обратитевнимение,чтонадоперехватыватьисключениеsqlite3.IntegrityError,анеIntegrityError.
Connectionкакменеджерконтекста
Послевыполненияопераций,изменениядолжныбытьсохранены(надовыполнитьcommit()),азатемможнозакрытьсоединение,еслионобольшененужно.
PythonпозволяетиспользоватьобъектConnection,какменеджерконтекста.Втакомслучае,ненужноявноделатьcommitизакрыватьсоединение.Приэтом:
Модульsqlite3
296
привозникновенииисключения,транзакцияавтоматическиоткатываетсяеслиисключениянебыло,автоматическивыполняетсяcommit
Примериспользованиясоединениясбазой,какменеджераконтекстов(create_sw_inventory_ver2.py):
#-*-coding:utf-8-*-
importsqlite3
data=[('0000.AAAA.CCCC','sw1','Cisco3750','London,GreenStr'),
('0000.BBBB.CCCC','sw2','Cisco3780','London,GreenStr'),
('0000.AAAA.DDDD','sw3','Cisco2960','London,GreenStr'),
('0011.AAAA.CCCC','sw4','Cisco3750','London,GreenStr')]
con=sqlite3.connect('sw_inventory3.db')
con.execute("createtableswitch(mactextprimarykey,hostnametext,modeltext,loc
ationtext)")
try:
withcon:
query="INSERTintoswitchvalues(?,?,?,?)"
con.executemany(query,data)
exceptsqlite3.IntegrityErrorase:
print"Erroroccured:",e
forrowincon.execute("select*fromswitch"):
printrow
Обратитевнимание,чтохотятранзакциябудетоткатываться,привозникновенииисключения,самоисключениевсёравнонадоперехватывать.
Дляпроверкиэтогофункционала,попробуемзаписатьвтаблицуданные,вкоторыхMAC-адресповторяется(файлcreate_sw_inventory_ver3.py):
Модульsqlite3
297
#-*-coding:utf-8-*-
importsqlite3
data=[('0000.AAAA.CCCC','sw1','Cisco3750','London,GreenStr'),
('0000.BBBB.CCCC','sw2','Cisco3780','London,GreenStr'),
('0000.AAAA.DDDD','sw3','Cisco2960','London,GreenStr'),
('0011.AAAA.CCCC','sw4','Cisco3750','London,GreenStr')]
con=sqlite3.connect('sw_inventory3.db')
con.execute("createtableswitch(mactextprimarykey,hostnametext,modeltext,loc
ationtext)")
try:
withcon:
query="INSERTintoswitchvalues(?,?,?,?)"
con.executemany(query,data)
exceptsqlite3.IntegrityErrorase:
print"Erroroccured:",e
forrowincon.execute("select*fromswitch"):
printrow
print'-'*30
#MAC-адресsw7совпадаетсMAC-адресомсуществующегокоммутатора-sw3
data2=[('0055.AAAA.CCCC','sw5','Cisco3750','London,GreenStr'),
('0066.BBBB.CCCC','sw6','Cisco3780','London,GreenStr'),
('0000.AAAA.DDDD','sw7','Cisco2960','London,GreenStr'),
('0088.AAAA.CCCC','sw8','Cisco3750','London,GreenStr')]
try:
withcon:
query="INSERTintoswitchvalues(?,?,?,?)"
con.executemany(query,data2)
exceptsqlite3.IntegrityErrorase:
print"Erroroccured:",e
forrowincon.execute("select*fromswitch"):
printrow
Перваячастьскриптаосталасьнеизменной.После,аналогичнымобразомзаписываютсяданные,новспискеdata2укоммутатораsw7MAC-адрессовпадаетсужесуществующимкоммутаторомsw3.
Результатвыполненияскрипта:
Модульsqlite3
298
$pythoncreate_sw_inventory_ver3.py
(u'0000.AAAA.CCCC',u'sw1',u'Cisco3750',u'London,GreenStr')
(u'0000.BBBB.CCCC',u'sw2',u'Cisco3780',u'London,GreenStr')
(u'0000.AAAA.DDDD',u'sw3',u'Cisco2960',u'London,GreenStr')
(u'0011.AAAA.CCCC',u'sw4',u'Cisco3750',u'London,GreenStr')
------------------------------
Erroroccured:UNIQUEconstraintfailed:switch.mac
(u'0000.AAAA.CCCC',u'sw1',u'Cisco3750',u'London,GreenStr')
(u'0000.BBBB.CCCC',u'sw2',u'Cisco3780',u'London,GreenStr')
(u'0000.AAAA.DDDD',u'sw3',u'Cisco2960',u'London,GreenStr')
(u'0011.AAAA.CCCC',u'sw4',u'Cisco3750',u'London,GreenStr')
Обратитевнимание,чтосодержимоетаблицыswitchдоипослевторогодобавленияинформации-одинаково.Этозначит,чтонезаписаласьниоднастрокаизспискаdata2.
Такполучилосьиз-затого,чтоиспользуетсяметодexecutemanyивпределаходнойтранзакциимыпытаемсязаписатьвсе4строки.Есливозникаетошибкасоднойизних-откатываютсявсеизменения.
Иногда,этоименнотоповедение,котороенужно.Еслиженадочтобыигнорировалисьтолькострокисошибками,надоиспользоватьметодexecuteизаписыватькаждуюстрокуотдельно.
Файлcreate_sw_inventory_ver4.py:
Модульsqlite3
299
#-*-coding:utf-8-*-
importsqlite3
data=[('0000.AAAA.CCCC','sw1','Cisco3750','London,GreenStr'),
('0000.BBBB.CCCC','sw2','Cisco3780','London,GreenStr'),
('0000.AAAA.DDDD','sw3','Cisco2960','London,GreenStr'),
('0011.AAAA.CCCC','sw4','Cisco3750','London,GreenStr')]
con=sqlite3.connect('sw_inventory3.db')
con.execute("createtableswitch(mactextprimarykey,hostnametext,modeltext,loc
ationtext)")
try:
withcon:
query="INSERTintoswitchvalues(?,?,?,?)"
con.executemany(query,data)
exceptsqlite3.IntegrityErrorase:
print"Erroroccured:",e
forrowincon.execute("select*fromswitch"):
printrow
print'-'*30
#MAC-адресsw7совпадаетсMAC-адресомсуществующегокоммутатора-sw3
data2=[('0055.AAAA.CCCC','sw5','Cisco3750','London,GreenStr'),
('0066.BBBB.CCCC','sw6','Cisco3780','London,GreenStr'),
('0000.AAAA.DDDD','sw7','Cisco2960','London,GreenStr'),
('0088.AAAA.CCCC','sw8','Cisco3750','London,GreenStr')]
forrowindata2:
try:
withcon:
query="INSERTintoswitchvalues(?,?,?,?)"
con.execute(query,row)
exceptsqlite3.IntegrityErrorase:
print"Erroroccured:",e
forrowincon.execute("select*fromswitch"):
printrow
Теперьрезультатвыполнениябудеттаким(пропущентолькоsw7):
Модульsqlite3
300
$pythoncreate_sw_inventory_ver4.py
(u'0000.AAAA.CCCC',u'sw1',u'Cisco3750',u'London,GreenStr')
(u'0000.BBBB.CCCC',u'sw2',u'Cisco3780',u'London,GreenStr')
(u'0000.AAAA.DDDD',u'sw3',u'Cisco2960',u'London,GreenStr')
(u'0011.AAAA.CCCC',u'sw4',u'Cisco3750',u'London,GreenStr')
------------------------------
Erroroccured:UNIQUEconstraintfailed:switch.mac
(u'0000.AAAA.CCCC',u'sw1',u'Cisco3750',u'London,GreenStr')
(u'0000.BBBB.CCCC',u'sw2',u'Cisco3780',u'London,GreenStr')
(u'0000.AAAA.DDDD',u'sw3',u'Cisco2960',u'London,GreenStr')
(u'0011.AAAA.CCCC',u'sw4',u'Cisco3750',u'London,GreenStr')
(u'0055.AAAA.CCCC',u'sw5',u'Cisco3750',u'London,GreenStr')
(u'0066.BBBB.CCCC',u'sw6',u'Cisco3780',u'London,GreenStr')
(u'0088.AAAA.CCCC',u'sw8',u'Cisco3750',u'London,GreenStr')
Блоксменеджеромконтекстаповторяется,поэтомуконечножеможносделатьфункцию,вкоторойбудетнаходитьсяподобныйблоккода.
Модульsqlite3
301
ПримериспользованияSQLiteВразделеРегулярныевыражениябылпримерразборавыводакомандыshowipdhcpsnoopingbinding.Навыходе,мыполучилиинформациюопараметрахподключенныхустройств(interface,IP,MAC,VLAN).
Втакомвариантеможнопосмотретьтольковсеподключенныеустройстваккоммутатору.Еслиженужноузнатьнаоснованииодногоизпараметров,другие,товтакомвидеэтонеоченьудобно.
Например,еслинужнопоIP-адресуполучитьинформациюотом,ккакомуинтерфейсуподключенкомпьютер,какойунегоMAC-адресивкакомонVLAN,топовыводускриптаэтосделатьнеоченьпростои,главное,неоченьудобно.
ЗапишеминформациюполученнуюизвыводаshipdhcpsnoopingbindingвSQLite.Этопозволитделатьзапросыполюбомупараметруиполучатьнедостающие.
Дляэтогопримера,достаточносоздатьоднутаблицу,гдебудетхранитьсяинформация.
Определениетаблицыпрописановотдельномфайлеdhcp_snooping_schema.sqlивыглядиттак:
createtabledhcp(
mactextprimarykey,
iptext,
vlantext,
interfacetext
);
Длявсехполейопределентипданных"текст".ИMAC-адресявляетсяпервичнымключомнашейтаблицы.Чтовполнелогично,таккак,MAC-адресдолженбытьуникальным.
ТеперьнадосоздатьфайлБД,подключитьсякбазеданныхисоздатьтаблицу(файлcreate_sqlite_ver1.py):
ПримериспользованияSQLite
302
importsqlite3
withsqlite3.connect('dhcp_snooping.db')asconn:
print'Creatingschema...'
withopen('dhcp_snooping_schema.sql','r')asf:
schema=f.read()
conn.executescript(schema)
print"Done"
Комментариикфайлу:
используетсяменеджерконтекстаwithпривыполнениистрокиwithsqlite3.connect('dhcp_snooping.db')asconn:
создаетсяфайлdhcp_snooping.db,еслиегонетсоздаетсяобъектConnection
вБДсоздаетсятаблица,наоснованиикоманд,которыеуказанывфайлеdhcp_snooping_schema.sql:
открываемфайлdhcp_snooping_schema.sqlschema=f.read()-считываемвесьфайлкакоднустрокуconn.executescript(schema)-методexecutescriptпозволяетвыполнятькомандыSQL,которыепрописанывфайле
Выполняемскрипт:
$pythoncreate_sqlite_ver1.py
Creatingschema...
Done
ВрезультатедолженбытьсозданфайлБДитаблицаdhcp.
Проверить,чтотаблицасоздалась,можноспомощьюутилитыsqlite3,котораяпозволяетвыполнятьзапросыпрямовкоманднойстроке.
Выведемсписоксозданныхтаблиц(запростакоговидапозволяетпроверитькакиетаблицысозданывDB):
$sqlite3dhcp_snooping.db"SELECTnameFROMsqlite_masterWHEREtype='table'"
dhcp
Теперьнужнозаписатьинформациюизвыводакомандыshipdhcpsnoopingbindingвтаблицу(файлdhcp_snooping.txt):
ПримериспользованияSQLite
303
MacAddressIpAddressLease(sec)TypeVLANInterface
----------------------------------------------------------------------------
----
00:09:BB:3D:D6:5810.1.10.286250dhcp-snooping10FastEthernet0/1
00:04:A3:3E:5B:6910.1.5.263951dhcp-snooping5FastEthernet0/1
0
00:05:B3:7E:9B:6010.1.5.463253dhcp-snooping5FastEthernet0/9
00:09:BC:3F:A6:5010.1.10.676260dhcp-snooping10FastEthernet0/3
Totalnumberofbindings:4
Вовторойверсиискрипта,сначалавыводвфайлеdhcp_snooping.txtобрабатываетсярегулярнымивыражениями,азатем,добавляютсязаписивБД(файлcreate_sqlite3_ver2.py):
importsqlite3
importre
regex=re.compile('(.+?)+(.*?)+\d++[\w-]++(\d+)+(.*$)')
result=[]
withopen('dhcp_snooping.txt')asdata:
forlineindata:
ifline[0].isdigit():
result.append(regex.search(line).groups())
withsqlite3.connect('dhcp_snooping.db')asconn:
print'Creatingschema...'
withopen('dhcp_snooping_schema.sql','r')asf:
schema=f.read()
conn.executescript(schema)
print"Done"
print'InsertingDHCPSnoopingdata'
forrowinresult:
query="""insertintodhcp(mac,ip,vlan,interface)
values(?,?,?,?)"""
conn.execute(query,row)
Покачто,файлБДкаждыйразнадоудалять,таккакскриптпытаетсяегосоздатьприкаждомзапуске.
Комментариикскрипту:
врегулярномвыражении,котороепроходитсяповыводукомандыshipdhcpsnoopingbinding,используютсянеименованныегруппы,каквпримереразделаРегулярныевыражения,анумерованные
ПримериспользованияSQLite
304
группысозданытолькодлятехэлементов,которыенасинтересуютresult-этосписок,вкоторомхранитсярезультатобработкивыводакоманды
нотеперьтутнесловари,акортежисрезультатамиэтонужнодлятого,чтобыихможнобылосразупередаватьназаписьвБД
Перебираемвполученномспискекортежей,элементыВэтомскриптеиспользуетсяещеодинвариантзаписивБД
строкаqueryописываетзапрос.Но,вместозначенийуказываютсязнакивопроса.Такойвариантзаписизапроса,позволяетдинамическиподставлятьзначениеполейзатемметодуexecuteпередаетсястроказапросаикортежrow,гденаходятсязначения
Выполняемскрипт:
$pythoncreate_sqlite_ver2.py
Creatingschema...
Done
InsertingDHCPSnoopingdata
Проверим,чтоданныезаписались:
$sqlite3dhcp_snooping.db"select*fromdhcp"
00:09:BB:3D:D6:58|10.1.10.2|10|FastEthernet0/1
00:04:A3:3E:5B:69|10.1.5.2|5|FastEthernet0/10
00:05:B3:7E:9B:60|10.1.5.4|5|FastEthernet0/9
00:07:BC:3F:A6:50|10.1.10.6|10|FastEthernet0/3
Теперьпопробуемзапроситьпоопределенномупараметру:
$sqlite3dhcp_snooping.db"select*fromdhcpwhereip='10.1.5.2'"
00:04:A3:3E:5B:69|10.1.5.2|5|FastEthernet0/10
Тоесть,теперьнаоснованииодногопараметра,можнополучатьостальные.
Переделаемнашскрипттакимобразом,чтобывнёмбылапроверкананаличиефайлаdhcp_snooping.db.ЕслифайлБДесть,тоненадосоздаватьтаблицу,считаем,чтоонаужесоздана.
Файлcreate_sqlite_ver3.py:
ПримериспользованияSQLite
305
importos
importsqlite3
importre
data_filename='dhcp_snooping.txt'
db_filename='dhcp_snooping.db'
schema_filename='dhcp_snooping_schema.sql'
regex=re.compile('(.+?)+(.*?)+\d++[\w-]++(\d+)+(.*$)')
withopen(data_filename)asdata:
result=[regex.search(line).groups()forlineindataifline[0].isdigit()]
db_exists=os.path.exists(db_filename)
withsqlite3.connect(db_filename)asconn:
ifnotdb_exists:
print'Creatingschema...'
withopen(schema_filename,'r')asf:
schema=f.read()
conn.executescript(schema)
print'Done'
else:
print'Databaseexists,assumedhcptabledoes,too.'
print'InsertingDHCPSnoopingdata'
forvalinresult:
query="""insertintodhcp(mac,ip,vlan,interface)
values(?,?,?,?)"""
conn.execute(query,val)
ТеперьестьпроверканаличияфайлаБД,ифайлdhcp_snooping.dbбудетсоздаватьсятольковтомслучае,еслиегонет.Данныетакжезаписываютсятольковтомслучае,еслинесозданфайлdhcp_snooping.db.
Проверим.Вслучаееслифайлужеесть:
$pythoncreate_sqlite_ver3.py
Databaseexists,assumedhcptabledoes,too.
Еслифайланет(предварительноегоудалить):
$rmdhcp_snooping.db
$pythoncreate_sqlite_ver3.py
Creatingschema...
Done
InsertingDHCPSnoopingdata
ПримериспользованияSQLite
306
Теперьделаемотдельныйскрипт,которыйзанимаетсяотправкойзапросоввБДивыводомрезультатов.Ондолжен:
ожидатьотпользователявводапараметров:имяпараметразначениепараметра
делатьнормальныйвыводданныхпозапросу
Файлget_data_ver1.py:
#-*-coding:utf-8-*-
importsqlite3
importsys
db_filename='dhcp_snooping.db'
key,value=sys.argv[1:]
keys=['mac','ip','vlan','interface']
keys.remove(key)
withsqlite3.connect(db_filename)asconn:
#Позволяетдалееобращатьсякданнымвколонках,поимениколонки
conn.row_factory=sqlite3.Row
print"\nDetailedinformationforhost(s)with",key,value
print'-'*40
query="select*fromdhcpwhere{}=?".format(key)
result=conn.execute(query,(value,))
forrowinresult:
forkinkeys:
print"{:12}:{}".format(k,row[k])
print'-'*40
Комментариикскрипту:
изаргументов,которыепередалискрипту,считываютсяпараметрыkey,valueизспискаkeys,удаляетсявыбранныйключ.Такимобразомвспискеостаютсятолькотепараметры,которыенужновывести
подключаемсякБДconn.row_factory=sqlite3.Row-позволяетдалееобращатьсякданнымвколонках,поимениколонки
изБДвыбираютсятестроки,вкоторыхключравенуказанномузначениювSQLзначенияможноподставлятьчереззнаквопроса,нонельзяподставлятьимястолбца.Поэтому,имястолбцаподставляетсячерез
ПримериспользованияSQLite
307
форматированиестрок,азначение-штатнымсредствомSQL.Обратитевниманиена(value,)такимобразомпередаетсякортежсоднимэлементом
Полученнаяинформациювыводитсянастандартныйпотоквывода:перебираемполученныерезультатыивыводимтолькотеполя,названиякоторыхнаходятсявспискеkeys
Проверимработускрипта.
ПоказатьпараметрыхостасIP10.1.10.2:
$pythonget_data_ver1.pyip10.1.10.2
Detailedinformationforhost(s)withip10.1.10.2
----------------------------------------
mac:00:09:BB:3D:D6:58
vlan:10
interface:FastEthernet0/1
----------------------------------------
ПоказатьхостывVLAN10:
$pythonget_data_ver1.pyvlan10
Detailedinformationforhost(s)withvlan10
----------------------------------------
mac:00:09:BB:3D:D6:58
ip:10.1.10.2
interface:FastEthernet0/1
----------------------------------------
mac:00:07:BC:3F:A6:50
ip:10.1.10.6
interface:FastEthernet0/3
----------------------------------------
Втораяверсияскриптадляполученияданных,снебольшимиулучшениями:
Вместоформатированиястрок,используетсясловарь,вкоторомописанызапросы,соответствующиекаждомуключу.Выполняетсяпроверкаключа,которыйбылвыбранДляполучениязаголовковвсехстолбцов,которыйсоответствуютзапросу,используетсяметодdescription
Файлget_data_ver2.py:
ПримериспользованияSQLite
308
#-*-coding:utf-8-*-
importsqlite3
importsys
db_filename='dhcp_snooping.db'
query_dict={'vlan':"select*fromdhcpwherevlan=?",
'mac':"select*fromdhcpwheremac=?",
'ip':"select*fromdhcpwhereip=?",
'interface':"select*fromdhcpwhereinterface=?"}
key,value=sys.argv[1:]
keys=query_dict.keys()
ifnotkeyinkeys:
print"Enterkeyfrom{}".format(','.join(keys))
else:
withsqlite3.connect(db_filename)asconn:
conn.row_factory=sqlite3.Row
print"\nDetailedinformationforhost(s)with",key,value
print'-'*40
query=query_dict[key]
result=conn.execute(query,(value,))
#методdescriptionпозволяетполучитьзаголовкиполученныхстолбцов.
#Внембудутнаходитьсятолькотестолбцы,которыйсоответствуютзапросу.
all_rows=[r[0]forrinresult.description]
forrowinresult:
forrow_nameinall_rows:
print"{:12}:{}".format(row_name,row[row_name])
print'-'*40
Вэтомскриптеестьнескольконедостатков:
непроверяетсяколичествоаргументов,которыепередаютсяскриптухотелосьбысобиратьинформациюсразныхкоммутаторов.Адляэтогонадодобавитьполе,котороеуказываетнакакомкоммутаторебыланайденазапись
Крометого,многоенужнодоработатьвскрипте,которыйсоздаетБДизаписываетданные.
Вседоработкибудутвыполнятьсявупражненияхкэтомуразделу.
ПримериспользованияSQLite
309
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание11.1
Наосновефайлаcreate_sqlite_ver3.pyизраздела,необходимосоздатьдваскрипта:
create_db.pyсюдадолжнабытьвынесенафункциональностьпосозданиюБД:должнавыполнятьсяпроверканаличияфайлаБДеслифайланет,согласноописаниюсхемыБДвфайлеdhcp_snooping_schema.sql,должнабытьсозданаБД(БДотличаетсяотпримеравразделе)
add_data.pyспомощьюэтогоскрипта,выполняетсядобавлениеданныхвБДдобавлятьнадонетолькоданныеизвыводаshipdhcpsnoopingbinding,ноиинформациюокоммутаторах
Кодвскриптахдолженбытьразбитнафункции.Какиеименнофункцииикакразделитькод,надорешитьсамостоятельно.Частькодаможетбытьглобальной.
ВБДтеперьдветаблицы(схемаописанавфайлеdhcp_snooping_schema.sql):
switches-внейнаходятсяданныеокоммутаторахdhcp-этатаблицаосталасьтакойжекаквпримере,заисключениемполяswitch
этополессылаетсянаполеhostnameвтаблицеswitches
Задания
310
Соответственно,вфайлеadd_data.pyдвечасти:
информацияокоммутаторахдобавляетсявтаблицуswitchesданныеокоммутаторах,находятсявфайлеswitches.yml
информациянаоснованиивыводаshipdhcpsnoopingbindingдобавляетсявтаблицуdhcp
выводстрёхкоммутаторов:файлыsw1_dhcp_snooping.txt,sw2_dhcp_snooping.txt,sw3_dhcp_snooping.txt
таккактаблицаdhcpизменилась,ивнейтеперьприсутствуетполеswitch,егонужнотакжезаполнять.Имякоммутатораопределяетсяпоименифайласданными
Наданномэтапе,обаскриптавызываютсябезаргументов.
Задание11.1a
Добавитьвфайлadd_data.py,иззадания11.1,проверкунаналичиеБД:
еслифайлБДесть,записатьданныееслифайлаБДнет,вывестисообщение,чтоБДнетиеёнеобходимосначаласоздать
Задание11.2
Наосновефайлаget_data_ver1.pyизраздела,создатьскриптget_data.py.
Кодвскриптедолженбытьразбитнафункции.Какиеименнофункцииикакразделитькод,надорешитьсамостоятельно.Частькодаможетбытьглобальной.
Впримереизраздела,скриптупередавалисьдвааргумента:
key-имястолбца,покоторомунадонайтиинформациюvalue-значение
Теперьнеобходиморасширитьфункциональностьтакимобразом:
еслискриптбылвызванбезаргументов,вывестивсёсодержимоетаблицыdhcpотформатироватьвыводввидестолбцов
еслискриптбылвызвансдвумяаргументами,вывестиинформациюизтаблицыdhcp,котораясоответствуетполюизначениюеслискриптбылвызванслюбымдругимколичествомаргументов,вывестисообщение,чтоскриптподдерживаеттолькодваилинольаргументов
ФайлБДможноскопироватьизпрошлыхзаданий
Задания
311
Витоге,выводдолженвыглядетьтак:
$pythonget_data.py
Втаблицеdhcpтакиезаписи:
----------------------------------------------------------------------
00:09:BB:3D:D6:5810.1.10.210FastEthernet0/1sw1
00:04:A3:3E:5B:6910.1.5.25FastEthernet0/10sw1
00:05:B3:7E:9B:6010.1.5.45FastEthernet0/9sw1
00:07:BC:3F:A6:5010.1.10.610FastEthernet0/3sw1
00:09:BC:3F:A6:50192.168.1.100100FastEthernet0/5sw1
00:A9:BB:3D:D6:5810.1.10.2010FastEthernet0/7sw2
00:B4:A3:3E:5B:6910.1.5.205FastEthernet0/5sw2
00:C5:B3:7E:9B:6010.1.5.405FastEthernet0/9sw2
00:A9:BC:3F:A6:50100.1.1.63FastEthernet0/20sw3
$pythonget_data.pyip10.1.10.2
Detailedinformationforhost(s)withip10.1.10.2
----------------------------------------
mac:00:09:BB:3D:D6:58
vlan:10
interface:FastEthernet0/1
switch:sw1
----------------------------------------
$pythonget_data.pyvlan10
Detailedinformationforhost(s)withvlan10
----------------------------------------
mac:00:09:BB:3D:D6:58
ip:10.1.10.2
interface:FastEthernet0/1
switch:sw1
----------------------------------------
mac:00:07:BC:3F:A6:50
ip:10.1.10.6
interface:FastEthernet0/3
switch:sw1
----------------------------------------
mac:00:A9:BB:3D:D6:58
ip:10.1.10.20
interface:FastEthernet0/7
switch:sw2
----------------------------------------
$pythonget_data.pyvlan
Пожалуйста,введитедваилинольаргументов
Задание11.2a
Задания
312
Дополнитьскриптget_data.pyиззадания11.2
Теперьдолжнавыполнятьсяпроверканетолькопоколичествуаргументов,ноипозначениюаргументов.Еслиимяаргументавведенонеправильно,надовывестисообщениеобошибке(примерсообщенияниже).
ФайлБДможноскопироватьизпрошлыхзаданий
Витоге,выводдолженвыглядетьтак:
$pythonget_data.pyvln10
Данныйпараметрнеподдерживается.
Допустимыезначенияпараметров:mac,ip,vlan,interface,switch
Задание11.3
ВпрошлыхзаданияхинформациядобавляласьвпустуюБД.Вэтомзадании,разбираетсяситуация,когдавБДужеестьинформация.
Попробуйтевыполнитьскриптadd_data.pyповторно,насуществующейБД.Должнавозникнутьошибка.
ПрисозданиисхемыБД,былоявноуказано,чтополеMAC-адрес,должнобытьуникальным.Поэтому,придобавлениизаписистакимжеMAC-адресом,возникаетошибка.
Но,нужнокаким-тообразомобновлятьБД,чтобывнейхраниласьактуальнаяинформация.
Например,можнокаждыйраз,когдазаписываетсяинформация,предварительнопростоудалятьвсёизтаблицыdhcp.
Но,впринципе,стараяинформациятожеможетпригодиться.
Поэтому,мыбудемделатьнемногопо-другому.Создадимновоеполеactive,котороебудетуказыватьявляетсялизаписьактуальной.
Полеactiveдолжноприниматьтакиезначения:
0-означаетFalse.Ииспользуетсядлятого,чтобыотметитьзаписькакнеактивную1-True.Используетсячтобыуказать,чтозаписьактивна
Каждыйраз,когдаинформацияизфайловсвыводомDHCPsnoopingдобавляетсязаново,надопометитьвсесуществующиезаписи(дляданногокоммутатора),какнеактивные(active=0).Затемможнообновлятьинформациюипометитьновыезаписи,какактивные(active=1).
Задания
313
Такимобразом,вБДостанутсяистарыезаписи,дляMAC-адресов,которыесейчаснеактивны,ипоявитсяобновленнаяинформациядляактивныхадресов.
НоваясхемаБДнаходитсявфайлеdhcp_snooping_schema.sql
Изменитескриптadd_data.pyтакимобразом,чтобывыполнялисьновыеусловияизаполнялосьполеactive.
Кодвскриптедолженбытьразбитнафункции.Какиеименнофункцииикакразделитькод,надорешитьсамостоятельно.Частькодаможетбытьглобальной.
ДляпроверкикорректностизапросаSQL,можновыполнитьеговкоманднойстроке,спомощьюутилитыsqlite3.
Дляпроверкизаданияиработыновогополя,попробуйтеудалитьпарустрокизодногоизфаловсвыводомdhcpsnooping.Ипослеэтогопроверить,чтоудаленнестрокиотображаютсявтаблицекакнеактивные.
Задание11.4
Обновитьфайлget_dataиззадания11.2или11.2a.
Теперь,призапросеинформации,сначаладолжныотображатьсяактивныезаписи,азатем,неактивные.
Например:
$pythonget_data.pyip10.1.10.2
Detailedinformationforhost(s)withip10.1.10.2
----------------------------------------
mac:00:09:BB:3D:D6:58
vlan:10
interface:FastEthernet0/1
switch:sw1
----------------------------------------
=======================================
Inactivevalues:
----------------------------------------
mac:00:09:23:34:16:18
vlan:10
interface:FastEthernet0/4
switch:sw1
----------------------------------------
Задание11.5
Задания
314
ТеперьвБДостаетсяистараяинформация.И,есликакой-тоMAC-адреснепоявлялсявновыхзаписях,записьсним,можетоставатьсявБДоченьдолго.
И,хотяэтоможетбытьполезно,чтобыпосмотреть,гдеMAC-адреснаходилсявпоследнийраз,постояннохранитьэтуинформациюнеоченьполезно.
Например,еслизаписьвБДужебольшемесяца,тоеёможноудалить.
Длятого,чтобысделатьтакойкритерий,нужноввестиновоеполе,вкотороебудетзаписыватьсяпоследнеевремядобавлениязаписи.
Новоеполеназываетсяlast_activeивнемдолжнанаходитьсястрока,вформате:YYYY-MM-DDHH:MM:SS.
Вэтомзаданиинеобходимо:
изменить,соответственно,таблицуdhcpидобавитьновоеполе.таблицуможнопоменятьизclisqlite,нофайлdhcp_snooping_schema.sqlтоженеобходимоизменить
изменитьскриптadd_data.py,чтобыондобавлялккаждойзаписивремя
Какполучитьстрокусовременемидатой,вуказанномформате,показановзадании.Раскомментируйтестрокуипосмотритекаконавыглядит.
importdatetime
now=str(datetime.datetime.today().replace(microsecond=0))
#printnow
Задание11.5a
Послевыполнениязадания11.5,втаблицеdhcpестьновоеполеlast_active.
Обновитескриптadd_data.py,такимобразом,чтобыонудалялвсезаписи,которыебылиактивнымиболее7днейназад.
Длятого,чтобыполучитьтакиезаписи,можнопростовручнуюобновитьполеlast_active.
Вфайлезаданияописанпримерработысобъектамимодуляdatetime.Обратитевнимание,чтообъекты,какистрокисдатой,которыепишутсявБД,можносравниватьмеждусобой.
Задания
315
fromdatetimeimporttimedelta,datetime
now=datetime.today().replace(microsecond=0)
week_ago=now-timedelta(days=7)
#printnow
#printweek_ago
#printnow>week_ago
#printstr(now)>str(week_ago)
Задание11.6
Вэтомзаданиивыложенфайлparse_dhcp_snooping.py.
Вфайлесозданынесколькофункцийиописаныаргументыкоманднойстроки,которыепринимаетфайл.
Вфайлеparse_dhcp_snooping.pyнельзяничегоменять.
Естьподдержкааргументовдлявыполнениявсехдействий,которые,впредыдущихзаданиях,выполнялисьвфайлахcreate_db.py,add_data.pyиget_data.py.
Вфайлеparse_dhcp_snooping.pyестьтакаястрока:
importparse_dhcp_snooping_functionsaspds
Изадачаэтогозаданиявтом,чтобысоздатьвсенеобходимыефункции,вфайлеparse_dhcp_snooping_functions.pyнаосновеинформациивфайлеparse_dhcp_snooping.py.
Изфайлаparse_dhcp_snooping.py,необходимоопределить:
какиефункциидолжныбытьвфайлеparse_dhcp_snooping_functions.pyкакиепараметрысоздатьвэтихфункциях
Необходимосоздатьсоответствующиефункциииперенестивнихфункционал,которыйописанвпредыдущихзаданиях.
Всянеобходимаяинформация,присутствуетвфункцияхcreate,add,get,вфайлеparse_dhcp_snooping.py.
Впринципе,длявыполнениязадания,необязательноразбиратьсясмодулемargparse.Но,выможетепочитатьонемвразделеДополнительнаяинформация.
Задания
316
Длятого,чтобыбылопрощеначать,попробуйтесоздатьнеобходимыефункциивфайлеparse_dhcp_snooping_functions.pyи,например,простовыведитеаргументыфункций,используяprint.
Потом,можносоздатьфункции,которыезапрашиваютинформациюизБД(базуданныхможноскопироватьизпредыдущихзаданий).
Можносоздаватьлюбыевспомогательныефункциивфайлеparse_dhcp_snooping_functions.py,анетолькоте,которыевызываютсяизфайлаparse_dhcp_snooping.py.
Проверьтевсеоперации:
созданиеБДдобавлениеинформацииокоммутаторахдобавлениеинформациинаоснованиивыводаshipdhcpsnoopingbindingизфайловвыборкуинформацииизБД(попараметруивсюинформацию)
Чтобыбылопрощепонять,какбудетвыглядетьвызовскрипта,ниженесколькопримеров.
$pythonparse_dhcp_snooping.py-h
usage:parse_dhcp_snooping.py[-h]{create_db,add,get}...
optionalarguments:
-h,--helpshowthishelpmessageandexit
subcommands:
validsubcommands
{create_db,add,get}additionalinfo
create_dbcreatenewdb
addadddatatodb
getgetdatafromdb
$pythonparse_dhcp_snooping.pyget-h
usage:parse_dhcp_snooping.pyget[-h][--dbDB_FILE]
[-k{mac,ip,vlan,interface,switch}]
[-vVALUE][-a]
optionalarguments:
-h,--helpshowthishelpmessageandexit
--dbDB_FILEdbname
-k{mac,ip,vlan,interface,switch}
hostkey(parameter)tosearch
-vVALUEvalueofkey
-ashowdbcontent
Задания
317
$pythonparse_dhcp_snooping.pyadd-h
usage:parse_dhcp_snooping.pyadd[-h][--dbDB_FILE][-s]
filename[filename...]
positionalarguments:
filenamefile(s)toaddtodb
optionalarguments:
-h,--helpshowthishelpmessageandexit
--dbDB_FILEdbname
-saddswitchdataifset,elseaddnormaldata
$pythonparse_dhcp_snooping.pycreate_db-h
usage:parse_dhcp_snooping.pycreate_db[-h][-nNAME][-sSCHEMA]
optionalarguments:
-h,--helpshowthishelpmessageandexit
-nNAMEdbfilename
-sSCHEMAdbschemafilename
$pythonparse_dhcp_snooping.pyget
Showingdhcp_snooping.dbcontent...
----------------------------------------------------------------------
00:09:BB:3D:D6:5810.1.10.210FastEthernet0/1sw1
00:04:A3:3E:5B:6910.1.5.25FastEthernet0/10sw1
00:05:B3:7E:9B:6010.1.5.45FastEthernet0/9sw1
00:07:BC:3F:A6:5010.1.10.610FastEthernet0/3sw1
00:09:BC:3F:A6:50192.168.1.100100FastEthernet0/5sw1
00:A9:BB:3D:D6:5810.1.10.2010FastEthernet0/7sw2
00:B4:A3:3E:5B:6910.1.5.205FastEthernet0/5sw2
00:C5:B3:7E:9B:6010.1.5.405FastEthernet0/9sw2
00:A9:BC:3F:A6:50100.1.1.63FastEthernet0/20sw3
$pythonparse_dhcp_snooping.pyget-kvlan-v10
GetingdatafromDB:dhcp_snooping.db
Requestdataforhost(s)withvlan10
Detailedinformationforhost(s)withvlan10
----------------------------------------
mac:00:09:BB:3D:D6:58
ip:10.1.10.2
interface:FastEthernet0/1
switch:sw1
----------------------------------------
mac:00:07:BC:3F:A6:50
ip:10.1.10.6
interface:FastEthernet0/3
switch:sw1
----------------------------------------
mac:00:A9:BB:3D:D6:58
ip:10.1.10.20
interface:FastEthernet0/7
switch:sw2
Задания
318
----------------------------------------
$pythonparse_dhcp_snooping.pyaddsw1_dhcp_snooping.txtsw2_dhcp_snooping.txtsw3_dh
cp_snooping.txt
Readinginfofromfile(s)
sw1_dhcp_snooping.txt,sw2_dhcp_snooping.txt,sw3_dhcp_snooping.txt
Задания
319
ПодключениекоборудованиюВэтомразделерассматриваетсякакподключитьсякоборудованиюпопротоколам:
SSHTelnet
ВPythonестьнесколькомодулей,которыепозволяютподключатьсякоборудованиюивыполнятькоманды:
pexpect-этореализацияexpectнаPythonэтотмодульпозволяетработатьслюбойинтерактивнойсессией:ssh,telnet,sftpидр.крометого,онпозволяетвыполнятьразличныекомандывОС(этоможноделатьиспомощьюдругихмодулей)несмотрянато,чтоpexpectможетбытьменееудобнымвиспользовании,чемдругиемодули,онреализуетболееобщийфункционалиэтопозволяетиспользоватьеговситуациях,когдадругиемодулинеработают
telnetlib-этотмодульпозволяетподключатьсяпоTelnetвверсии1.0netmikoтакжепоявиласьподдержкаTelnet,поэтому,еслиnetmikoподдерживаеттооборудование,котороеиспользуетсяувас,удобнейбудетиспользоватьего
paramiko-этомодуль,которыйпозволяетподключатьсяпоSSHv2онболееудобенвиспользовании,чемpexpect,носболееузкойфункциональностью(поддерживаеттолькоSSH)
netmiko-этомодуль,которыйупрощаетиспользованиеparamikoдлясетевыхустройств
netmikoэто"обертка"вокругparamiko,котораяориентировананаработуссетевымоборудованием
Вэтомразделерассматриваютсявсе4модуля,атакжекакподключатьсякнесколькимустройствампараллельно.
Дляработыссетевымоборудованием,удобнееиспользоватьnetmiko.
Впримерахразделаиспользуютсятримаршрутизатора.Книмнетникакихтребований,тольконастроенныйSSH.
Параметры,которыеиспользуютсявразделе:
пользователь:ciscoпароль:cisco
12.Подключениекоборудованию
320
парольнарежимenable:ciscoSSHверсии2IP-адреса:192.168.100.1,192.168.100.2,192.168.100.3
12.Подключениекоборудованию
321
ВводпароляПриподключениикоборудованиювручную,какправило,парольтакжевводитсявручную.
Приавтоматизизацииподключения,надорешитькакимобразомбудетпередаватьсяпароль:
запрашиватьпарольпристартескриптаисчитыватьвводпользователяминусвтом,чтобудетвиднокакиесимволывводитпользователь
записыватьлогинипарольвкаком-тофайлеэтонеоченьбезопасно
Какправило,одинитотжепользовательиспользуетодинаковыйлогинипарольдляподключениякоборудованию.
И,какправило,будетдостаточнозапроситьлогинипарольпристартескрипта,азатемиспользоватьихдляподключениянаразныеустройства.
Ксожалению,еслииспользоватьraw_input(),набираемыйпарольбудетвиден.Ахотелосьбы,чтобыпривводепаролявводимыесимволынеотображались.
Модульgetpass
Модульgetpassпозволяетзапрашиватьпароль,неотображаявводимыесимволы:
In[1]:importgetpass
In[2]:password=getpass.getpass()
Password:
In[3]:printpassword
testpass
Переменныеокружения
Ещеодинвариантхраненияпароля(аможноипользователя)-переменныеокружения.
Например,такимобразомлогинипарользаписываютсявпеременные:
Вводпароля
322
$exportSSH_USER=user
$exportSSH_PASSWORD=userpass
Азатем,вPython,считываютсязначениявпеременныевскрипте:
importos
USERNAME=os.environ.get('SSH_USER')
PASSWORD=os.environ.get('SSH_PASSWORD')
Вводпароля
323
МодульpexpectМодульpexpectпозволяетавтоматизироватьинтерактивныеподключения,такиекак:
telnetsshftp
Дляначала,модульpexpectнужноустановить:
pipinstallpexpect
PexpectэтореализацияexpectнаPython.
Логикаработыpexpectтакая:
запускаетсякакая-топрограммаpexpectожидаетопределенныйвывод(приглашение,запроспароляиподобное)получиввывод,онотправляеткоманды/данныепоследниедвадействияповторяютсястолько,скольконужно
Приэтом,самpexpectнереализуетразличныеутилиты,аиспользуетужеготовые.
Вpexpectестьдваосновныхинструмента:
функцияrun()классspawn
pexpect.run()
Функцияrun()позволяетлегковызватькакую-топрограммуивернутьеёвывод.
Например:
Pexpect
324
In[1]:importpexpect
In[2]:output=pexpect.run('ls-ls')
In[3]:printoutput
total368
8-rw-r--r--1natashastaff372Sep2608:36LICENSE.md
8-rw-r--r--1natashastaff3483Sep2608:36README.md
16-rw-r--r--1natashastaff7098Oct511:41SUMMARY.md
8-rw-r--r--1natashastaff70Sep2519:01about.md
0drwxr-xr-x17natashastaff578Sep2519:05book
8-rw-r--r--1natashastaff239Sep2706:32book.json
288-rw-r--r--1natashastaff146490Sep2709:11cover.jpg
0drwxr-xr-x6natashastaff204Sep2519:01exercises
24-rw-r--r--1natashastaff10024Sep2519:01faq.md
0drwxr-xr-x3natashastaff102Sep2716:25resources
8-rw-r--r--1natashastaff3633Sep2715:52schedule.md
pexpect.spawn
Классspawnподдерживаетбольшевозможностей.Онпозволяетвзаимодействоватьсвызваннойпрограммой,отправляяданныеиожидаяответ.
Простойпримериспользованияpexpect.spawn:
t=pexpect.spawn('[email protected]')
t.expect('Password:')
t.sendline("userpass")
t.expect('>')
СначалавыполняетсяподключениепоSSH,затемpexpectожидаетстрокуPassword:.Кактолькоэтастрокапоявилась,отправляетсяпароль.Послеотправки,pexpectожидаетстроку>.
Примериспользованияpexpect
Примериспользованияpexpectдляподключениякоборудованиюипередачикомандыshow(файл1_pexpect.py):
Pexpect
325
importpexpect
importgetpass
importsys
COMMAND=sys.argv[1]
USER=raw_input("Username:")
PASSWORD=getpass.getpass()
ENABLE_PASS=getpass.getpass(prompt='Enterenablepassword:')
DEVICES_IP=['192.168.100.1','192.168.100.2','192.168.100.3']
forIPinDEVICES_IP:
print"Connectiontodevice%s"%IP
t=pexpect.spawn('ssh%s@%s'%(USER,IP))
t.expect('Password:')
t.sendline(PASSWORD)
t.expect('>')
t.sendline('enable')
t.expect('Password:')
t.sendline(ENABLE_PASS)
t.expect('#')
t.sendline("terminallength0")
t.expect('#')
t.sendline(COMMAND)
t.expect('#')
printt.before
Комментариисскрипту:
команда,которуюнужновыполнить,передаетсякакаргументзатемзапрашиваетсялогин,парольипарольнарежимenable
паролизапрашиваютсяспомощьюмодуляgetpassip_listэтосписокIP-адресовустройств,ккоторымбудетвыполнятьсяподключениевцикле,выполняетсяподключениекустройствамизспискавклассеspawnвыполняетсяподключениепоSSHктекущемуадресу,используяуказанноеимяпользователяпослеэтого,начинаютчередоватьсяпарыметодов:expectиsendline
expect-ожиданиеподстрокиsendline-когдастрокапоявилась,отправляетсякоманда
такпроисходитдоконцацикла,итолькопоследняякомандаотличается:
Pexpect
326
beforeпозволяетсчитатьвсё,чтопоймалpexpectдопредыдущейподстрокивexpect
Выполнениескриптавыглядиттак:
$python1_pexpect.py"shipintbr"
Username:nata
Password:
Enterenablesecret:
Connectiontodevice192.168.100.1
shipintbr
InterfaceIP-AddressOK?MethodStatusProtocol
FastEthernet0/0192.168.100.1YESNVRAMupup
FastEthernet0/1unassignedYESNVRAMupup
FastEthernet0/1.1010.1.10.1YESmanualupup
FastEthernet0/1.2010.1.20.1YESmanualupup
FastEthernet0/1.3010.1.30.1YESmanualupup
FastEthernet0/1.4010.1.40.1YESmanualupup
FastEthernet0/1.5010.1.50.1YESmanualupup
FastEthernet0/1.6010.1.60.1YESmanualupup
FastEthernet0/1.7010.1.70.1YESmanualupup
R1
Connectiontodevice192.168.100.2
shipintbr
InterfaceIP-AddressOK?MethodStatusProtocol
FastEthernet0/0192.168.100.2YESNVRAMupup
FastEthernet0/1unassignedYESNVRAMupup
FastEthernet0/1.1010.2.10.1YESmanualupup
FastEthernet0/1.2010.2.20.1YESmanualupup
FastEthernet0/1.3010.2.30.1YESmanualupup
FastEthernet0/1.4010.2.40.1YESmanualupup
FastEthernet0/1.5010.2.50.1YESmanualupup
FastEthernet0/1.6010.2.60.1YESmanualupup
FastEthernet0/1.7010.2.70.1YESmanualupup
R2
Connectiontodevice192.168.100.3
shipintbr
InterfaceIP-AddressOK?MethodStatusProtocol
FastEthernet0/0192.168.100.3YESNVRAMupup
FastEthernet0/1unassignedYESNVRAMupup
FastEthernet0/1.1010.3.10.1YESmanualupup
FastEthernet0/1.2010.3.20.1YESmanualupup
FastEthernet0/1.3010.3.30.1YESmanualupup
FastEthernet0/1.4010.3.40.1YESmanualupup
FastEthernet0/1.5010.3.50.1YESmanualupup
FastEthernet0/1.6010.3.60.1YESmanualupup
FastEthernet0/1.7010.3.70.1YESmanualupup
R3
Обратитевнимание,что,таккаквпоследнемexpectуказано,чтонадоожидатьподстроку#,методbeforeпоказаликомандуиимяхоста.
Pexpect
327
Специальныесимволывshell
Pexpectнеинтерпретируетспециальныесимволыshell,такиекак>,|,*.
Длятогочтобы,например,командаls-ls|grepSUMMARYотработала,нужнозапуститьshellтакимобразом:
In[1]:importpexpect
In[2]:p=pexpect.spawn('/bin/bash-c"ls-ls|grepSUMMARY"')
In[3]:p.expect(pexpect.EOF)
Out[3]:0
In[4]:printp.before
16-rw-r--r--1natashastaff7156Oct513:05SUMMARY.md
pexpect.EOF
Впредыдущемпримеревстретилосьиспользованиеpexpect.EOF.
EOF(endoffile)—конецфайла
Этоспециальноезначение,котороепозволяетотреагироватьназавершенниеисполнениякомандыилисессии,котораябылазапущенавspawn.
Привызовекомандыls-ls,pexpectнеполучаетинтерактивныйсеанс.Командавыполняетсяивсё,наэтомзавершаетсяеёработа.
Поэтому,еслизапуститьеёиуказатьвexpectприглашение,возникнетошибка:
In[5]:p=pexpect.spawn('/bin/bash-c"ls-ls|grepSUMMARY"')
In[6]:p.expect('nattaur')
---------------------------------------------------------------------------
EOFTraceback(mostrecentcalllast)
<ipython-input-9-9c71777698c2>in<module>()
---->1p.expect('nattaur')
/Library/Python/2.7/site-packages/pexpect/spawnbase.pycinexpect(self,pattern,timeo
ut,searchwindowsize,async)
313compiled_pattern_list=self.compile_pattern_list(pattern)
314returnself.expect_list(compiled_pattern_list,
-->315timeout,searchwindowsize,async)
316
317defexpect_list(self,pattern_list,timeout=-1,searchwindowsize=-1,
/Library/Python/2.7/site-packages/pexpect/spawnbase.pycinexpect_list(self,pattern_l
ist,timeout,searchwindowsize,async)
Pexpect
328
337returnexpect_async(exp,timeout)
338else:
-->339returnexp.expect_loop(timeout)
340
341defexpect_exact(self,pattern_list,timeout=-1,searchwindowsize=-1,
/Library/Python/2.7/site-packages/pexpect/expect.pycinexpect_loop(self,timeout)
100timeout=end_time-time.time()
101exceptEOFase:
-->102returnself.eof(e)
103exceptTIMEOUTase:
104returnself.timeout(e)
/Library/Python/2.7/site-packages/pexpect/expect.pycineof(self,err)
47iferrisnotNone:
48msg=str(err)+'\n'+msg
--->49raiseEOF(msg)
50
51deftimeout(self,err=None):
EOF:EndOfFile(EOF).Emptystringstyleplatform.
<pexpect.pty_spawn.spawnobjectat0x107100b10>
command:/bin/bash
args:['/bin/bash','-c','ls-ls|grepSUMMARY']
searcher:None
buffer(last100chars):''
before(last100chars):'16-rw-r--r--1natashastaff7156Oct513:05SUMMA
RY.md\r\n'
after:<class'pexpect.exceptions.EOF'>
match:None
match_index:None
exitstatus:0
flag_eof:True
pid:85765
child_fd:7
closed:False
timeout:30
delimiter:<class'pexpect.exceptions.EOF'>
logfile:None
logfile_read:None
logfile_send:None
maxread:2000
ignorecase:False
searchwindowsize:None
delaybeforesend:0.05
delayafterclose:0.1
delayafterterminate:0.1
Но,еслипередатьвexpectEOF,ошибкинебудет.
Возможностиpexpect.expect
Pexpect
329
pexpect.expectвкачествешаблонаможетприниматьнетолькостроку.
Чтоможетиспользоватьсякакшаблонвpexpect.expect:
строкаEOF-этотшаблонпозволяетсреагироватьнаисключениеEOFTIMEOUT-исключениеtimeout(поумолчаниюзначениеtimeout=30секунд)compiledre
Ещеоднаоченьполезнаявозможностьpexpect.expect:можнопередаватьнеоднозначение,асписок.
Например:
In[7]:p=pexpect.spawn('/bin/bash-c"ls-ls|grepSUMMARY"')
In[8]:p.expect(['nattaur',pexpect.TIMEOUT,pexpect.EOF])
Out[8]:2
Тутнескольковажныхмоментов:
когдаpexpect.expectвызываетсясосписком,можноуказыватьразныеожидаемыестрокикроместрок,можноуказыватьисключенияpexpect.expectвозвращаетномерэлементасписка,которыйсработал
вданномслучае,номер2,таккакисключениеEOFнаходитсявспискеподномеромдва
засчеттакогоформата,можноделатьответвлениявпрограмме,взависимостиоттогоскакимэлементомбылосовпадение
Документацияpexpect
Документациямодуля:pexpect.
Pexpect
330
МодульtelnetlibМодульtelnetlibвходитвстандартнуюбиблиотекуPython.Этореализацияклиентаtelnet.
Подключитьсяпоtelnetможноииспользуяpexpect.Плюсtelnetlibвтом,чтоэтотмодульвходитвстандартнуюбиблиотекуPython.
Принципработыtelnetlibнапоминаетpexpect,поэтомупримернижедолженбытьпонятен.
Файл2_telnetlib.py:
importtelnetlib
importtime
importgetpass
importsys
COMMAND=sys.argv[1]
USER=raw_input("Username:")
PASSWORD=getpass.getpass()
ENABLE_PASS=getpass.getpass(prompt='Enterenablepassword:')
DEVICES_IP=['192.168.100.1','192.168.100.2','192.168.100.3']
forIPinDEVICES_IP:
print"Connectiontodevice%s"%IP
t=telnetlib.Telnet(IP)
t.read_until("Username:")
t.write(USER+'\n')
t.read_until("Password:")
t.write(PASSWORD+'\n')
t.write("enable\n")
t.read_until("Password:")
t.write(ENABLE_PASS+'\n')
t.write("terminallength0\n")
t.write(COMMAND+'\n')
time.sleep(5)
output=t.read_very_eager()
printoutput
Telnetlib
331
Перваяособенность,котораябросаетсявглаза-вконцеотправляемыхкоманд,надодобавлятьсимволпереводастроки.
Востальном,telnetlibоченьпохожнаpexpect:
t=telnetlib.Telnet(ip)-классTelnetпредставляетсоединениексерверу.вданномслучае,емупередаетсятолькоIP-адрес,номожнопередатьипорт,ккоторомунужноподключаться
read_until-похожнаметодexpectвмодулеpexpect.Указываетдокакойстрокиследуетсчитыватьвыводwrite-передатьстрокуread_very_eager-считатьвсё,чтополучается
Выполнениескрипт:
Telnetlib
332
$python2_telnetlib.py"shipintbr"
Username:nata
Password:
Enterenablesecret:
Connectiontodevice192.168.100.1
R1#terminallength0
R1#shipintbr
InterfaceIP-AddressOK?MethodStatusProtocol
FastEthernet0/0192.168.100.1YESNVRAMupup
FastEthernet0/1unassignedYESNVRAMupup
FastEthernet0/1.1010.1.10.1YESmanualupup
FastEthernet0/1.2010.1.20.1YESmanualupup
FastEthernet0/1.3010.1.30.1YESmanualupup
FastEthernet0/1.4010.1.40.1YESmanualupup
FastEthernet0/1.5010.1.50.1YESmanualupup
FastEthernet0/1.6010.1.60.1YESmanualupup
FastEthernet0/1.7010.1.70.1YESmanualupup
R1#
Connectiontodevice192.168.100.2
R2#terminallength0
R2#shipintbr
InterfaceIP-AddressOK?MethodStatusProtocol
FastEthernet0/0192.168.100.2YESNVRAMupup
FastEthernet0/1unassignedYESNVRAMupup
FastEthernet0/1.1010.2.10.1YESmanualupup
FastEthernet0/1.2010.2.20.1YESmanualupup
FastEthernet0/1.3010.2.30.1YESmanualupup
FastEthernet0/1.4010.2.40.1YESmanualupup
FastEthernet0/1.5010.2.50.1YESmanualupup
FastEthernet0/1.6010.2.60.1YESmanualupup
FastEthernet0/1.7010.2.70.1YESmanualupup
R2#
Connectiontodevice192.168.100.3
R3#terminallength0
R3#shipintbr
InterfaceIP-AddressOK?MethodStatusProtocol
FastEthernet0/0192.168.100.3YESNVRAMupup
FastEthernet0/1unassignedYESNVRAMupup
FastEthernet0/1.1010.3.10.1YESmanualupup
FastEthernet0/1.2010.3.20.1YESmanualupup
FastEthernet0/1.3010.3.30.1YESmanualupup
FastEthernet0/1.4010.3.40.1YESmanualupup
FastEthernet0/1.5010.3.50.1YESmanualupup
FastEthernet0/1.6010.3.60.1YESmanualupup
FastEthernet0/1.7010.3.70.1YESmanualupup
R3#
Telnetlib
333
Мынебудемподробнееостанавливатьсянаtelnetlib.Остальныеметоды,которыеподдерживаетмодуль,можнопосмотретьвдокументации.
Документациямодуляtelnetlib:telnetlib
Telnetlib
334
МодульparamikoParamikoэтореализацияпротоколаSSHv2наPython.Paramikoпредоставляетфункциональностьклиентаисервера.Мыбудемрассматриватьтолькофункциональностьклиента.
ТаккакParamikoневходитвстандартнуюбиблиотекумодулейPython,егонужноустановить:
pipinstallparamiko
ПримериспользованияParamiko(файл3_paramiko.py):
importparamiko
importgetpass
importsys
importtime
COMMAND=sys.argv[1]
USER=raw_input("Username:")
PASSWORD=getpass.getpass()
ENABLE_PASS=getpass.getpass(prompt='Enterenablepassword:')
DEVICES_IP=['192.168.100.1','192.168.100.2','192.168.100.3']
forIPinDEVICES_IP:
print"Connectiontodevice%s"%IP
client=paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname=IP,username=USER,password=PASSWORD,look_for_keys=False,
allow_agent=False)
ssh=client.invoke_shell()
ssh.send("enable\n")
ssh.send(ENABLE_PASS+'\n')
time.sleep(1)
ssh.send("terminallength0\n")
time.sleep(1)
printssh.recv(1000)
ssh.send(COMMAND+"\n")
time.sleep(2)
result=ssh.recv(5000)
printresult
Paramiko
335
Комментариикскрипту:
client=paramiko.SSHClient()
этоткласспредставляетсоединениекSSH-серверу.Онвыполняетаутентификациюклиента.
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
set_missing_host_key_policy-устанавливаеткакуюполитикуиспользовать,когдавыполнятсяподключениексерверу,ключкоторогонеизвестен.paramiko.AutoAddPolicy()-политика,котораяавтоматическидобавляетновоеимяхостаиключвлокальныйобъектHostKeys.
client.connect(IP,username=USER,password=PASSWORD,look_for_keys=False,
allow_agent=False)
client.connect-метод,которыйвыполняетподключениекSSH-серверуиаутентифицируетподключение
hostname-имяхостаилиIP-адресusername-имяпользователяpassword-парольlook_for_keys-поумолчаниюparamikoвыполняетаутентификациюпоключам.Чтобыотключитьэто,надопоставитьпоставивFalseallow_agent-paramikoможетподключатьсяклокальномуSSHагентуОС.Этонужноприработесключами,атаккак,вданномслучае,аутентификациявыполняетсяпологину/паролю,этонужноотключить.
ssh=client.invoke_shell()
послевыполненияпредыдущейкомандыужеестьподключениексерверу.Методinvoke_shellпозволяетустановитьинтерактивнуюсессиюSSHссервером.
Внутриустановленнойсессиивыполняютсякомандыиполучаютсяданные:ssh.send-отправляетуказаннуюстрокувсессиюssh.recv-получаетданныеизсессии.Вскобкахуказываетсямаксимальноезначениевбайтах,котороеможнополучить.Этотметодвозвращаетсчитаннуюстроку
Кромеэтого,междуотправкойкомандыисчитыванием,кое-гдестоитстрокаtime.sleep
спомощьюнеёуказываетсяпауза-скольковремениподождать,преждечемскриптпродолжитвыполняться.Этоделаетсядлятого,чтобыдождатьсявыполнениякомандынаоборудовании
Таквыглядитрезультатвыполненияскрипта:
$python3_paramiko.py"shipintbr"
Username:cisco
Password:
Paramiko
336
Enterenablesecret:
Connectiontodevice192.168.100.1
R1>enable
Password:
R1#terminallength0
R1#
shipintbr
InterfaceIP-AddressOK?MethodStatusProtocol
FastEthernet0/0192.168.100.1YESNVRAMupup
FastEthernet0/1unassignedYESNVRAMupup
FastEthernet0/1.1010.1.10.1YESmanualupup
FastEthernet0/1.2010.1.20.1YESmanualupup
FastEthernet0/1.3010.1.30.1YESmanualupup
FastEthernet0/1.4010.1.40.1YESmanualupup
FastEthernet0/1.5010.1.50.1YESmanualupup
FastEthernet0/1.6010.1.60.1YESmanualupup
FastEthernet0/1.7010.1.70.1YESmanualupup
R1#
Connectiontodevice192.168.100.2
R2>enable
Password:
R2#terminallength0
R2#
shipintbr
FastEthernet0/0192.168.100.2YESNVRAMupup
FastEthernet0/1unassignedYESNVRAMupup
FastEthernet0/1.1010.2.10.1YESmanualupup
FastEthernet0/1.2010.2.20.1YESmanualupup
FastEthernet0/1.3010.2.30.1YESmanualupup
FastEthernet0/1.4010.2.40.1YESmanualupup
FastEthernet0/1.5010.2.50.1YESmanualupup
FastEthernet0/1.6010.2.60.1YESmanualupup
FastEthernet0/1.7010.2.70.1YESmanualupup
R2#
Connectiontodevice192.168.100.3
R3>enable
Password:
R3#terminallength0
R3#
shipintbr
InterfaceIP-AddressOK?MethodStatusProtocol
FastEthernet0/0192.168.100.3YESNVRAMupup
FastEthernet0/1unassignedYESNVRAMupup
FastEthernet0/1.1010.3.10.1YESmanualupup
FastEthernet0/1.2010.3.20.1YESmanualupup
FastEthernet0/1.3010.3.30.1YESmanualupup
FastEthernet0/1.4010.3.40.1YESmanualupup
FastEthernet0/1.5010.3.50.1YESmanualupup
Paramiko
337
FastEthernet0/1.6010.3.60.1YESmanualupup
FastEthernet0/1.7010.3.70.1YESmanualupup
R3#
Обратитевнимание,чтоввыводпопалипроцессвводапароляenableикомандаterminallength.
Этосвязаностем,чтоparamikoсобираетвесьвыводвбуфер.И,привызовеметодаrecv(например,ssh.recv(1000)),paramikoвозвращаетвсё,чтоестьвбуфере.Послевыполненияrecv,буферпуст.
Поэтому,еслинужнополучитьтольковыводкомандыshipintbr,тонадооставитьrecv,нонеделатьprint:
ssh.send("enable\n")
ssh.send(ENABLE_PASS+'\n')
time.sleep(1)
ssh.send("terminallength0\n")
time.sleep(1)
#Тутмывызываемrecv,ноневыводимсодержимоебуфера
ssh.recv(1000)
ssh.send(COMMAND+"\n")
time.sleep(3)
result=ssh.recv(5000)
printresult
ДокументацияParamiko
Всё,чтокасаетсяклиента:ParamikoClientВсё,чтокасаетсясоединения(внашемпримере,всё,чтоотноситсякпеременнойssh):ParamikoChannel
Paramiko
338
МодульnetmikoNetmikoэтомодуль,которыйпозволяетупроститьиспользованиеparamikoдлясетевыхустройств.
Грубоговоря,netmikoэтотакая"обертка"дляparamiko.
Сначалаnetmikoнужноустановить:
pipinstallnetmiko
Примериспользованияnetmiko(файл4_netmiko.py):
fromnetmikoimportConnectHandler
importgetpass
importsys
COMMAND=sys.argv[1]
USER=raw_input("Username:")
PASSWORD=getpass.getpass()
ENABLE_PASS=getpass.getpass(prompt='Enterenablepassword:')
DEVICES_IP=['192.168.100.1','192.168.100.2','192.168.100.3']
forIPinDEVICES_IP:
print"Connectiontodevice%s"%IP
DEVICE_PARAMS={'device_type':'cisco_ios',
'ip':IP,
'username':USER,
'password':PASSWORD,
'secret':ENABLE_PASS}
ssh=ConnectHandler(**DEVICE_PARAMS)
ssh.enable()
result=ssh.send_command(COMMAND)
printresult
Посмотритенасколькопрощевыглядитэтотпримерсnetmiko.
Разберемсяссодержимымскрипта:
DEVICE_PARAMS-этословарь,вкоторомуказываютсяпараметрыустройстваdevice_type-этопредопределенныезначения,которыепонимаетnetmiko
вданномслучае,таккакподключениевыполняетсякустройствусCisco
Netmiko
339
IOS,используетсязначение'cisco_ios'ssh=ConnectHandler(**DEVICE_PARAMS)-устанавливаетсясоединениесустройством,наосновепараметров,которыенаходятсявсловаре
двезвездочкипередсловарем,этораспаковкасловаря(подробнеевразделеРаспаковкааргументов)
ssh.enable()-переходврежимenableпарольпередаетсяавтоматическииспользуетсязначениеключаsecret,которыйуказанвсловареDEVICE_PARAMS
result=ssh.send_command(COMMAND)-отправкакомандыиполучениевывода
Вэтомпримеренепередаетсякомандаterminallength,таккакnetmikoпоумолчанию,выполняетэтукоманду.
Таквыглядитрезультатвыполненияскрипта:
Netmiko
340
$python4_netmiko.py"shipintbr"
Username:cisco
Password:
Enterenablepassword:
Connectiontodevice192.168.100.1
InterfaceIP-AddressOK?MethodStatusProtocol
FastEthernet0/0192.168.100.1YESNVRAMupup
FastEthernet0/1unassignedYESNVRAMupup
FastEthernet0/1.1010.1.10.1YESmanualupup
FastEthernet0/1.2010.1.20.1YESmanualupup
FastEthernet0/1.3010.1.30.1YESmanualupup
FastEthernet0/1.4010.1.40.1YESmanualupup
FastEthernet0/1.5010.1.50.1YESmanualupup
FastEthernet0/1.6010.1.60.1YESmanualupup
FastEthernet0/1.7010.1.70.1YESmanualupup
Connectiontodevice192.168.100.2
InterfaceIP-AddressOK?MethodStatusProtocol
FastEthernet0/0192.168.100.2YESNVRAMupup
FastEthernet0/1unassignedYESNVRAMupup
FastEthernet0/1.1010.2.10.1YESmanualupup
FastEthernet0/1.2010.2.20.1YESmanualupup
FastEthernet0/1.3010.2.30.1YESmanualupup
FastEthernet0/1.4010.2.40.1YESmanualupup
FastEthernet0/1.5010.2.50.1YESmanualupup
FastEthernet0/1.6010.2.60.1YESmanualupup
FastEthernet0/1.7010.2.70.1YESmanualupup
Connectiontodevice192.168.100.3
InterfaceIP-AddressOK?MethodStatusProtocol
FastEthernet0/0192.168.100.3YESNVRAMupup
FastEthernet0/1unassignedYESNVRAMupup
FastEthernet0/1.1010.3.10.1YESmanualupup
FastEthernet0/1.2010.3.20.1YESmanualupup
FastEthernet0/1.3010.3.30.1YESmanualupup
FastEthernet0/1.4010.3.40.1YESmanualupup
FastEthernet0/1.5010.3.50.1YESmanualupup
FastEthernet0/1.6010.3.60.1YESmanualupup
FastEthernet0/1.7010.3.70.1YESmanualupup
Ввыводенетникакихлишнихприглашений,тольковыводкомандыshipintbr.
Таккакnetmikoнаиболееудобныймодульдляподключенияксетевомуоборудования,разберемсяснимподробней.
Netmiko
341
ПоддерживаемыетипыустройствNetmikoподдерживаетнесколькотиповустройств:
AristavEOSCiscoASACiscoIOSCiscoIOS-XRCiscoSG300HPComware7HPProCurveJuniperJunosLinuxидругие
Актуальныйсписокможнопосмотретьврепозиториимодуля.
Словарь,определяющийпараметрыустройствВсловаремогутуказыватьсятакиепараметры:
cisco_router={'device_type':'cisco_ios',#предопределенныйтипустройства
'ip':'192.168.1.1',#адресустройства
'username':'user',#имяпользователя
'password':'userpass',#парольпользователя
'secret':'enablepass',#парольрежимаenable
'port':20022,#портSSH,поумолчанию22
}
ПодключениепоSSH
ssh=ConnectHandler(**cisco_router)
РежимenableПерейтиврежимenable:
Возможностиnetmiko
342
ssh.enable()
Выйтиизрежимаenable:
ssh.exit_enable_mode()
ОтправкакомандВnetmikoестьнесколькоспособовотправкикоманд:
send_command-отправитьоднукомандуsend_config_set-отправитьсписоккомандsend_config_from_file-отправитькомандыизфайла(используетвнутриметодsend_config_set)send_command_timing-отправитькомандуиподождатьвыводнаоснованиитаймера
send_command
Методsend_commandпозволяетотправитьоднукомандунаустройство.
Например:
result=ssh.send_command("showipintbr")
Методработаеттакимобразом:
отправляеткомандунаустройствоиполучаетвыводдострокисприглашениемилидоуказаннойстроки
приглашениеопределяетсяавтоматическиеслинавашемустройствеононеопределилось,можнопростоуказатьстроку,докоторойсчитыватьвыводранеетакработалметодsend_command_expect,носверсии1.0.0такработаетsend_command,аметодsend_command_expectоставлендлясовместимости
методвозвращаетвыводкомандыметодуможнопередаватьтакиепараметры:
command_string-командаexpect_string-докакойстрокисчитыватьвывод```delay_factor-параметрпозволяетувеличитьзадержкудоначалапоискастроки
Возможностиnetmiko
343
max_loops-количествоитераций,дотогокакметодвыдастошибку(исключение).Поумолчанию500strip_prompt-удалитьприглашениеизвывода.Поумолчаниюудаляетсяstrip_command-удалитьсамукомандуизвывода
Вбольшинствеслучаев,достаточнобудетуказатьтолькокоманду.
send_config_set
Методsend_config_setпозволяетотправитьнесколькокомандконфигурационногорежима.
Примериспользования:
commands=["routerospf1",
"network10.0.0.00.255.255.255area0",
"network192.168.100.00.0.0.255area1"]
result=ssh.send_config_set(commands)
Методработаеттакимобразом:
заходитвконфигурационныйрежим,затемпередаетвсекомандыивыходитизконфигурационногорежима
взависимостиоттипаустройства,выходаизконфигурационногорежимаможетинебыть.Например,дляIOS-XRвыходанебудет,таккаксначаланадозакомититьизменения
send_config_from_file
Методsend_config_from_fileотправляеткомандыизуказанногофайлавконфигурационныйрежим.
Примериспользования:
result=ssh.send_config_from_file("config_ospf.txt")
Методоткрываетфайл,считываеткомандыипередаетихметодуsend_config_set.
Дополнительныеметоды
Возможностиnetmiko
344
Кромеперечисленныхметодовдляотправкикоманд,netmikoподдерживаеттакиеметоды:
config_mode-перейтиврежимконфигурацииssh.config_mode()
exit_config_mode-выйтиизрежимаконфигурацииssh.exit_config_mode()
check_config_mode-проверитьнаходитсялиnetmikoврежимеконфигурации(возвращаетTrue,есливрежимеконфигурациииFalse-еслинет)
ssh.check_config_mode()
find_prompt-возвращаеттекущееприглашениеустройстваssh.find_prompt()
commit-выполнитьcommitнаIOS-XRиJuniperssh.commit()
disconnect-завершитьсоединениеSSH
ТутsshэтосозданноепредварительносоединениеSSH:ssh=ConnectHandler(**cisco_router)
TelnetСверсии1.0.0netmikoподдерживаетподключенияпоTelnet.Покачто,толькодляCiscoIOSустройств.
Внутри,netmikoиспользуетtelnetlib,дляподключенияпоTelnet.Но,приэтом,предоставляеттотжеинтерфейсдляработы,чтоиподключениепоSSH.
Длятого,чтобыподключитьсяпоTelnet,достаточновсловаре,которыйопределяетпараметрыподключения,указатьтипустройства'cisco_ios_telnet':
DEVICE_PARAMS={'device_type':'cisco_ios_telnet',
'ip':IP,
'username':USER,
'password':PASSWORD,
'secret':ENABLE_PASS}
Востальном,методы,которыеприменимыкSSH,применимыикTelnet.Пример,аналогичныйпримерусSSH(файл4_netmiko_telnet.py):
Возможностиnetmiko
345
fromnetmikoimportConnectHandler
importgetpass
importsys
importtime
COMMAND=sys.argv[1]
USER=raw_input("Username:")
PASSWORD=getpass.getpass()
ENABLE_PASS=getpass.getpass(prompt='Enterenablepassword:')
DEVICES_IP=['192.168.100.1','192.168.100.2','192.168.100.3']
forIPinDEVICES_IP:
print"Connectiontodevice%s"%IP
DEVICE_PARAMS={'device_type':'cisco_ios_telnet',
'ip':IP,
'username':USER,
'password':PASSWORD,
'secret':ENABLE_PASS,
'verbose':True}
ssh=ConnectHandler(**DEVICE_PARAMS)
ssh.enable()
result=ssh.send_command(COMMAND)
printresult
Аналогичноработаютиметоды:
send_command_timing()
find_prompt()
send_config_set()
send_config_from_file()
check_enable_mode()
disconnect()
Возможностиnetmiko
346
ПараллельныесессииКогданужноопроситьмногоустройств,выполнениеподключенийпоочередно,будетдостаточнодолгим.Конечно,этобудетбыстрее,чемподключениевручную.Но,хотелосьбыполучатьоткликкакможнобыстрее.
Всеэти"долго"и"быстрее"относительныепонятия.Но,вэтомразделемынаучимсяиконкретноизмерятьсколькоотрабатывалскрипт,чтобысравнитьнасколькобыстреебудетвыполнятьсяподключение.
Дляпараллельногоподключениякустройствам,вкурсеиспользуютсядвамодуля:
threadingmultiprocessing
ИзмерениевременивыполненияскриптаДляоценкивременивыполненияскриптаестьнескольковариантов.Вкурсеиспользуютсясамыепростыеварианты:
утилитуLinuxtimeимодульPythondatetime
Рассматриваютсяобаварианта,натотслучай,еслииспользуетсяWindows.
Приоценкевременивыполненияскрипта,вданномслучае,неважнавысокаяточность.Главное,сравнитьвремявыполненияскриптавразныхвариантах.
time
УтилитаtimeвLinuxпозволяетзамеритьвремявыполненияскрипта.Например:
$timepythonthread_paramiko.py
...
real0m4.712s
user0m0.336s
sys0m0.064s
Насинтересуетrealвремя.Вданномслучае,это4.7секунд.
Дляиспользованияутилитыtimeдостаточнонаписатьtimeпередстрокойзапускаскрипта.
Одновременноеподключениекнесколькимустройствам
347
datetime
Второйвариант-модульdatetime.ЭтотмодульпозволяетработатьсвременемидатамивPython.
Примериспользования:
fromdatetimeimportdatetime
importtime
start_time=datetime.now()
#Тутвыполняютсядействия
time.sleep(5)
printdatetime.now()-start_time
Результатвыполнения:
$pythontest.py
0:00:05.004949
ПроблемыспотокамивPython(CPython)
Подробности,которыеописаныниженеобязательнознать.Вобщемслучае,лучшеиспользоватьмодульmultiprocessingиможноплюс-минуснезадумыватьсяобовсехэтихособенностях.
Дляначала,намнужноразобратьсястерминами:
процесс(process)-это,грубоговоря,запущеннаяпрограмма.Процессувыделяютсяотдельныересурсы:память,процессорноевремяпоток(thread)-этоединицаисполнениявпроцессе.Потокиразделяютресурсыпроцесса,ккоторомуониотносятся.
Python(аточнее,CPython-реализациякотораяиспользуетсявкурсе)оптимизировандляработыводнопоточномрежиме.Этохорошо,есливпрограммеиспользуетсятолькоодинпоток.
И,втожевремя,уPythonестьопределенныенюансыработывмногопоточномрежиме.Связаныонистем,чтоCPythonиспользуетGIL(globalinterpreterlock).
Одновременноеподключениекнесколькимустройствам
348
GILнедаетнесколькимпотокамисполнятьодновременнокодPython.Еслиневдаватьсявподробности,тоGILможнопредставитькакнекийпереходящийфлаг,которыйразрешаетпотокамвыполняться.Укогофлаг,тотможетвыполнятьработу.
Флагпередаетсялибокаждыесколько-тоинструкцийPython,либо,например,когдавыполняютсякакие-тооперацииввода-вывода.
Поэтому,получается,чторазныепотокинебудутвыполнятьсяпараллельно,апрограммапростобудетмеждунимипереключаться,выполняяихвразноевремя.
Но,невсётакплохо.Есливпрограммеестьнекое"ожидание":пакетовизсети,запросапользователя,паузатипаsleep,товтакойпрограммепотокибудутвыполнятьсякак-будтопараллельно.Авсёпотому,что,вовремятакихпауз,флаг(GIL)можнопередатьдругомупотоку.
Но,туттакженужнобытьосторожным,таккактакойрезультатможетнаблюдатьсянанебольшомколичествесессий,номожетухудшитьсясростомколичествасессий.
Влюбомслучае,употоковвPythonестьсвоиобластиприменения.ВследующихразделахрассматриваетсякакиспользоватьпотокидляподключенияпоTelnet/SSH.Ипроверяется,какоесуммарноевремябудетзаниматьисполнениескрипта,посравнениюспоследовательнымисполнениемисиспользованиемпроцессов.
ПроцессыОбычно,чтобынепогружатьсявовсеэтитонкостисGILипотокамивPython,прощепростоиспользоватьмодульmultiprocessingивсё.
Еслижевыстолкнетесьсситуациейгдевамнужнобудетиспользоватьпотоки,скореевсего,ктомувременивыужеразберетесьсэтимвопросом.Какминимумдостаточновцеломзнатьобописанныхвышеособенностях.
ДополнительнаяинформацияЕсливыхотитеподробнееразобратьсясэтимивопросами,илизахотитевернутьсякнимпозже,несколькоссылок:
GIL:GIL(нарусском)InsidethePythonGIL
ОтличнаястатьяPythonthreadsandtheGILКороткооGIL,threads,processes:
Одновременноеподключениекнесколькимустройствам
349
http://stackoverflow.com/questions/3044580/multiprocessing-vs-threading-pythonhttp://stackoverflow.com/questions/18114285/python-what-are-the-differences-between-the-threading-and-multiprocessing-modul
Одновременноеподключениекнесколькимустройствам
350
МодульthreadingМодульthreadingможетбытьполезендлятакихзадач:
фоновоевыполнениекаких-тозадач:например,отправкапочтывовремяожиданияответаотпользователя
параллельноевыполнениезадачсвязанныхсвводом/выводоможиданиевводаотпользователячтение/записьфайлов
задачи,гдеприсутствуютпаузы:например,паузыспомощьюsleep
Однако,следуетучитывать,чтовситуациях,когдатребуетсяповышениепроизводительности,засчетиспользованиянесколькихпроцессоровилиядер,нужноиспользоватьмодульmultiprocessing,анемодульthreading.
Рассмотримпримериспользованиямодуляthreadingвместеспоследнимпримеромсnetmiko.
Таккакдляработысthreading,удобнееиспользоватьфункции,кодизменен:
кодподключенияпоSSHперенесенвфункциюпараметрыустройствперенесенывотдельныйфайлвформатеYAML
Файлnetmiko_function.py:
fromnetmikoimportConnectHandler
importsys
importyaml
COMMAND=sys.argv[1]
devices=yaml.load(open('devices.yaml'))
defconnect_ssh(device_dict,command):
print"Connectiontodevice%s"%device_dict['ip']
ssh=ConnectHandler(**device_dict)
ssh.enable()
result=ssh.send_command(command)
printresult
forrouterindevices['routers']:
connect_ssh(router,COMMAND)
Модульthreading
351
Файлdevices.yamlспараметрамиподключениякустройствам:
routers:
-device_type:cisco_ios
ip:192.168.100.1
username:cisco
password:cisco
secret:cisco
-device_type:cisco_ios
ip:192.168.100.2
username:cisco
password:cisco
secret:cisco
-device_type:cisco_ios
ip:192.168.100.3
username:cisco
password:cisco
secret:cisco
Времявыполненияскрипта(выводскриптаудален):
$timepythonnetmiko_function.py"shipintbr"
...
real0m6.189s
user0m0.336s
sys0m0.080s
ПримериспользованиямодуляthreadingдляподключенияпоSSHспомощьюnetmiko(файлnetmiko_threading.py):
Модульthreading
352
fromnetmikoimportConnectHandler
importsys
importyaml
importthreading
COMMAND=sys.argv[1]
devices=yaml.load(open('devices.yaml'))
defconnect_ssh(device_dict,command):
ssh=ConnectHandler(**device_dict)
ssh.enable()
result=ssh.send_command(command)
print"Connectiontodevice%s"%device_dict['ip']
printresult
defconn_threads(function,devices,command):
threads=[]
fordeviceindevices:
th=threading.Thread(target=function,args=(device,command))
th.start()
threads.append(th)
forthinthreads:
th.join()
conn_threads(connect_ssh,devices['routers'],COMMAND)
Времявыполнениякода:
$timepythonnetmiko_function_threading.py"shipintbr"
...
real0m2.229s
user0m0.408s
sys0m0.068s
Времяпочтивтриразаменьше.Но,надоучесть,чтотакаяситуациянебудетповторятьсяприбольшомколичествеподключений.
Комментариикфункцииconn_threads:
threading.Thread-класс,которыйсоздаетпотокемупередаетсяфункция,которуюнадовыполнить,иеёаргументы
th.start()-запускпотокаthreads.append(th)-потокдобавляетсявсписокth.join()-методожидаетзавершенияработыпотока
Модульthreading
353
методjoinвыполняетсядлякаждоногопотокавсписке.Такимобразомосновнаяпрограммазавершитсятолькокогдазавершатработувсепотокипоумолчанию,joinждетзавершенияработыпотокабесконечно.Но,можноограничитьвремяожиданияпередавjoinвремявсекундах.Втакомслучае,joinзавершитсяпослеуказанногоколичествасекунд.
Получениеданныхизпотоков
Впредыдущемпримере,данныевыводилисьнастандартныйпотоквывода.Дляполноценнойработыспотоками,необходимотакженаучитьсяполучатьданныеизпотоков.Чащевсего,дляэтогоиспользуетсяочередь.
ВPythonестьмодульQueue,которыйпозволяетсоздаватьразныетипыочередей.
Очередьэтоструктураданных,котораяиспользуетсяивработессетевымоборудованием.ОбъектQueue.Queue()-этоFIFOочередь.
Очередьпередаетсякакаргументвфункциюconnect_ssh,котораяподключаетсякустройствупоSSH.Результатвыполнениякомандыдобавляетсявочередь.
Примериспользованияпотоковсполучениемданных(файлnetmiko_threading_data.py):
Модульthreading
354
#-*-coding:utf-8-*-
fromnetmikoimportConnectHandler
importsys
importyaml
importthreading
fromQueueimportQueue
COMMAND=sys.argv[1]
devices=yaml.load(open('devices.yaml'))
defconnect_ssh(device_dict,command,queue):
ssh=ConnectHandler(**device_dict)
ssh.enable()
result=ssh.send_command(command)
print"Connectiontodevice%s"%device_dict['ip']
#Добавляемсловарьвочередь
queue.put({device_dict['ip']:result})
defconn_threads(function,devices,command):
threads=[]
#Создаемочередь
q=Queue()
fordeviceindevices:
#Передаемочередькакаргумент,функции
th=threading.Thread(target=function,args=(device,command,q))
th.start()
threads.append(th)
forthinthreads:
th.join()
results=[]
#Беремрезультатыизочередиидобавляемихвсписокresults
fortinthreads:
results.append(q.get())
returnresults
printconn_threads(connect_ssh,devices['routers'],COMMAND)
Обратитевнимание,чтовфункцииconnect_sshдобавилсяаргументqueue.
Очередьвполнеможновосприниматькаксписок:
методqueue.put()равнозначенlist.append()методqueue.get()равнозначенlist.pop(0)
Модульthreading
355
Дляработыспотокамиимодулемthreading,лучшеиспользоватьочередь.Но,конкретновданномпримере,можнобылобыиспользоватьисписок.
Примерсосписком,скореевсего,будетпрощепонять.Поэтомунижеаналогичныйкод,носиспользованиемобычногосписка,вместоочереди(файлnetmiko_threading_data_list.py):
#-*-coding:utf-8-*-
fromnetmikoimportConnectHandler
importsys
importyaml
importthreading
COMMAND=sys.argv[1]
devices=yaml.load(open('devices.yaml'))
defconnect_ssh(device_dict,command,queue):
ssh=ConnectHandler(**device_dict)
ssh.enable()
result=ssh.send_command(command)
print"Connectiontodevice%s"%device_dict['ip']
#Добавляемсловарьвсписок
queue.append({device_dict['ip']:result})
defconn_threads(function,devices,command):
threads=[]
q=[]
fordeviceindevices:
#Передаемсписоккакаргумент,функции
th=threading.Thread(target=function,args=(device,command,q))
th.start()
threads.append(th)
forthinthreads:
th.join()
#Этачастьнамненужна,таккак,прииспользованиисписка,
#мыпростоможемвернутьего
#results=[]
#fortinthreads:
#results.append(q.get())
returnq
printconn_threads(connect_ssh,devices['routers'],COMMAND)
Модульthreading
356
Модульthreading
357
МодульmultiprocessingМодульmultiprocessingиспользуетинтерфейсподобныймодулюthreading.Поэтомуперенестикодсиспользованияпотоковнаиспользованиепроцессов,обычно,достаточнолегко.
Каждомупроцессувыделяютсясвоиресурсы.Крометого,укаждогопроцессасвойGIL,азначит,неттехпроблем,которыебылиспотокамиикодможетвыполнятьсяпараллельноизадействоватьядра/процессорыкомпьютера.
Примериспользованиямодуляmultiprocessing(файлnetmiko_multiprocessing.py):
Модульmultiprocessing
358
importmultiprocessing
fromnetmikoimportConnectHandler
importsys
importyaml
COMMAND=sys.argv[1]
devices=yaml.load(open('devices.yaml'))
defconnect_ssh(device_dict,command,queue):
ssh=ConnectHandler(**device_dict)
ssh.enable()
result=ssh.send_command(command)
print"Connectiontodevice%s"%device_dict['ip']
queue.put({device_dict['ip']:result})
defconn_processes(function,devices,command):
processes=[]
queue=multiprocessing.Queue()
fordeviceindevices:
p=multiprocessing.Process(target=function,args=(device,command,queue)
)
p.start()
processes.append(p)
forpinprocesses:
p.join()
results=[]
forpinprocesses:
results.append(queue.get())
returnresults
print(conn_processes(connect_ssh,devices['routers'],COMMAND))
Обратитевнимание,чтоэтотпримераналогиченпоследнемупримеру,которыйиспользовалсясмодулемthreading.Единственноеотличиевтом,чтовмодулеmultiprocessingестьсвояреализацияочереди,поэтомунетнеобходимостииспользоватьмодульQueue.
Еслипроверитьвремяисполненияэтогоскрипта,аналогичногодлямодуляthreadingипоследовательногоподключения,тополучаемтакуюкартину:
Модульmultiprocessing
359
последовательное:5.833s
threading:2.225s
multiprocessing:2.365s
Времявыполнениядлямодуляmultiprocessingнемногобольше.Ноэтосвязаностем,чтонасозданиепроцессовуходитбольшевремени,чемнасозданиепотоков.Еслибыскриптбылсложнееивыполнялосьбольшезадач,илибылобыбольшеподключений,тогдабыmultiprocessingначалбысущественновыигрыватьумодуляthreading.
Модульmultiprocessing
360
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание12.1
Создатьфункциюsend_show_command.
ФункцияподключаетсяпоSSH(спомощьюnetmiko)кустройствамизсписка,ивыполняеткомандунаоснованиипереданныхаргументов.
Параметрыфункции:
devices_list-списоксловарейспараметрамиподключениякустройствам,которымнадопередатькомандыcommand-команда,которуюнадовыполнить
Функциявозвращаетсловарьсрезультатамивыполнениякоманды:
ключ-IPустройствазначение-результатвыполнениякоманды
Проверитьработуфункциинапримере:
устройствизфайлаdevices.yaml(дляэтогонадосчитатьинформациюизфайла)икомандыcommand
Задания
361
importnetmiko
command="shipintbr"
defsend_show_command(device_list,command):
"""
ФункцияподключаетсяпоSSHкустройствамизсписка,ивыполняеткоманду
наоснованиипереданныхаргументов.
Параметрыфункции:
-devices_list-списоксловарейспараметрамиподключениякустройствам,
которымнадопередатькоманды
-command-команда,которуюнадовыполнить
Функциявозвращаетсловарьсрезультатамивыполнениякоманды:
-ключ-IPустройства
-значение-результатвыполнениякоманды
"""
Задание12.2
Создатьфункциюsend_config_commands
ФункцияподключаетсяпоSSH(спомощьюnetmiko)кустройствамизсписка,ивыполняетпереченькомандвконфигурационномрежименаоснованиипереданныхаргументов.
Параметрыфункции:
devices_list-списоксловарейспараметрамиподключениякустройствам,которымнадопередатькомандыconfig_commands-списоккоманд,которыенадовыполнить
Функциявозвращаетсловарьсрезультатамивыполнениякоманды:
ключ-IPустройствазначение-выводсвыполнениемкоманд
Проверитьработуфункциинапримере:
устройствизфайлаdevices.yaml(дляэтогонадосчитатьинформациюизфайла)испискакомандcommands
Задания
362
importnetmiko
commands=['logging10.255.255.1',
'loggingbuffered20010',
'nologgingconsole']
defsend_config_commands(device_list,config_commands):
"""
ФункцияподключаетсяпоSSHкустройствамизсписка,
ивыполняеткомандывконфигурационномрежиме.
Параметрыфункции:
-devices_list-списоксловарейспараметрамиподключениякустройствам,кото
рымнадопередатькоманды
-config_commands-списоккоманд,которыенадовыполнить
Функциявозвращаетсловарьсрезультатамивыполнениякоманды:
-ключ-IPустройства
-значение-выводсвыполнениемкоманд
"""
Задание12.2a
Дополнитьфункциюsend_config_commandsиззадания12.2
Добавитьаргументoutput,которыйконтролируетбудетлирезультатвыполнениякомандвыводитьсянастандартныйпотоквывода.Поумолчанию,результатдолженвыводиться.
Задание12.2b
Дополнитьфункциюsend_config_commandsиззадания12.2aили12.2
Добавитьпроверкунаошибки:
Привыполнениикоманд,скриптдолженпроверятьрезультатнатакиеошибки:Invalidinputdetected,Incompletecommand,Ambiguouscommand
Еслипривыполнениикакой-тоизкомандвозниклаошибка,функциядолжнавыводитьсообщениенастандартныйпотоквыводасинформациейотом,какаяошибкавозникла,привыполнениикакойкомандыинакакомустройстве.
Проверитьфункциюнакомандесошибкой.
Задание12.3
Задания
363
Создатьфункциюsend_commands(дляподключенияпоSSHиспользуетсяnetmiko).
Параметрыфункции:
devices_list-списоксловарейспараметрамиподключениякустройствам,которымнадопередатькомандыshow-однакомандаshow(строка)filename-имяфайла,вкоторомнаходятсякоманды,которыенадовыполнить(строка)config-списокскомандами,которыенадовыполнитьвконфигурационномрежиме
Взависимостиоттого,какойаргументбылпередан,функциявызываетразныефункциивнутри.
Далеекомбинацияизаргументаисоответствующейфункции:
show--функцияsend_show_commandиззадания12.1config--функцияsend_config_commandsиззадания12.2,12.2aили12.2bfilename--функцияsend_commands_from_file(еетакженадонаписатьпоаналогииспредыдущими)
Функциявозвращаетсловарьсрезультатамивыполнениякоманды:
ключ-IPустройствазначение-выводсвыполнениемкоманд
Проверитьработуфункциинапримере:
устройствизфайлаdevices.yaml(дляэтогонадосчитатьинформациюизфайла)иразличныхкомбинацияаргументаскомандами:
спискакомандcommandsкомандыcommandфайлаconfig.txt
Задания
364
fromnetmikoimportConnectHandler
commands=['logging10.255.255.1',
'loggingbuffered20010',
'nologgingconsole']
command="shipintbr"
defsend_show_command(device_list,show_command):
pass
defsend_config_commands(device_list,config_commands,output=True):
pass
defsend_commands_from_file(device_list,filename):
pass
defsend_commands(device_list,config=[],show='',filename=''):
pass
Задание12.3a
Изменитьфункциюsend_commandsтакимобразом,чтобывспискесловарейdevice_listненадобылоуказыватьимяпользователя,пароль,ипарольнаenable.
Функциядолжназапрашиватьимяпользователя,парольипарольнаenableпристарте.Парольнедолженотображатьсяпринаборе.
Вфайлеdevices2.yamlэтипараметрыужеудалены.
Задание12.3b
Дополнитьфункциюsend_commandsтакимобразом,чтобыпередподключениемкустройствампоSSH,выполняласьпроверкадоступностиустройстваpingом(можновызватькомандуpingвОС).
КаквыполнятькомандыОС,описановразделеsubprocess.Тамжеестьпримерфункциисотправкойping.
Еслиустройстводоступно,можновыполнятьподключение.Еслинедоступно,вывестисообщениеотом,чтоустройствосопределеннымIP-адресомнедоступноиневыполнятьподключение.
Дляудобстваможносделатьотдельнуюфункциюдляпроверкидоступностиизатемиспользоватьеевфункцииsend_commands.
Задания
365
Задание12.4
Взаданиииспользуетсяпримеризразделапромодульthreading.
Переделатьпримертакимобразом,чтобы:
вместофункцииconnect_ssh,использоваласьфункцияsend_commandsиззадания12.3
переделатьфункциюsend_commands,чтобыиспользоваласьочередьифункцияconn_threadsпо-прежнемувозвращаласловарьсрезультатами.Проверитьработусоспискомкоманд,скомандамиизфайла,скомандойshow
Примеризраздела:
Задания
366
fromnetmikoimportConnectHandler
importsys
importyaml
importthreading
fromQueueimportQueue
COMMAND=sys.argv[1]
devices=yaml.load(open('devices.yaml'))
defconnect_ssh(device_dict,command,queue):
ssh=ConnectHandler(**device_dict)
ssh.enable()
result=ssh.send_command(command)
print"Connectiontodevice%s"%device_dict['ip']
queue.put({device_dict['ip']:result})
defconn_threads(function,devices,command):
threads=[]
q=Queue()
fordeviceindevices:
th=threading.Thread(target=function,args=(device,command,q))
th.start()
threads.append(th)
forthinthreads:
th.join()
results=[]
fortinthreads:
results.append(q.get())
returnresults
printconn_threads(connect_ssh,devices['routers'],COMMAND)
Задание12.5
Использоватьфункцииполученныеврезультатевыполнениязадания12.4.
Переделатьфункциюconn_threadsтакимобразом,чтобыспомощьюаргументаlimit,можнобылоуказыватьсколькоподключенийбудутвыполнятьсяпараллельно.Поумолчанию,значениеаргументадолжнобыть2.
Изменитьфункциюсоответственно,так,чтобыпараллельныхподключенийвыполнялосьстолько,сколькоуказановаргументеlimit.
Задания
367
Задание12.6
Взаданиииспользуетсяпримеризразделапромодульmultiprocessing.
Переделатьпримертакимобразом,чтобы:
вместофункцииconnect_ssh,использоваласьфункцияsend_commandsиззадания12.3
переделатьфункциюsend_commands,чтобыиспользоваласьочередьифункцияconn_processesпо-прежнемувозвращаласловарьсрезультатами.Проверитьработусоспискомкоманд,скомандамиизфайла,скомандойshow
Примеризраздела:
Задания
368
importmultiprocessing
fromnetmikoimportConnectHandler
importsys
importyaml
COMMAND=sys.argv[1]
devices=yaml.load(open('devices.yaml'))
defconnect_ssh(device_dict,command,queue):
ssh=ConnectHandler(**device_dict)
ssh.enable()
result=ssh.send_command(command)
print"Connectiontodevice%s"%device_dict['ip']
queue.put({device_dict['ip']:result})
defconn_processes(function,devices,command):
processes=[]
queue=multiprocessing.Queue()
fordeviceindevices:
p=multiprocessing.Process(target=function,args=(device,command,queue)
)
p.start()
processes.append(p)
forpinprocesses:
p.join()
results=[]
forpinprocesses:
results.append(queue.get())
returnresults
print(conn_processes(connect_ssh,devices['routers'],COMMAND))
Задание12.7
Использоватьфункцииполученныеврезультатевыполнениязадания12.6.
Переделатьфункциюconn_processesтакимобразом,чтобыспомощьюаргументаlimit,можнобылоуказыватьсколькоподключенийбудутвыполнятьсяпараллельно.Поумолчанию,значениеаргументадолжнобыть2.
Изменитьфункциюсоответственно,так,чтобыпараллельныхподключенийвыполнялосьстолько,сколькоуказановаргументеlimit.
Задания
369
Задания
370
ШаблоныконфигурацийсJinjaJinja2этоязыкшаблонов,которыйиспользуетсявPython.Jinjaэтонеединственныйязыкшаблонов(шаблонизатор)дляPythonи,конечноже,неединственныйязыкшаблоноввцелом.
Jinja2используетсядлягенерациидокументовнаосновеодногоилинесколькихшаблонов.
Примерыиспользования:
шаблоныдлягенерацииHTML-страницшаблоныдлягенерацииконфигурационныхфайловвUnix/Linuxшаблоныдлягенерацииконфигурационныхфайловсетевыхустройств
УстановитьJinja2можноспомощьюpip:
pipinstalljinja2
ДалеетерминыJinjaиJinja2используютсявзаимозаменяемо.
ИдеяJinjaоченьпроста:разделениеданныхишаблона.Этопозволяетиспользоватьодинитотжешаблон,ноподставлятьвнегоразныеданные.
Всамомпростомслучае,шаблонэтопростотекстовыйфайл,вкоторомуказаныместаподстановкизначений,спомощьюпеременныхJinja.
ПримершаблонаJinja:
hostname{{name}}
!
interfaceLoopback255
descriptionManagementloopback
ipaddress10.255.{{id}}.1255.255.255.255
!
interfaceGigabitEthernet0/0
descriptionLANto{{name}}sw1{{int}}
ipaddress{{ip}}255.255.255.0
!
routerospf10
router-id10.255.{{id}}.1
auto-costreference-bandwidth10000
network10.0.0.00.255.255.255area0
13.ШаблоныконфигурацийсJinja
371
Комментариикшаблону:
ВJinjaпеременныезаписываютсявдвойныхфигурныхскобках.Привыполнениискрипта,этипеременныезаменяютсянужнымизначениями.
Этотшаблонможетиспользоватьсядлягенерацииконфигурацииразныхустройств,спомощьюподстановкидругихнаборовпеременных.
ПримерскриптасгенерациейфайланаосновешаблонаJinja(файлbasic_generator.py):
fromjinja2importTemplate
template=Template(u"""
hostname{{name}}
!
interfaceLoopback255
descriptionManagementloopback
ipaddress10.255.{{id}}.1255.255.255.255
!
interfaceGigabitEthernet0/0
descriptionLANto{{name}}sw1{{int}}
ipaddress{{ip}}255.255.255.0
!
routerospf10
router-id10.255.{{id}}.1
auto-costreference-bandwidth10000
network10.0.0.00.255.255.255area0
""")
liverpool={'id':'11','name':'Liverpool','int':'Gi1/0/17','ip':'10.1.1.10'}
printtemplate.render(liverpool)
Комментариикфайлуbasic_generator.py:
впервойстрокеизJinja2импортируетсяклассTemplateсоздаетсяобъектtemplate,которомупередаетсяшаблон
вшаблонеиспользуютсяпеременныевсинтаксисеJinjaвсловареliverpoolключидолжныбытьтакимиже,какименапеременныхвшаблоне
значения,которыесоответствуютключам,этотеданные,которыебудутподставленынаместопеременных
последняястрокарендеритшаблониспользуясловарьliverpool,тоесть,подставляетзначениявпеременные.
Еслизапуститьскриптbasic_generator.py,товыводбудеттаким:
13.ШаблоныконфигурацийсJinja
372
$pythonbasic_generator.py
hostnameLiverpool
!
interfaceLoopback255
descriptionManagementloopback
ipaddress10.255.11.1255.255.255.255
!
interfaceGigabitEthernet0/0
descriptionLANtoLiverpoolsw1Gi1/0/17
ipaddress10.1.1.10255.255.255.0
!
routerospf10
router-id10.255.11.1
auto-costreference-bandwidth10000
network10.0.0.00.255.255.255area0
13.ШаблоныконфигурацийсJinja
373
ПримериспользованияJinja2Вэтомпримерелогикаразнесенав3разныхфайла(всефайлынаходятсявкаталоге1_example):
router_template.py-шаблонrouters_info.yml-вэтомфайле,ввидеспискасловарей(вформатеYAML),находитсяинформацияомаршрутизаторах,длякоторыхнужносгенерироватьконфигурационныйфайлrouter_config_generator.py-вэтомскриптеимпортируетсяфайлсшаблономисчитываетсяинформацияизфайлавформатеYAML,азатемгенерируютсяконфигурационныефайлымаршрутизаторов
Файлrouter_template.py
ПримериспользованияJinja
374
#-*-coding:utf-8-*-
fromjinja2importTemplate
importsys
reload(sys)
sys.setdefaultencoding('utf-8')
template_r1=Template(u"""
hostname{{name}}
!
interfaceLoopback10
descriptionMPLSloopback
ipaddress10.10.{{id}}.1255.255.255.255
!
interfaceGigabitEthernet0/0
descriptionWANto{{name}}sw1G0/1
!
interfaceGigabitEthernet0/0.1{{id}}1
descriptionMPLSto{{to_name}}
encapsulationdot1Q1{{id}}1
ipaddress10.{{id}}.1.2255.255.255.252
ipospfnetworkpoint-to-point
ipospfhello-interval1
ipospfcost10
!
interfaceGigabitEthernet0/1
descriptionLAN{{name}}tosw1G0/2!
interfaceGigabitEthernet0/1.{{IT}}
descriptionPWIT{{name}}-{{to_name}}
encapsulationdot1Q{{IT}}
xconnect10.10.{{to_id}}.1{{id}}11encapsulationmpls
backuppeer10.10.{{to_id}}.2{{id}}21
backupdelay11
!
interfaceGigabitEthernet0/1.{{BS}}
descriptionPWBS{{name}}-{{to_name}}
encapsulationdot1Q{{BS}}
xconnect10.10.{{to_id}}.1{{to_id}}{{id}}11encapsulationmpls
backuppeer10.10.{{to_id}}.2{{to_id}}{{id}}21
backupdelay11
!
routerospf10
router-id10.10.{{id}}.1
auto-costreference-bandwidth10000
network10.0.0.00.255.255.255area0
!
""")
ПримериспользованияJinja
375
Этистрокименяюткодировкупоумолчаниюasciiнаutf-8:
reload(sys)
sys.setdefaultencoding('utf-8')
Файлrouters_info.yml
-id:11
name:Liverpool
to_name:LONDON
IT:791
BS:1550
to_id:1
-id:12
name:Bristol
to_name:LONDON
IT:793
BS:1510
to_id:1
-id:14
name:Coventry
to_name:Manchester
IT:892
BS:1650
to_id:2
Файлrouter_config_generator.py
#-*-coding:utf-8-*-
importyaml
fromjinja2importTemplate
fromrouter_templateimporttemplate_r1
routers=yaml.load(open('routers_info.yml'))
forrouterinrouters:
r1_conf=router['name']+'_r1.txt'
withopen(r1_conf,'w')asf:
f.write(template_r1.render(router))
Файлrouter_config_generator.py:
импортируетшаблонtemplate_r1изфайлаrouters_info.ymlсписокпараметровсчитываетсявпеременнуюrouters
Затемвциклеперебираютсяобъекты(словари)вспискеrouters:
ПримериспользованияJinja
376
названиефайла,вкоторыйзаписываетсяитоговаяконфигурация,состоитизполяnameвсловареистроки_r1.txt
например,Liverpool_r1.txtфайлстакимименемоткрываетсяврежимедлязаписивфайлзаписываетсярезультатрендерингашаблонасиспользованиемтекущегословаряконструкцияwithсамазакрываетфайлуправлениевозвращаетсявначалоцикла(поканепереберутсявсесловари)
Запускаемфайлrouter_config_generator.py:
$pythonrouter_config_generator.py
Врезультатеполучатсятриконфигурационныхфайла:
Liverpool_r1.txtBristol_r1.txtCoventry_r1.txt
ПримериспользованияJinja
377
hostnameLiverpool
!
interfaceLoopback10
descriptionMPLSloopback
ipaddress10.10.11.1255.255.255.255
!
interfaceGigabitEthernet0/0
descriptionWANtoLiverpoolsw1G0/1
!
interfaceGigabitEthernet0/0.1111
descriptionMPLStoLONDON
encapsulationdot1Q1111
ipaddress10.11.1.2255.255.255.252
ipospfnetworkpoint-to-point
ipospfhello-interval1
ipospfcost10
!
interfaceGigabitEthernet0/1
descriptionLANLiverpooltosw1G0/2
!
interfaceGigabitEthernet0/1.791
descriptionPWITLiverpool-LONDON
encapsulationdot1Q791
xconnect10.10.1.11111encapsulationmpls
backuppeer10.10.1.21121
backupdelay11
!
interfaceGigabitEthernet0/1.1550
descriptionPWBSLiverpool-LONDON
encapsulationdot1Q1550
xconnect10.10.1.111111encapsulationmpls
backuppeer10.10.1.211121
backupdelay11
!
routerospf10
router-id10.10.11.1
auto-costreference-bandwidth10000
network10.0.0.00.255.255.255area0
!
ПримериспользованияJinja
378
hostnameBristol
!
interfaceLoopback10
descriptionMPLSloopback
ipaddress10.10.12.1255.255.255.255
!
interfaceGigabitEthernet0/0
descriptionWANtoBristolsw1G0/1
!
interfaceGigabitEthernet0/0.1121
descriptionMPLStoLONDON
encapsulationdot1Q1121
ipaddress10.12.1.2255.255.255.252
ipospfnetworkpoint-to-point
ipospfhello-interval1
ipospfcost10
!
interfaceGigabitEthernet0/1
descriptionLANBristoltosw1G0/2
!
interfaceGigabitEthernet0/1.793
descriptionPWITBristol-LONDON
encapsulationdot1Q793
xconnect10.10.1.11211encapsulationmpls
backuppeer10.10.1.21221
backupdelay11
!
interfaceGigabitEthernet0/1.1510
descriptionPWBSBristol-LONDON
encapsulationdot1Q1510
xconnect10.10.1.111211encapsulationmpls
backuppeer10.10.1.211221
backupdelay11
!
routerospf10
router-id10.10.12.1
auto-costreference-bandwidth10000
network10.0.0.00.255.255.255area0
!
ПримериспользованияJinja
379
hostnameCoventry
!
interfaceLoopback10
descriptionMPLSloopback
ipaddress10.10.14.1255.255.255.255
!
interfaceGigabitEthernet0/0
descriptionWANtoCoventrysw1G0/1
!
interfaceGigabitEthernet0/0.1141
descriptionMPLStoManchester
encapsulationdot1Q1141
ipaddress10.14.1.2255.255.255.252
ipospfnetworkpoint-to-point
ipospfhello-interval1
ipospfcost10
!
interfaceGigabitEthernet0/1
descriptionLANCoventrytosw1G0/2
!
interfaceGigabitEthernet0/1.892
descriptionPWITCoventry-Manchester
encapsulationdot1Q892
xconnect10.10.2.11411encapsulationmpls
backuppeer10.10.2.21421
backupdelay11
!
interfaceGigabitEthernet0/1.1650
descriptionPWBSCoventry-Manchester
encapsulationdot1Q1650
xconnect10.10.2.121411encapsulationmpls
backuppeer10.10.2.221421
backupdelay11
!
routerospf10
router-id10.10.14.1
auto-costreference-bandwidth10000
network10.0.0.00.255.255.255area0
!
ПримериспользованияJinja
380
ПримериспользованияJinjaскорректнымиспользованиемпрограммногоинтерфейсаДлятогочтобыразобратьсясJinja2,лучшеиспользоватьпредыдущиепримеры.ВэтомразделеописываетсякорректноеиспользованиеJinja.Втакомвариантеданные,шаблонискрипт,которыйгенерируетитоговуюинформацию,разделены.
Термин"программныйинтерфейс"относитсякспособуработыJinjaсвводнымиданнымиишаблоном,длягенерацииитоговыхфайлов.
Переделанныйпримерпредыдущегоскрипта,шаблонаифайласданными(всефайлынаходятсявкаталоге2_example):
Шаблонtemplates/router_template.txtэтообычныйтекстовыйфайл:
ПримериспользованияJinjaскорректнымиспользованиемпрограммногоинтерфейса
381
hostname{{name}}
!
interfaceLoopback10
descriptionMPLSloopback
ipaddress10.10.{{id}}.1255.255.255.255
!
interfaceGigabitEthernet0/0
descriptionWANto{{name}}sw1G0/1
!
interfaceGigabitEthernet0/0.1{{id}}1
descriptionMPLSto{{to_name}}
encapsulationdot1Q1{{id}}1
ipaddress10.{{id}}.1.2255.255.255.252
ipospfnetworkpoint-to-point
ipospfhello-interval1
ipospfcost10
!
interfaceGigabitEthernet0/1
descriptionLAN{{name}}tosw1G0/2!
interfaceGigabitEthernet0/1.{{IT}}
descriptionPWIT{{name}}-{{to_name}}
encapsulationdot1Q{{IT}}
xconnect10.10.{{to_id}}.1{{id}}11encapsulationmpls
backuppeer10.10.{{to_id}}.2{{id}}21
backupdelay11
!
interfaceGigabitEthernet0/1.{{BS}}
descriptionPWBS{{name}}-{{to_name}}
encapsulationdot1Q{{BS}}
xconnect10.10.{{to_id}}.1{{to_id}}{{id}}11encapsulationmpls
backuppeer10.10.{{to_id}}.2{{to_id}}{{id}}21
backupdelay11
!
routerospf10
router-id10.10.{{id}}.1
auto-costreference-bandwidth10000
network10.0.0.00.255.255.255area0
!
Файлсданнымиrouters_info.yml
ПримериспользованияJinjaскорректнымиспользованиемпрограммногоинтерфейса
382
-id:11
name:Liverpool
to_name:LONDON
IT:791
BS:1550
to_id:1
-id:12
name:Bristol
to_name:LONDON
IT:793
BS:1510
to_id:1
-id:14
name:Coventry
to_name:Manchester
IT:892
BS:1650
to_id:2
Скриптдлягенерацииконфигурацийrouter_config_generator_ver2.py
#-*-coding:utf-8-*-
fromjinja2importEnvironment,FileSystemLoader
importyaml
importsys
reload(sys)
sys.setdefaultencoding('utf-8')
env=Environment(loader=FileSystemLoader('templates'))
template=env.get_template('router_template.txt')
routers=yaml.load(open('routers_info.yml'))
forrouterinrouters:
r1_conf=router['name']+'_r1.txt'
withopen(r1_conf,'w')asf:
f.write(template.render(router))
Файлrouter_config_generator.pyимпортируетизмодуляjinja2:
FileSystemLoader-загрузчик,которыйпозволяетработатьсфайловойсистемойтутуказываетсяпутьккаталогу,гденаходятсяшаблонывданномслучае,шаблоннаходитсявкаталогеtemplates
Environment-классдляописанияпараметровокружения:вданномслучае,указантолькозагрузчикновнемможноуказыватьметодыобрабаткишаблона
ПримериспользованияJinjaскорректнымиспользованиемпрограммногоинтерфейса
383
Обратитевнимание,чтошаблонтеперьнаходитсявкаталогеtemplates.
Еслишаблонынаходятсявтекущемкаталоге,надодобавитьпарустрокиизменитьзначениевзагрузчике:
importos
curr_dir=os.path.dirname(os.path.abspath(__file__))
env=Environment(loader=FileSystemLoader(curr_dir))
Переменная__file__-этоспециальнаяпеременнаямодуля,котораявыставляетсяравнойименискрипта,которыйбылзапущеннапрямую.Иравнаполномупутикмодулю,когдаонимпортируется.Подробнееоспециальныхпеременныхиметодах.
Методget_template()используетсядлятого,чтобыполучитьшаблон.Вскобкахуказываетсяимяфайла.
Последняячастьосталасьнеизменной.
ПримериспользованияJinjaскорректнымиспользованиемпрограммногоинтерфейса
384
СинтаксисшаблоновJinja2Досихпор,впримерахшаблоновJinja2использоваласьтолькоподстановкапеременных.Этосамыйпростойипонятныйпримериспользованияшаблонов.НосинтаксисшаблоновJinjaнаэтомнеограничивается.
ВшаблонахJinja2можноиспользовать:
переменныеусловия(if/else)циклы(for)фильтры-специальныевстроенныеметоды,которыепозволяютделатьпреобразованияпеременныхтесты-используютсядляпроверкисоответствуетлипеременнаякакому-тоусловию
Крометого,Jinjaподдерживаетнаследованиемеждушаблонами.Атакжепозволяетдобавлятьсодержимоеодногошаблонавдругой.
Мыразберемсясосновамиэтихвозможностей.ПодробнееошаблонахJinja2можнопочитатьвдокументации.
Всефайлы,которыеиспользуютсякакпримерывэтомподразделе,находятсявкаталоге3_template_syntax/
Длягенерациишаблоновбудетиспользоватьскриптcfg_gen.py
#-*-coding:utf-8-*-
fromjinja2importEnvironment,FileSystemLoader
importyaml
importsys
reload(sys)
sys.setdefaultencoding('utf-8')
TEMPLATE_DIR,template=sys.argv[1].split('/')
VARS_FILE=sys.argv[2]
env=Environment(loader=FileSystemLoader(TEMPLATE_DIR),
trim_blocks=True,lstrip_blocks=True)
template=env.get_template(template)
vars_dict=yaml.load(open(VARS_FILE))
printtemplate.render(vars_dict)
СинтаксисшаблоновJinja2
385
Длятого,чтобыпосмотретьнарезультат,нужновызватьскриптипередатьемудвааргумента:
шаблонфайлспеременными,вформатеYAML
Результатбудетвыведеннастандартныйпотоквывода.
Примерзапускаскрипта:
$pythoncfg_gen.pytemplates/variables.txtdata_files/vars.yml
Параметрыtrim_blocksиlstrip_blocksописыванывследующемподразделе.
СинтаксисшаблоновJinja2
386
Контрольсимволовwhitespace
trim_blocks,lstrip_blocks
Параметрtrim_blocksудаляетпервуюпустуюстрокупослеблокаконструкции,еслиегозначениеравноTrue(поумолчаниюFalse).
Посмотримнаэффектпримененияфлаганапримерешаблонаtemplates/env_flags.txt:
routerbgp{{bgp.local_as}}
{%foribgpinbgp.ibgp_neighbors%}
neighbor{{ibgp}}remote-as{{bgp.local_as}}
neighbor{{ibgp}}update-source{{bgp.loopback}}
{%endfor%}
Еслискриптcfg_gen.pyзапускаетсябезфлаговtrim_blocks,lstrip_blocks:
env=Environment(loader=FileSystemLoader(TEMPLATE_DIR))
Выводбудеттаким:
$pythoncfg_gen.pytemplates/env_flags.txtdata_files/router.yml
routerbgp100
neighbor10.0.0.2remote-as100
neighbor10.0.0.2update-sourcelo100
neighbor10.0.0.3remote-as100
neighbor10.0.0.3update-sourcelo100
Из-заблока{%foribgpinbgp.ibgp_neighbors%}появляютсяпереводыстрок.Поумолчанию,такоежеповедениебудетслюбымидругимиблокамиJinja.
Придобавлениифлагаtrim_blocksтакимобразом:
env=Environment(loader=FileSystemLoader(TEMPLATE_DIR),trim_blocks=True)
Результатвыполнениябудеттаким:
Контрольсимволовwhitespace
387
$pythoncfg_gen.pytemplates/env_flags.txtdata_files/router.yml
routerbgp100
neighbor10.0.0.2remote-as100
neighbor10.0.0.2update-sourcelo100
neighbor10.0.0.3remote-as100
neighbor10.0.0.3update-sourcelo100
Былиудаленыпустыестрокипослеблока.
Нопередстрокамиneighbor...remote-asпоявилисьдвапробела.Такполучилосьиз-затого,чтопередблоком{%foribgpinbgp.ibgp_neighbors%}стоитпробел.Послетого,какбылотключенлишнийпереводстроки,пробелыитабыпередблокомдобавляютсякпервойстрокеблока.
Ноэтоневлияетнаследующиестроки.Поэтомустрокисneighbor...update-sourceотображаютсясоднимпробелом.
Параметрlstrip_blocksконтролируетто,будутлиудалятьсяпробелыитабыотначаластрокидоначалаблока(дооткрывающейсяфигурнойскобки).
Еслидобавитьаргументlstrip_blocks=Trueтакимобразом:
env=Environment(loader=FileSystemLoader(TEMPLATE_DIR),trim_blocks=True,lstrip_bl
ocks=True)
Результатвыполнениябудеттаким:
$pythoncfg_gen.pytemplates/env_flags.txtdata_files/router.yml
routerbgp100
neighbor10.0.0.2remote-as100
neighbor10.0.0.2update-sourcelo100
neighbor10.0.0.3remote-as100
neighbor10.0.0.3update-sourcelo100
Отключениеlstrip_blocksдляблока
Иногда,нужноотключитьфункциональностьlstrip_blocksдляблока.
Например,еслипараметрlstrip_blocksустановленравнымTrueвокружении,нонужноотключитьегодлявторогоблокавшаблоне(файлtemplates/env_flags2.txt):
Контрольсимволовwhitespace
388
routerbgp{{bgp.local_as}}
{%foribgpinbgp.ibgp_neighbors%}
neighbor{{ibgp}}remote-as{{bgp.local_as}}
neighbor{{ibgp}}update-source{{bgp.loopback}}
{%endfor%}
routerbgp{{bgp.local_as}}
{%+foribgpinbgp.ibgp_neighbors%}
neighbor{{ibgp}}remote-as{{bgp.local_as}}
neighbor{{ibgp}}update-source{{bgp.loopback}}
{%endfor%}
Результатбудеттаким:
$pythoncfg_gen.pytemplates/env_flags2.txtdata_files/router.yml
routerbgp100
neighbor10.0.0.2remote-as100
neighbor10.0.0.2update-sourcelo100
neighbor10.0.0.3remote-as100
neighbor10.0.0.3update-sourcelo100
routerbgp100
neighbor10.0.0.2remote-as100
neighbor10.0.0.2update-sourcelo100
neighbor10.0.0.3remote-as100
neighbor10.0.0.3update-sourcelo100
Плюспослезнакапроцентаотключаетlstrip_blocksдляблока.Вданномслучае,толькодляначалаблока.
Еслисделатьтакимобразом(плюсдобавленввыражениидлязавершенияблока):
routerbgp{{bgp.local_as}}
{%foribgpinbgp.ibgp_neighbors%}
neighbor{{ibgp}}remote-as{{bgp.local_as}}
neighbor{{ibgp}}update-source{{bgp.loopback}}
{%endfor%}
routerbgp{{bgp.local_as}}
{%+foribgpinbgp.ibgp_neighbors%}
neighbor{{ibgp}}remote-as{{bgp.local_as}}
neighbor{{ibgp}}update-source{{bgp.loopback}}
{%+endfor%}
Онбудетотключенидляконцаблока:
Контрольсимволовwhitespace
389
$pythoncfg_gen.pytemplates/env_flags2.txtdata_files/router.yml
routerbgp100
neighbor10.0.0.2remote-as100
neighbor10.0.0.2update-sourcelo100
neighbor10.0.0.3remote-as100
neighbor10.0.0.3update-sourcelo100
routerbgp100
neighbor10.0.0.2remote-as100
neighbor10.0.0.2update-sourcelo100
neighbor10.0.0.3remote-as100
neighbor10.0.0.3update-sourcelo100
Удалениеwhitespaceвблоке
Аналогичнымобразомможноконтролироватьудалениеwhitespaceдляблока.
Дляэтогопримеравокруженииневыставленыфлаги:
env=Environment(loader=FileSystemLoader(TEMPLATE_DIR))
Шаблонtemplates/env_flags3.txt:
routerbgp{{bgp.local_as}}
{%foribgpinbgp.ibgp_neighbors%}
neighbor{{ibgp}}remote-as{{bgp.local_as}}
neighbor{{ibgp}}update-source{{bgp.loopback}}
{%endfor%}
routerbgp{{bgp.local_as}}
{%-foribgpinbgp.ibgp_neighbors%}
neighbor{{ibgp}}remote-as{{bgp.local_as}}
neighbor{{ibgp}}update-source{{bgp.loopback}}
{%endfor%}
Обратитевниманиенаминусвначалевторогоблока.Минутудаляетвсеwhitespaceсимволы.Вданномслучае,вначалеблока.
Результатбудеттаким:
Контрольсимволовwhitespace
390
$pythoncfg_gen.pytemplates/env_flags3.txtdata_files/router.yml
routerbgp100
neighbor10.0.0.2remote-as100
neighbor10.0.0.2update-sourcelo100
neighbor10.0.0.3remote-as100
neighbor10.0.0.3update-sourcelo100
routerbgp100
neighbor10.0.0.2remote-as100
neighbor10.0.0.2update-sourcelo100
neighbor10.0.0.3remote-as100
neighbor10.0.0.3update-sourcelo100
Еслидобавитьминутвконецблока:
routerbgp{{bgp.local_as}}
{%foribgpinbgp.ibgp_neighbors%}
neighbor{{ibgp}}remote-as{{bgp.local_as}}
neighbor{{ibgp}}update-source{{bgp.loopback}}
{%endfor%}
routerbgp{{bgp.local_as}}
{%-foribgpinbgp.ibgp_neighbors%}
neighbor{{ibgp}}remote-as{{bgp.local_as}}
neighbor{{ibgp}}update-source{{bgp.loopback}}
{%-endfor%}
Удалитсяпустаястрокаивконцеблока:
$pythoncfg_gen.pytemplates/env_flags3.txtdata_files/router.yml
routerbgp100
neighbor10.0.0.2remote-as100
neighbor10.0.0.2update-sourcelo100
neighbor10.0.0.3remote-as100
neighbor10.0.0.3update-sourcelo100
routerbgp100
neighbor10.0.0.2remote-as100
neighbor10.0.0.2update-sourcelo100
neighbor10.0.0.3remote-as100
neighbor10.0.0.3update-sourcelo100
Контрольсимволовwhitespace
391
Попробуйтедобавитьминусвконцевыраженийописывающихблокипосмотретьнарезультат:
routerbgp{{bgp.local_as}}
{%foribgpinbgp.ibgp_neighbors%}
neighbor{{ibgp}}remote-as{{bgp.local_as}}
neighbor{{ibgp}}update-source{{bgp.loopback}}
{%endfor%}
routerbgp{{bgp.local_as}}
{%-foribgpinbgp.ibgp_neighbors-%}
neighbor{{ibgp}}remote-as{{bgp.local_as}}
neighbor{{ibgp}}update-source{{bgp.loopback}}
{%-endfor-%}
Контрольсимволовwhitespace
392
ПеременныеПеременныевшаблонеуказываютсявдвойныхфигурныхскобках:
hostname{{name}}
interfaceLoopback0
ipaddress10.0.0.{{id}}255.255.255.255
Значенияпеременныхподставляютсянаосновесловаря,которыйпередаетсяшаблону.
Переменная,котораяпередаетсявсловаре,можетбытьнетолькочисломилистрокой,нои,например,спискомилисловарем.Внутришаблонаможно,соответственно,обращатьсякэлементупономеруилипоключу.
Примершаблонаtemplates/variables.txt,сиспользованиемразныхвариантовпеременных:
hostname{{name}}
interfaceLoopback0
ipaddress10.0.0.{{id}}255.255.255.255
vlan{{vlans[0]}}
routerospf1
router-id10.0.0.{{id}}
auto-costreference-bandwidth10000
network{{ospf.network}}area{{ospf['area']}}
Исоответствующийфайлdata_files/vars.ymlспеременными:
id:3
name:R3
vlans:
-10
-20
-30
ospf:
network:10.0.1.00.0.0.255
area:0
Обратитевниманиенаиспользованиепеременнойvlansвшаблоне:
Переменные
393
таккакпеременнаяvlansэтосписок,можноуказыватькакойименноэлементизсписканамнужен
Еслипередаетсясловарь(каквслучаеспеременнойospf),товнутришаблонаможнообращатьсякобъектамсловаря,используяодинизвариантов:
ospf.networkилиospf['network']
Результатзапускаскриптабудеттаким:
$pythoncfg_gen.pytemplates/variables.txtdata_files/vars.yml
hostnameR3
interfaceLoopback0
ipaddress10.0.0.3255.255.255.255
vlan10
routerospf1
router-id10.0.0.3
auto-costreference-bandwidth10000
network10.0.1.00.0.0.255area0
Переменные
394
ЦиклforЦиклforпозволяетпроходитьсяпоэлементампоследовательности.
Циклforдолженнаходитьсявнутрисимволов{%%}.Крометого,нужноявноуказыватьзавершениецикла:
{%forvlaninvlans%}
vlan{{vlan}}
{%endfor%}
Примершаблонаtemplates/for.txtсиспользованиемцикла:
hostname{{name}}
interfaceLoopback0
ipaddress10.0.0.{{id}}255.255.255.255
{%forvlan,nameinvlans.iteritems()%}
vlan{{vlan}}
name{{name}}
{%endfor%}
routerospf1
router-id10.0.0.{{id}}
auto-costreference-bandwidth10000
{%fornetworksinospf%}
network{{networks.network}}area{{networks.area}}
{%endfor%}
Файлdata_files/for.ymlспеременными:
id:3
name:R3
vlans:
10:Marketing
20:Voice
30:Management
ospf:
-network:10.0.1.00.0.0.255
area:0
-network:10.0.2.00.0.0.255
area:2
-network:10.1.1.00.0.0.255
area:0
for
395
Вциклеforможнопроходитьсякакпоэлементамсписка(например,списокospf),такипословарю(словарьvlans).И,аналогичнымобразом,полюбойпоследовательности.
Элементысловарянеупорядочены.Поэтому,еслинужнополучитьупорядоченныеэлементы,можнолибоиспользоватьотсортированныйсписоккортежей,либоиспользоватьупорядоченныйсловарьcollections.OrderedDict.
Результатвыполнениябудеттаким:
$pythoncfg_gen.pytemplates/for.txtdata_files/for.yml
hostnameR3
interfaceLoopback0
ipaddress10.0.0.3255.255.255.255
vlan10
nameMarketing
vlan20
nameVoice
vlan30
nameManagement
routerospf1
router-id10.0.0.3
auto-costreference-bandwidth10000
network10.0.1.00.0.0.255area0
network10.0.2.00.0.0.255area2
network10.1.1.00.0.0.255area0
for
396
if/elif/elseifпозволяетдобавлятьусловиевшаблон.Например,можноиспользоватьifчтобыдобавлятькакие-точастишаблона,взависимостиотналичияпеременныхвсловаресданными.
Конструкцияifтакжедолжнанаходитьсявнутри{%%}.Инужноявноуказыватьокончаниеусловия:
{%ifospf%}
routerospf1
router-id10.0.0.{{id}}
auto-costreference-bandwidth10000
{%endif%}
Примершаблонаtemplates/if.txt:
hostname{{name}}
interfaceLoopback0
ipaddress10.0.0.{{id}}255.255.255.255
{%forvlan,nameinvlans.iteritems()%}
vlan{{vlan}}
name{{name}}
{%endfor%}
{%ifospf%}
routerospf1
router-id10.0.0.{{id}}
auto-costreference-bandwidth10000
{%fornetworksinospf%}
network{{networks.network}}area{{networks.area}}
{%endfor%}
{%endif%}
Выражениеifospfработаеттакже,каквPython:еслипеременнаясуществуетинепустая,результатбудетTrue.Еслипеременнойнет,илионапустая,результатбудетFalse.
Тоесть,вэтомшаблонеконфигурацияOSPFгенерируетсятольковтомслучае,еслипеременнаяospfсуществуетинепустая.
Конфигурациябудетгенерироватьсясдвумявариантамиданных.
if/elif/else
397
Сначала,сфайломdata_files/if.yml,вкоторомнетпеременнойospf:
id:3
name:R3
vlans:
10:Marketing
20:Voice
30:Management
Результатбудеттаким:
$pythoncfg_gen.pytemplates/if.txtdata_files/if.yml
hostnameR3
interfaceLoopback0
ipaddress10.0.0.3255.255.255.255
vlan10
nameMarketing
vlan20
nameVoice
vlan30
nameManagement
Теперьаналогичныйшаблон,носфайломdata_files/if_ospf.yml:
id:3
name:R3
vlans:
10:Marketing
20:Voice
30:Management
ospf:
-network:10.0.1.00.0.0.255
area:0
-network:10.0.2.00.0.0.255
area:2
-network:10.1.1.00.0.0.255
area:0
Теперьрезультатвыполнениябудеттаким:
if/elif/else
398
hostnameR3
interfaceLoopback0
ipaddress10.0.0.3255.255.255.255
vlan10
nameMarketing
vlan20
nameVoice
vlan30
nameManagement
routerospf1
router-id10.0.0.3
auto-costreference-bandwidth10000
network10.0.1.00.0.0.255area0
network10.0.2.00.0.0.255area2
network10.1.1.00.0.0.255area0
КакивPython,вJinjaможноделатьответвлениявусловии.
Примершаблонаtemplates/if_vlans.txt:
{%forintf,paramsintrunks.iteritems()%}
interface{{intf}}
{%ifparams.action=='add'%}
switchporttrunkallowedvlanadd{{params.vlans}}
{%elifparams.action=='delete'%}
switchporttrunkallowedvlanremove{{params.vlans}}
{%else%}
switchporttrunkallowedvlan{{params.vlans}}
{%endif%}
{%endfor%}
Файлdata_files/if_vlans.ymlсданными:
trunks:
Fa0/1:
action:add
vlans:10,20
Fa0/2:
action:only
vlans:10,30
Fa0/3:
action:delete
vlans:10
if/elif/else
399
Вданномпримере,взависимостиотзначенияпараметраaction,генерируютсяразныекоманды.
Вшаблонеможнобылоиспользоватьитакойвариантобращенияквложеннымсловарям:
{%forintfintrunks%}
interface{{intf}}
{%iftrunks[intf]['action']=='add'%}
switchporttrunkallowedvlanadd{{trunks[intf]['vlans']}}
{%eliftrunks[intf]['action']=='delete'%}
switchporttrunkallowedvlanremove{{trunks[intf]['vlans']}}
{%else%}
switchporttrunkallowedvlan{{trunks[intf]['vlans']}}
{%endif%}
{%endfor%}
Витоге,будетсгенерированатакаяконфигурация:
$pythoncfg_gen.pytemplates/if_vlans.txtdata_files/if_vlans.yml
interfaceFa0/1
switchporttrunkallowedvlanadd10,20
interfaceFa0/3
switchporttrunkallowedvlanremove10
interfaceFa0/2
switchporttrunkallowedvlan10,30
Также,спомощьюif,можнофильтроватьпокакимэлементампоследовательностипройдетсяциклfor.
Примершаблонаtemplates/if_for.txtсфильтром,вциклеfor:
{%forvlan,nameinvlans.iteritems()ifvlan>15%}
vlan{{vlan}}
name{{name}}
{%endfor%}
Файлсданными(data_files/if_for.yml):
vlans:
10:Marketing
20:Voice
30:Management
Результатвыполнения:
if/elif/else
400
$pythoncfg_gen.pytemplates/if_for.txtdata_files/if_for.yml
vlan20
nameVoice
vlan30
nameManagement
if/elif/else
401
ФильтрыВJinjaпеременныеможноизменятьспомощьюфильтров.Фильтрыотделяютсяотпеременнойвертикальнойчертой(pipe|)имогутсодержатьдополнительныеаргументы.
Крометого,кпеременноймогутбытьпримененынесколькофильтров.Втакомслучае,фильтрыпростопишутсяпоследовательно,икаждыйизнихотделенвертикальнойчертой.
Jinjaподдерживаетбольшоеколичествовстроенныхфильтров.Мырассмотримлишьнесколькоизних.Остальныефильтрыможнонайтивдокументации.
Также,достаточнолегко,можносоздаватьисвоисобственныефильтры.Мынебудемрассматриватьэтувозможность,ноэтохорошоописановдокументации.
default
Фильтрdefaultпозволяетуказатьдляпеременнойзначениепоумолчанию.Еслипеременнаяопределена,будетвыводитьсяпеременная,еслипеременнаянеопределена,будетвыводитьсязначение,котороеуказановфильтреdefault.
Примершаблонаtemplates/filter_default.txt:
routerospf1
auto-costreference-bandwidth{{ref_bw|default(10000)}}
{%fornetworksinospf%}
network{{networks.network}}area{{networks.area}}
{%endfor%}
Еслипеременнаяref_bwопределенавсловаре,будетподставленоеёзначение.Еслижепеременнойнет,будетподставленозначение10000.
Файлсданными(data_files/filter_default.yml):
ospf:
-network:10.0.1.00.0.0.255
area:0
-network:10.0.2.00.0.0.255
area:2
-network:10.1.1.00.0.0.255
area:0
Фильтры
402
Результатвыполнения:
$pythoncfg_gen.pytemplates/filter_default.txtdata_files/filter_default.yml
routerospf1
auto-costreference-bandwidth10000
network10.0.1.00.0.0.255area0
network10.0.2.00.0.0.255area2
network10.1.1.00.0.0.255area0
Поумолчанию,еслипеременнаяопределенаиеёзначениепустойобъект,будетсчитаться,чтопеременнаяиеёзначениеесть.
Еслинужносделатьтак,чтобызначениепоумолчаниюподставлялосьивтомслучае,когдапеременнаяпустая(тоесть,обрабатываетсякакFalseвPython),надоуказатьдополнительныйпараметрboolean=true.
Например,еслифайлданныхбылбытаким:
ref_bw:''
ospf:
-network:10.0.1.00.0.0.255
area:0
-network:10.0.2.00.0.0.255
area:2
-network:10.1.1.00.0.0.255
area:0
Товитогесгенерировалсятакойрезультат:
$pythoncfg_gen.pytemplates/filter_default.txtdata_files/filter_default.yml
routerospf1
auto-costreference-bandwidth
network10.0.1.00.0.0.255area0
network10.0.2.00.0.0.255area2
network10.1.1.00.0.0.255area0
Еслиже,притакомжефайледанных,изменитьшаблонтакимобразом:
routerospf1
auto-costreference-bandwidth{{ref_bw|default(10000,boolean=true)}}
{%fornetworksinospf%}
network{{networks.network}}area{{networks.area}}
{%endfor%}
Вместоdefault(10000,boolean=true),можнонаписатьdefault(10000,true)
Фильтры
403
Результатужебудеттаким(значениепоумолчаниюподставится):
$pythoncfg_gen.pytemplates/filter_default.txtdata_files/filter_default.yml
routerospf1
auto-costreference-bandwidth10000
network10.0.1.00.0.0.255area0
network10.0.2.00.0.0.255area2
network10.1.1.00.0.0.255area0
dictsort
Фильтрdictsortпозволяетсортироватьсловарь.Поумолчанию,сортировкавыполняетсяпоключам.Но,изменивпараметрыфильтра,можновыполнятьсортировкупозначениям.
Синтаксисфильтра:
dictsort(value,case_sensitive=False,by='key')
Послетого,какdictsortотсортировалсловарь,онвозвращаетсписоккортежей,анесловарь.
Примершаблонаtemplates/filter_dictsort.txtсиспользованиемфильтраdictsort:
{%forintf,paramsintrunks|dictsort%}
interface{{intf}}
{%ifparams.action=='add'%}
switchporttrunkallowedvlanadd{{params.vlans}}
{%elifparams.action=='delete'%}
switchporttrunkallowedvlanremove{{params.vlans}}
{%else%}
switchporttrunkallowedvlan{{params.vlans}}
{%endif%}
{%endfor%}
Обратитевнимание,чтофильтрожидаетсловарь,анесписоккортежейилиитератор.
Файлсданными(data_files/filter_dictsort.yml):
Фильтры
404
trunks:
Fa0/1:
action:add
vlans:10,20
Fa0/2:
action:only
vlans:10,30
Fa0/3:
action:delete
vlans:10
Результатвыполнениябудеттаким(интерфейсыупорядочены):
$pythoncfg_gen.pytemplates/filter_dictsort.txtdata_files/filter_dictsort.yml
interfaceFa0/1
switchporttrunkallowedvlanadd10,20
interfaceFa0/2
switchporttrunkallowedvlan10,30
interfaceFa0/3
switchporttrunkallowedvlanremove10
join
Фильтрjoinработаеттакже,какиметодjoinвPython.
Спомощьюфильтраjoinможнообъединятьэлементыпоследовательностивстроку,сопциональнымразделителеммеждуэлементами.
Примершаблонаtemplates/filter_join.txtсиспользованиемфильтраjoin:
{%forintf,paramsintrunks|dictsort%}
interface{{intf}}
{%ifparams.action=='add'%}
switchporttrunkallowedvlanadd{{params.vlans|join(',')}}
{%elifparams.action=='delete'%}
switchporttrunkallowedvlanremove{{params.vlans|join(',')}}
{%else%}
switchporttrunkallowedvlan{{params.vlans|join(',')}}
{%endif%}
{%endfor%}
Файлсданными(data_files/filter_join.yml):
Фильтры
405
trunks:
Fa0/1:
action:add
vlans:
-10
-20
Fa0/2:
action:only
vlans:
-10
-30
Fa0/3:
action:delete
vlans:
-10
Результатвыполнения:
$pythoncfg_gen.pytemplates/filter_join.txtdata_files/filter_join.yml
interfaceFa0/1
switchporttrunkallowedvlanadd10,20
interfaceFa0/2
switchporttrunkallowedvlan10,30
interfaceFa0/3
switchporttrunkallowedvlanremove10
Фильтры
406
ТестыКромефильтров,Jinjaтакжеподдерживаеттесты.Тестыпозволяютпроверятьпеременныенакакое-тоусловие.
Jinjaподдерживаетбольшоеколичествовстроенныхтестов.Мырассмотримлишьнесколькоизних.Остальныетестывыможетенайтивдокументации.
Тесты,какифильтры,можносоздаватьсамостоятельно.
defined
Тестdefinedпозволяетпроверитьестьлипеременнаявсловареданных.
Примершаблонаtemplates/test_defined.txt:
routerospf1
{%ifref_bwisdefined%}
auto-costreference-bandwidth{{ref_bw}}
{%else%}
auto-costreference-bandwidth10000
{%endif%}
{%fornetworksinospf%}
network{{networks.network}}area{{networks.area}}
{%endfor%}
Этотпримерболеегромоздкий,чемвариантсиспользованиемфильтраdefault,ноэтоттестможетбытьполезенвтомслучае,если,взависимостиоттого,определенапеременнаяилинет,нужновыполнятьразныекоманды.
Файлсданными(data_files/test_defined.yml):
ospf:
-network:10.0.1.00.0.0.255
area:0
-network:10.0.2.00.0.0.255
area:2
-network:10.1.1.00.0.0.255
area:0
Результатвыполнения:
Тесты
407
$pythoncfg_gen.pytemplates/test_defined.txtdata_files/test_defined.yml
routerospf1
auto-costreference-bandwidth10000
network10.0.1.00.0.0.255area0
network10.0.2.00.0.0.255area2
network10.1.1.00.0.0.255area0
iterable
Тестiterableпроверяетявляетсялиобъектитератором.
Благодарятакимпроверкам,можноделатьответвлениявшаблоне,которыебудутучитыватьтиппеременной.
Шаблонtemplates/test_iterable.txt(сделаныотступы,чтобыбылипонятнейответвления):
{%forintf,paramsintrunks|dictsort%}
interface{{intf}}
{%ifparams.vlansisiterable%}
{%ifparams.action=='add'%}
switchporttrunkallowedvlanadd{{params.vlans|join(',')}}
{%elifparams.action=='delete'%}
switchporttrunkallowedvlanremove{{params.vlans|join(',')}}
{%else%}
switchporttrunkallowedvlan{{params.vlans|join(',')}}
{%endif%}
{%else%}
{%ifparams.action=='add'%}
switchporttrunkallowedvlanadd{{params.vlans}}
{%elifparams.action=='delete'%}
switchporttrunkallowedvlanremove{{params.vlans}}
{%else%}
switchporttrunkallowedvlan{{params.vlans}}
{%endif%}
{%endif%}
{%endfor%}
Файлсданными(data_files/test_iterable.yml):
Тесты
408
trunks:
Fa0/1:
action:add
vlans:
-10
-20
Fa0/2:
action:only
vlans:
-10
-30
Fa0/3:
action:delete
vlans:10
Обратитевниманиенапоследнююстроку:vlans:10.Вданномслучае,10ужененаходитсявспискеифильтрjoinвтакомслучаенеработает.Но,засчеттестаisiterable(вэтомслучаерезультатбудетfalse),вэтомслучаешаблонуходитвветкуelse.
Результатвыполнения:
$pythoncfg_gen.pytemplates/test_iterable.txtdata_files/test_iterable.yml
interfaceFa0/1
switchporttrunkallowedvlanadd10,20
interfaceFa0/2
switchporttrunkallowedvlan10,30
interfaceFa0/3
switchporttrunkallowedvlanremove10
Такиеотступыполучилисьиз-затого,чтовшаблонеиспользуютсяотступы,нонеустановленоlstrip_blocks=True(онудалетпробелыитабывначалестроки).
Тесты
409
setВнутришаблонаможноприсваиватьзначенияпеременным.Этомогутбытьновыепеременные,амогутбытьизмененныезначенияпеременных,которыебылипереданышаблону.
Такимобразомможнозапомнитьзначение,которое,например,былополученоврезультатеприменениянесколькихфильтров.Ивдальнейшемиспользоватьимяпеременной,анеповторятьсновавсефильтры.
Примершаблонаtemplates/set.txt,вкоторомвыражениеsetиспользуетсячтобызадатьболеекороткиеименапараметрам:
{%forintf,paramsintrunks|dictsort%}
{%setvlans=params.vlans%}
{%setaction=params.action%}
interface{{intf}}
{%ifvlansisiterable%}
{%ifaction=='add'%}
switchporttrunkallowedvlanadd{{vlans|join(',')}}
{%elifaction=='delete'%}
switchporttrunkallowedvlanremove{{vlans|join(',')}}
{%else%}
switchporttrunkallowedvlan{{vlans|join(',')}}
{%endif%}
{%else%}
{%ifaction=='add'%}
switchporttrunkallowedvlanadd{{vlans}}
{%elifaction=='delete'%}
switchporttrunkallowedvlanremove{{vlans}}
{%else%}
switchporttrunkallowedvlan{{vlans}}
{%endif%}
{%endif%}
{%endfor%}
Обратитевниманиенавторуюитретьюстроки:
{%setvlans=params.vlans%}
{%setaction=params.action%}
Такимобразомсозданыновыепеременныеидальшеиспользуютсяужеэтиновыезначения.Такшаблонвыглядитпонятней.
Присваивание(set)
410
Файлсданными(data_files/set.yml):
trunks:
Fa0/1:
action:add
vlans:
-10
-20
Fa0/2:
action:only
vlans:
-10
-30
Fa0/3:
action:delete
vlans:10
Результатвыполнения:
$pythoncfg_gen.pytemplates/set.txtdata_files/set.yml
interfaceFa0/1
switchporttrunkallowedvlanadd10,20
interfaceFa0/2
switchporttrunkallowedvlan10,30
interfaceFa0/3
switchporttrunkallowedvlanremove10
Присваивание(set)
411
includeВыражениеincludeпозволяетдобавитьодиншаблонвдругой.
Переменные,которыепередаютсякакданные,должнысодержатьвседанныеидляосновногошаблона,идлятого,которыйдобавленчерезinclude.
Шаблонtemplates/vlans.txt:
{%forvlan,nameinvlans.items()%}
vlan{{vlan}}
name{{name}}
{%endfor%}
Шаблонtemplates/ospf.txt:
routerospf1
auto-costreference-bandwidth10000
{%fornetworksinospf%}
network{{networks.network}}area{{networks.area}}
{%endfor%}
Шаблонtemplates/bgp.txt:
routerbgp{{bgp.local_as}}
{%foribgpinbgp.ibgp_neighbors%}
neighbor{{ibgp}}remote-as{{bgp.local_as}}
neighbor{{ibgp}}update-source{{bgp.loopback}}
{%endfor%}
{%forebgpinbgp.ebgp_neighbors%}
neighbor{{ebgp}}remote-as{{bgp.ebgp_neighbors[ebgp]}}
{%endfor%}
Шаблонtemplates/switch.txtиспользуетсозданныешаблоныospfиvlans:
{%include'vlans.txt'%}
{%include'ospf.txt'%}
Файлсданными,длягенерацииконфигурации(data_files/switch.yml):
Include
412
vlans:
10:Marketing
20:Voice
30:Management
ospf:
-network:10.0.1.00.0.0.255
area:0
-network:10.0.2.00.0.0.255
area:2
-network:10.1.1.00.0.0.255
area:0
Результатвыполненияскрипта:
$pythoncfg_gen.pytemplates/switch.txtdata_files/switch.yml
vlan10
nameMarketing
vlan20
nameVoice
vlan30
nameManagement
routerospf1
auto-costreference-bandwidth10000
network10.0.1.00.0.0.255area0
network10.0.2.00.0.0.255area2
network10.1.1.00.0.0.255area0
Итоговаяконфигурацияполучиласьтакой,как-будтострокиизшаблоновospf.txtиvlans.txt,находилисьвшаблонеswitch.txt.
Шаблонtemplates/router.txt:
{%include'ospf.txt'%}
{%include'bgp.txt'%}
logging{{log_server}}
Вданномслучае,кромеinclude,добавленаещёоднастрокавшаблон,чтобыпоказать,чтовыраженияincludeмогутидтивперемешкусобычнымшаблоном.
Файлсданными(data_files/router.yml):
Include
413
ospf:
-network:10.0.1.00.0.0.255
area:0
-network:10.0.2.00.0.0.255
area:2
-network:10.1.1.00.0.0.255
area:0
bgp:
local_as:100
loopback:lo100
ibgp_neighbors:
-10.0.0.2
-10.0.0.3
ebgp_neighbors:
90.1.1.1:500
80.1.1.1:600
log_server:10.1.1.1
Результатвыполненияскриптабудеттаким:
$pythoncfg_gen.pytemplates/router.txtdata_files/router.yml
routerospf1
auto-costreference-bandwidth10000
network10.0.1.00.0.0.255area0
network10.0.2.00.0.0.255area2
network10.1.1.00.0.0.255area0
routerbgp100
neighbor10.0.0.2remote-as100
neighbor10.0.0.2update-sourcelo100
neighbor10.0.0.3remote-as100
neighbor10.0.0.3update-sourcelo100
neighbor90.1.1.1remote-as500
neighbor80.1.1.1remote-as600
logging10.1.1.1
Благодаряinclude,шаблонtemplates/ospf.txtиспользуетсяившаблонеtemplates/switch.txt,ившаблонеtemplates/router.txt,вместотого,чтобыповторятьодноитожедважды.
Include
414
НаследованиешаблоновНаследованиешаблоновэтооченьмощныйфункционал,которыйпозволяетизбежатьповторенияодногоитогожевразныхшаблонах.
Прииспользованиинаследованияразличают:
базовыйшаблон-этошаблон,вкоторомописываетсякаркасшаблона.вэтомшаблонемогутнаходитсялюбыеобычныевыраженияилитекст.Но,крометого,вэтомшаблонеопределяютсяспециальныеблоки(block).
дочернийшаблон-шаблон,которыйрасширяетбазовыйшаблон,заполняяобозначенныеблоки.
дочерниешаблонымогутпереписыватьилидополнятьблоки,определенныевбазовомшаблоне.
Примербазовогошаблонаtemplates/base_router.txt:
Наследованиешаблонов
415
!
{%blockservices%}
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
{%endblock%}
!
noipdomainlookup
!
ipsshversion2
!
{%blockospf%}
routerospf1
auto-costreference-bandwidth10000
{%endblock%}
!
{%blockbgp%}
{%endblock%}
!
{%blockalias%}
{%endblock%}
!
linecon0
loggingsynchronous
historysize100
linevty04
loggingsynchronous
historysize100
transportinputssh
!
Обратитевниманиеначетыреблока,которыесозданывшаблоне:
Наследованиешаблонов
416
{%blockservices%}
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
{%endblock%}
!
{%blockospf%}
routerospf1
auto-costreference-bandwidth10000
{%endblock%}
!
{%blockbgp%}
{%endblock%}
!
{%blockalias%}
{%endblock%}
Этозаготовкидлясоответствующихразделовконфигурации.Дочернийшаблон,которыйбудетиспользоватьэтотбазовыйшаблонкакоснову,можетзаполнятьвсеблокиилитолькокакие-тоизних.
Дочернийшаблонtemplates/hq_router.txt:
{%extends"base_router.txt"%}
{%blockospf%}
{{super()}}
{%fornetworksinospf%}
network{{networks.network}}area{{networks.area}}
{%endfor%}
{%endblock%}
{%blockalias%}
aliasconfigureshdosh
aliasexecospfshrun|s^routerospf
aliasexecbrishowipintbri|excunass
aliasexecidshowintdesc
aliasexectopshproccpusorted|excl0.00%__0.00%__0.00%
aliasexeccconft
aliasexecdiffsharchiveconfigdifferencesnvram:startup-configsystem:running-conf
ig
aliasexecdescshintdesc|exdown
{%endblock%}
Перваястрокавшаблонеtemplates/hq_router.txtоченьважна:
{%extends"base_router.txt"%}
Наследованиешаблонов
417
Именноонаговоритотом,чтошаблонhq_router.txtбудетпостроеннаосновешаблонаbase_router.txt.
Внутридочернегошаблонавсёпроисходитвнутриблоков.Засчетблоков,которыебылиопределенывбазовомшаблоне,дочернийшаблонможетрасширятьродительскийшаблон.
Обратитевнимание,чтотестроки,которыеописанывдочернемшаблонезапределамиблоков,игнорируются.
Вбазовомшаблонечетыреблока:services,ospf,bgp,alias.Вдочернемшаблонезаполненытолькодваизних:ospfиalias.
Вэтомудобствонаследования.Необязательнозаполнятьвсеблокивкаждомдочернемшаблоне.
Приэтом,блокиospfиaliasиспользуютсяпо-разному.Вбазовомшаблоне,вблокеospfужебылачастьконфигурации:
{%blockospf%}
routerospf1
auto-costreference-bandwidth10000
{%endblock%}
Поэтому,вдочернемшаблонеестьвыбор:использоватьэтуконфигурациюидополнитьеё,илиполностьюпереписатьвсёвдочернемшаблоне.
Вданномслучае,конфигурациядополняется.Именнопоэтомувдочернемшаблонеtemplates/hq_router.txtблокospfначинаетсясвыражения{{super()}}:
{%blockospf%}
{{super()}}
{%fornetworksinospf%}
network{{networks.network}}area{{networks.area}}
{%endfor%}
{%endblock%}
{{super()}}переноситвдочернийшаблонсодержимоеэтогоблокаизродительскогошаблона.Засчетэтого,вдочернийшаблонперенесутсястрокиизродительского.
Выражениеsuperнеобязательнодолжнонаходитсявсамомначалеблока.Ономожетбытьвлюбомместеблока.Содержимоебазовогошаблона,перенесетсявтоместо,гденаходитсявыражениеsuper.
Наследованиешаблонов
418
Вблокеaliasпростоописанынужныеalias.И,дажееслибывродительскомшаблонебыликакие-тонастройки,онибылибызатертысодержимымдочернегошаблона.
Подытожимправилаработысблоками.Есливродительскомшаблонесозданблок:
безсодержимого-вдочернемшаблонеможнозаполнитьэтотблокилиигнорировать.Еслиблокзаполнен,внембудеттолькото,чтобылонаписановдочернемшаблоне(пример-блокalias)ссодержимым,товдочернемшаблонеможновыполнитьтакиедействия:
игнорироватьблок-втакомслучаевдочернийшаблонпопадетсодержимое,котороенаходилосьвэтомблокевродительскомшаблоне(пример-блокservices)переписатьблок-тогдавдочернемшаблонебудеттолькото,чтоуказановнемперенестисодержимоеблокаизродительскогошаблонаидополнитьего-тогдавдочернемшаблонебудетисодержимоеблокаизродительскогошаблонаисодержимоеиздочернегошаблона.Дляпереносасодержимогоизродительскогошаблонаиспользуетсявыражение{{super()}}(пример-блокospf)
Файлсданнымидлягенерацииконфигурациипошаблону(data_files/hq_router.yml):
ospf:
-network:10.0.1.00.0.0.255
area:0
-network:10.0.2.00.0.0.255
area:2
-network:10.1.1.00.0.0.255
area:0
Результатвыполнениябудеттаким:
Наследованиешаблонов
419
$pythoncfg_gen.pytemplates/hq_router.txtdata_files/hq_router.yml
!
servicetimestampsdebugdatetimemseclocaltimeshow-timezoneyear
servicetimestampslogdatetimemseclocaltimeshow-timezoneyear
servicepassword-encryption
servicesequence-numbers
!
noipdomainlookup
!
ipsshversion2
!
routerospf1
auto-costreference-bandwidth10000
network10.0.1.00.0.0.255area0
network10.0.2.00.0.0.255area2
network10.1.1.00.0.0.255area0
!
!
aliasconfigureshdosh
aliasexecospfshrun|s^routerospf
aliasexecbrishowipintbri|excunass
aliasexecidshowintdesc
aliasexectopshproccpusorted|excl0.00%__0.00%__0.00%
aliasexeccconft
aliasexecdiffsharchiveconfigdifferencesnvram:startup-configsystem:running-conf
ig
aliasexecdescshintdesc|exdown
!
linecon0
loggingsynchronous
historysize100
linevty04
loggingsynchronous
historysize100
transportinputssh
!
Обратитевнимание,чтовблокеospfестьикомандыизбазовогошаблона,икомандыиздочернегошаблона.
Наследованиешаблонов
420
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание13.1
Переделатьскриптcfg_gen.pyвфункциюgenerate_cfg_from_template.
Функцияожидаетдвааргумента:
путькшаблонуфайлспеременнымивформатеYAML
Функциядолжнавозвращатьконфигурацию,котораябыласгенерирована.
Проверитьработуфункциинашаблонеtemplates/for.txtиданныхdata_files/for.yml.
Задания
421
fromjinja2importEnvironment,FileSystemLoader
importyaml
importsys
reload(sys)
sys.setdefaultencoding('utf-8')
TEMPLATE_DIR,template=sys.argv[1].split('/')
VARS_FILE=sys.argv[2]
env=Environment(loader=FileSystemLoader(TEMPLATE_DIR),trim_blocks=True)
template=env.get_template(template)
vars_dict=yaml.load(open(VARS_FILE))
printtemplate.render(vars_dict)
Задание13.1a
Переделатьфункциюgenerate_cfg_from_template:
добавитьподдержкуиспользованияшаблона,которыйнаходитсявтекущемкаталоге
Дляпроверки,скопируйтеодинизшаблоновизкаталогаtemplates,втекущийкаталогскрипта.
Можнопроверитьнатехжешаблонеиданных,чтоивпрошломзадании:
шаблонеtemplates/for.txt(носкопироватьеговтекущийкаталог)иданныхdata_files/for.yml
Задание13.1b
Дополнитьфункциюgenerate_cfg_from_templateиззадания13.1или13.1a:
добавитьподдержкуаргументовокружения(Environment)
Функцияgenerate_cfg_from_templateдолжнаприниматьлюбыеаргументы,которыепринимаетклассEnvironmentипростопередаватьихему.
Проверитьфункциональностьнааргументах:
trim_blockslstrip_blocks
Задание13.1c
Задания
422
Дополнитьфункциюgenerate_cfg_from_templateиззадания13.1,13.1aили13.1b:
добавитьподдержкуразныхформатовдляфайласданными
Должныподдерживатьсятакиеформаты:
YAMLJSONсловарьPython
Сделатьдлякаждогоформатасвойпараметрфункции.Например:
YAML-yaml_fileJSON-json_fileсловарьPython-py_dict
Проверитьработуфункциинашаблонеtemplates/for.txtиданных:
data_files/for.ymldata_files/for.jsonсловареdata_dict
data_dict={'vlans':{
10:'Marketing',
20:'Voice',
30:'Management'},
'ospf':[{'network':'10.0.1.00.0.0.255','area':0},
{'network':'10.0.2.00.0.0.255','area':2},
{'network':'10.1.1.00.0.0.255','area':0}],
'id':3,
'name':'R3'}
Задание13.1d
Переделатьфункциюgenerate_cfg_from_templateиззадания13.1,13.1a,13.1bили13.1c:
сделатьавтоматическоераспознаваниеразныхформатовдляфайласданнымидляпередачиразныхтиповданных,должениспользоватьсяодинитотжепараметрdata
Должныподдерживатьсятакиеформаты:
YAML-файлысрасширениемymlилиyamlJSON-файлысрасширениемjsonсловарьPython
Задания
423
Еслинеполучилосьопределитьтипданных,вывестисообщениеerror_message(перенеститекстсообщениявтелофункции),завершитьработуфункцииивернутьNone.
Проверитьработуфункциинашаблонеtemplates/for.txtиданных:
data_files/for.ymldata_files/for.jsonсловареdata_dict
error_message="""
Неполучилосьопределитьформатданных.
Поддерживаютсяфайлысрасширением.json,.yml,.yamlисловариPython
"""
data_dict={'vlans':{
10:'Marketing',
20:'Voice',
30:'Management'},
'ospf':[{'network':'10.0.1.00.0.0.255','area':0},
{'network':'10.0.2.00.0.0.255','area':2},
{'network':'10.1.1.00.0.0.255','area':0}],
'id':3,
'name':'R3'}
Задание13.2
Наосновеконфигурацииconfig_r1.txt,создатьшаблоны:
templates/cisco_base.txt-внемдолжныбытьвсестроки,кроменастройкиaliasиeventmanager
имяхостадолжнобытьпеременнойhostnametemplates/alias.txt-вэтотшаблонперенестивсеaliastemplates/eem_int_desc.txt-вэтомшаблонедолженбытьeventmanagerapplet
Вшаблонахtemplates/alias.txtиtemplates/eem_int_desc.txtпеременныхнет.
Создатьшаблонtemplates/cisco_router_base.txt.Вшаблондолжнобытьвключеносодержимоешаблонов:
templates/cisco_base.txttemplates/alias.txttemplates/eem_int_desc.txt
Приэтом,нельзякопироватьтекстшаблонов.
Задания
424
Проверьтешаблонtemplates/cisco_router_base.txt,спомощьюфункцииgenerate_cfg_from_templateиззадания13.1-13.1d.Некопируйтекодфункции.
Вкачестведанных,используйтесловарьrouter_info
router_info={'hostname':'R1'}
Задание13.3
Создайтешаблонtemplates/ospf.txtнаосновеконфигурацииOSPFвфайлеcisco_ospf.txt.Примерконфигурациидан,чтобынапомнитьсинтаксис.
Какиезначениядолжныбытьпеременными:
номерпроцесса.Имяпеременной-processrouter-id.Имяпеременной-router_idreference-bandwidth.Имяпеременной-ref_bwинтерфейсы,накоторыхнужновключитьOSPF.Имяпеременной-ospf_intf
наместеэтойпеременнойожидаетсясписоксловарейстакимиключами:name-имяинтерфейса,видаFa0/1,VLan10,Gi0/0ip-IP-адресинтерфейса,вида10.0.1.1area-номерзоныpassive-являетсялиинтерфейспассивным.Допустимыезначения:TrueилиFalse
Длявсехинтерфейсоввспискеospf_intf,надосгенерироватьстроки:
networkx.x.x.x0.0.0.0areax
Еслиинтерфейспассивный,длянегодолжнабытьдобавленастрока:
passive-interfacex
Дляинтерфейсов,которыенеявляютсяпассивными,врежимеконфигурацииинтерфейса,надодобавитьстроку:
ipospfhello-interval1
Всекомандыдолжныбытьвсоответствующихрежимах.
Задания
425
Проверьтеполучившийсяшаблонtemplates/ospf.txt,наданныхвфайлеdata_files/ospf.yml,спомощьюфункцииgenerate_cfg_from_templateиззадания13.1-13.1d.Некопируйтекодфункции.
Задание13.3a
Изменитешаблонtemplates/ospf.txtтакимобразом,чтобыдляперечисленныхпеременныхбылиуказанызначенияпоумолчанию,которыеиспользуютсявтомслучае,еслипеременнаянезадана.
Неиспользоватьдляэтоговыраженияif/else.
Задатьвшаблонезначенияпоумолчаниюдлятакихпеременных:
process-значениепоумолчанию1ref_bw-значениепоумолчанию10000
Проверьтеполучившийсяшаблонtemplates/ospf.txt,наданныхвфайлеdata_files/ospf2.yml,спомощьюфункцииgenerate_cfg_from_templateиззадания13.1-13.1d.Некопируйтекодфункции.
Задание13.3b
Изменитешаблонtemplates/ospf.txtиззадания13.3aтакимобразом,чтобыдляперечисленныхпеременныхбылиуказанызначенияпоумолчанию,которыеиспользуютсявтомслучае,еслипеременнаянезаданаили,есливпеременнойпустоезначение.
Неиспользоватьдляэтоговыраженияif/else.
Задатьвшаблонезначенияпоумолчаниюдлятакихпеременных:
process-значениепоумолчанию1ref_bw-значениепоумолчанию10000
Проверьтеполучившийсяшаблонtemplates/ospf.txt,наданныхвфайлеdata_files/ospf3.yml,спомощьюфункцииgenerate_cfg_from_templateиззадания13.1-13.1d.Некопируйтекодфункции.
Задание13.4
Создайтешаблонtemplates/add_vlan_to_switch.txt,которыйбудетиспользоватьсяпринеобходимостидобавитьVLANнакоммутатор.
Вшаблонедолжныподдерживатьсявозможности:
Задания
426
добавленияVLANиимениVLANдобавленияVLANкакaccess,науказанноминтерфейседобавленияVLANвсписокразрешенных,науказанныетранки
ЕслиVLANнеобходимодобавитькакaccess,тонадонастроитьирежиминтерфейсаидобавитьеговVLAN:
interfaceGi0/1
switchportmodeaccess
switchportaccessvlan5
Длятранков,необходимотолькодобавитьVLANвсписокразрешенных:
interfaceGi0/10
switchporttrunkallowedvlanadd5
Именапеременныхнадовыбратьнаоснованиипримераданных,вфайлеdata_files/add_vlan_to_switch.yaml.
Проверьтешаблонtemplates/add_vlan_to_switch.txtнаданныхвфайлеdata_files/add_vlan_to_switch.yaml,спомощьюфункцииgenerate_cfg_from_templateиззадания13.1-13.1d.Некопируйтекодфункции.
Задания
427
ОбработкавыводакомандсTextFSMНаоборудовании,котороенеподдерживаеткакого-топрограммногоинтерфейса,выводкомандshowвозвращаетсяввидестроки.И,хотяотчастионаструктурирована,новсёжеэтопростострока.Иеёнадокак-тообработать,чтобыполучитьобъектыPython,например,словарьилисписок.
Например,можнопострочнообрабатыватьвыводкомандыииспользуя,например,регулярныевыражения,получитьобъектыPython.Ноестьболееудобныйвариант,чемпростообрабатыватькаждыйвыводпострочно:TextFSM.
TextFSMэтобиблиотекасозданнаяGoogleдляобработкивыводассетевыхустройств.Онапозволяетсоздаватьшаблоны,покоторымбудетобрабатыватьсявыводкоманды.
ИспользованиеTextFSMлучше,чемпростаяпострочнаяобработка,таккакшаблоныдаютлучшеепредставлениеотом,каквыводбудетобрабатыватьсяишаблонамипрощеподелиться.Азначит,прощенайтиужесозданныешаблоныииспользоватьих.Илиподелитьсясвоими.
Дляначала,библиотекунадоустановить:
pipinstallgtextfsm
ДляиспользованияTextFSM,надосоздатьшаблон,покоторомубудетобрабатыватьсявыводкоманды.
Примервыводакомандыtraceroute:
r2#traceroute90.0.0.9source33.0.0.2
traceroute90.0.0.9source33.0.0.2
Typeescapesequencetoabort.
Tracingtherouteto90.0.0.9
VRFinfo:(vrfinname/id,vrfoutname/id)
110.0.12.11msec0msec0msec
215.0.0.50msec5msec4msec
357.0.0.74msec1msec4msec
479.0.0.94msec*1msec
Например,извыводанадополучитьхопы,черезкоторыепрошелпакет.
Втакомслучае,шаблонTextFSMбудетвыглядетьтак(файлtraceroute.template):
14.TextFSM.Обработкавыводакоманд
428
ValueID(\d+)
ValueHop(\d+(\.\d+){3})
Start
^${ID}${Hop}->Record
Первыедвестрокиопределяютпеременные:
ValueID(\d+)-этастрокаопределяетпеременнуюID,котораяописываетрегулярноевыражение:(\d+)-однаилиболеецифр
сюдапопадутномерахоповValueHop(\d+(\.\d+){3})-этастрокаопределяетпеременнуюHop,котораяописываетIP-адрестакимрегулярнымвыражением:(\d+(\.\d+){3})
ПослестрокиStartначинаетсясамшаблон.Вданномслучае,оноченьпростой:
${ID}${Hop}->Record
сначалаидетсимволначаластроки,затемдвапробелаипеременныеIDиHopвTextFSMпеременныеописываютсятакимобразом:${имяпеременной}словоRecordвконцеозначает,чтостроки,которыепопадутподописанныйшаблон,будутобработаныивыведеныврезультатыTextFSM(сэтимподробнеемыразберемсявследующемразделе)
СкриптдляобработкивыводакомандыtracerouteспомощьюTextFSM(parse_traceroute.py):
importtextfsm
traceroute="""
r2#traceroute90.0.0.9source33.0.0.2
traceroute90.0.0.9source33.0.0.2
Typeescapesequencetoabort.
Tracingtherouteto90.0.0.9
VRFinfo:(vrfinname/id,vrfoutname/id)
110.0.12.11msec0msec0msec
215.0.0.50msec5msec4msec
357.0.0.74msec1msec4msec
479.0.0.94msec*1msec
"""
template=open('traceroute.textfsm')
fsm=textfsm.TextFSM(template)
result=fsm.ParseText(traceroute)
printfsm.header
printresult
14.TextFSM.Обработкавыводакоманд
429
Результатвыполненияскрипта:
$pythonparse_traceroute.py
['ID','Hop']
[['1','10.0.12.1'],['2','15.0.0.5'],['3','57.0.0.7'],['4','79.0.0.9']]
Строки,которыесовпалисописаннымшаблоном,возвращаютсяввидеспискасписков.Каждыйэлементэтосписок,которыйсостоитиздвухэлементов:номерахопаиIP-адреса.
Разберемсяссодержимымскрипта:
traceroute-этопеременная,котораясодержитвыводкомандыtraceroutetemplate=open('traceroute.textfsm')-содержимоефайласшаблономTextFSMсчитываетсявпеременнуюtemplatefsm=textfsm.TextFSM(template)-класс,которыйобрабатываетшаблонисоздаетизнегообъектвTextFSMresult=fsm.ParseText(traceroute)-метод,которыйобрабатываетпереданныйвыводсогласношаблонуивозращаетсписоксписков,вкоторомкаждыйэлементэтообработаннаястрокаВконцевыводитсязаголовок:printfsm.header,которыйсодержитименапеременныхирезультатобработки
Вэтимвыводомможноработатьдальше.Например,периодическивыполнятькомандуtracerouteисравниватьизменилосьликоличествохоповиихпорядок.
ДляработысTextFSMнужнывыводкомандыишаблон:
дляразныхкоманднужныразныешаблоныTextFSMвозвращаетрезультатобработкивтабличномвиде(ввидеспискасписков)
этотвыводлегкопреобразоватьвcsvформатиливсписоксловарей
14.TextFSM.Обработкавыводакоманд
430
СинтаксисшаблоновTextFSMШаблонTextFSMописываеткакимобразомданныедолжныобрабатываться.
Вэтомразделеописансинтаксисшаблонов,наосноведокументацииTextFSM.Вследующемразделепоказаныпримерыиспользованиясинтаксиса.Поэтому,впринципе,можноперейтисразукследующемуразделу,акэтомувозвращатьсяпонеобходимости,длятехситуаций,длякоторыхнетпримераикогданужноперечитать,чтоозначаеткакой-топараметр.
Любойшаблонсостоитиздвухчастей:
определенияпеременныхэтипеременныеописываюткакиестолбцыбудутвтабличномпредставлении
определениясостояний
Примерразборакомандыtraceroute:
#Определениепеременных:
ValueID(\d+)
ValueHop(\d+(\.\d+){3})
#СекциясопределениемсостоянийвсегдадолжнаначинатьсяссостоянияStart
Start
#Переменныедействие
^${ID}${Hop}->Record
Определениепеременных
Всекцииспеременнымидолжныидтитолькоопределенияпеременных.Единственноеисключение-вэтомразделемогутбытькомментарии.
Вэтомразделенедолжнобытьпустыхстрок.ДляTextFSMпустаястрокаозначаетзавершениесекцииопределенияпеременных.
Форматописанияпеременных:
Value[option[,option...]]nameregex
Синтаксисописанияпеременных(длякаждойопциинижемырассмотримпримеры):
Value-этоключевоеслово,котороеуказывает,чтосоздаетсяпеременная.Егообязательнонужноуказывать
СинтаксисшаблоновTextFSM
431
option-опции,которыеопределяюткакработатьспеременной.Еслинужноуказатьнесколькоопций,онидолжныбытьотделенызапятой,безпробелов.Поддерживаютсятакиеопции:
Filldown-значение,котороеранеесовпалосрегулярнымвыражением,запоминаетсядоследующейобработкистроки(еслинебылоявноочищеноилисновасовпалорегулярноевыражение).
этозначит,чтопоследнеезначениестолбца,котороесовпалосрегулярнымвыражением,запоминаетсяииспользуетсявследующихстроках,есливнихнеприсутствовалэтотстолбец.
Key-определяет,чтоэтополесодержитуникальныйидентификаторстрокиRequired-строка,котораяобрабатывается,будетзаписанатольковтомслучае,еслиэтапеременнаяприсутствует.List-значениеэтосписокикаждоесовпадениесрегулярнымвыражениембудетдобавлятьвсписокэлемент.Поумолчанию,каждоеследующеесовпадениеперезаписываетпредыдущее.Fillup-работаеткакFilldown,нозаполняетпустыезначениевыше,дотехпор,поканенайдетсовпадение.НесовместимосRequired.
name-имяпеременной,котороебудетиспользоватьсякакимяколонки.Зарезервированныеименанедолжныиспользоватьсякакимяпеременной.regex-регулярноевыражение,котороеописываетпеременную.Регулярноевыражениедолжнобытьвскобках.
Определениесостояний
Послеопределенияпеременных,нужноописатьсостояния:
каждоеопределениесостояниядолжнобытьотделенопустойстрокой(какминимум,одной)перваястрока-имясостояниязатемидутстроки,которыеописываютправила
правиладолжныначинатьсяспробелаисимвола
НачальноесостояниевсегдаStart.Входныеданныесравниваютсястекущимсостоянием,новстрокеправиламожетбытьуказано,чтонужноперейтикдругомусостоянию.
Проверкавыполняетсяпострочно,поканебудетдостигнутEOF(конецфайла)илитекущеесостояниеперейдетвсостояниеEnd.
Зарезервированныесостояния
Зарезервированытакиесостояния:
СинтаксисшаблоновTextFSM
432
Start-этосостояниеобязательнодолжнобытьуказано.Безнегошаблоннебудетработать.End-этосостояниезавершаетобработкувходящихстрокиневыполняетсостояниеEOF.EOF-этонеявноесостояние,котороевыполняетсявсегда,когдаобработкадошладоконцафайла.Выглядитонотакимобразом:
EOF
^.*->Record
EOFзаписываеттекущуюстроку,преждечемобработказавершается.Еслиэтоповедениенужноизменить,надоявно,вконцешаблона,написатьEOF:
EOF
ПравиласостоянийКаждоесостояниесостоитизодногоилиболееправил:
TextFSMобрабатываетвходящиестрокиисравниваетихсправиламиеслиправило(регулярноевыражение)совпадаетсострокой,выполняютсядействия,которыеописанывправилеидляследующейстрокипроцессповторяетсязаново,сначаласостояния.
Правиладолжныбытьописанывтакомформате:
^regex[->action]
Вправиле:
каждоеправилодолжноначинатьсяспробелаисимвола
символ означаетначалострокиивсегдадолженуказыватьсяявноregex-эторегулярноевыражение,вкотороммогутиспользоватьсяпеременные
дляуказанияпеременной,можетиспользоватьсясинтаксис$ValueNameили${ValueName}(этотформатпредпочтителен)вправиле,наместопеременныхподставляютсярегулярныевыражения,которыеониописываютеслинужноявноуказатьсимволконцастроки,используетсязначение$$
Действиявправилах
СинтаксисшаблоновTextFSM
433
Послерегулярноговыражения,вправилемогутуказыватьсядействия:
междурегулярнымвыражениемидействием,долженбытьсимвол->действиямогутсостоятьизтрехчастей,втакомформатеL.RS
L-LineAction-действия,которыеприменяютсяквходящейстрокеR-RecordAction-действия,которыеприменяютсяксобраннымзначениямS-StateAction-переходвдругоесостояние
еслинетуказанныхдействий,топоумолчаниюиспользуетсядействиеNext.NoRecord.
LineActions
LineActions:
Next-обработатьстроку,прочитатьследующуюиначатьпроверятьеёсначаласостояния.Этодействиеиспользуетсяпоумолчанию,еслинеуказанодругоеContinue-продолжитьобработкуправил,как-будтосовпадениянебыло,приэтомзначенияприсваиваются
RecordAction
RecordAction-опциональноедействие,котороеможетбытьуказанопослеLineAction.Онидолжныбытьразделеныточкой.Типыдействий:
NoRecord-невыполнятьничего.Этодействиепоумолчанию,когдадругоенеуказаноRecord-запомнитьзначение,которыесовпалисправилом.Всепеременные,крометех,гдеуказанаопцияFilldown,обнуляются.Clear-обнулитьвсепеременные,крометех,гдеуказанаопцияFilldown.Clearall-обнулитьвсепеременные.
Разделятьдействияточкойнужнотольковтомслучае,еслинужноуказатьиLineиRecordдействия.Еслинужноуказатьтолькоодноизних,точкуставитьненужно.
StateTransition
Последействия,можетбытьуказаноновоесостояние:
состояниедолжнобытьоднимиззарезервированныхилисостояниеопределенныхвшаблонеесливходнаястрокасовпала:
вседействиявыполняются,
СинтаксисшаблоновTextFSM
434
считываетсяследующаястрока,затемтекущеесостояниеменяетсянановоеиобработкапродолжаетсявновомсостоянии.
Есливправилеиспользуетсядействие,товнемнельзяиспользоватьпереходвдругоесостояние.Этоправилонужнодлятого,чтобывпоследовательностисостоянийнебылопетель.
ErrorAction
СпециальноедействиеErrorостанавливаетвсюобработкустрок,отбрасываетвсестроки,которыебылисобраныдосихпоривозвращаетисключение.
Синтаксисэтогодействиятакой:
^regex->Error[word|"string"]
СинтаксисшаблоновTextFSM
435
ПримерыиспользованияTextFSMВэтомразделерассматриваютсяпримерышаблоновииспользованияTextFSM.
Дляобработкивыводакомандпошаблону,вразделеиспользуетсяскриптparse_output.py.Оннепривязанкконкретномушаблонуивыводу:шаблонивыводкомандыбудутпередаватьсякакаргументы:
importsys
importtextfsm
fromtabulateimporttabulate
template=sys.argv[1]
output_file=sys.argv[2]
f=open(template)
output=open(output_file).read()
re_table=textfsm.TextFSM(f)
header=re_table.header
result=re_table.ParseText(output)
printtabulate(result,headers=header)
Примерзапускаскрипта:
$pythonparse_output.pytemplatecommand_output
Модульtabulateиспользуетсядляотображенияданныхвтабличномвиде(егонужноустановить,еслихотитеиспользоватьэтотскрипт).Аналогичныйвыводможнобылосделатьиспомощьюформатированиястрок,носtabulate,этосделатьпроще.
Обработкаданныхпошаблонувсегдавыполняетсяодинаково.Поэтомускриптбудетодинаковыйитолькошаблониданныеотличаться.
Начинаяспростогопримера,разберемсястемкакиспользоватьTextFSM.
showclock
Первыйпример-разборвыводакомандыshclock(файлoutput/sh_clock.txt):
ПримерыиспользованияTextFSM
436
15:10:44.867UTCSunNov132016
Дляначала,вшаблоненадоопределитьпеременные:
вначалекаждойстрокидолжнобытьключевоесловоValueкаждаяпеременнаяопределяетстолбецвтаблице
следующееслово-названиепеременнойпосленазвания,вскобках,регулярноевыражение,котороеописываетзначениепеременной
Определениепеременныхвыглядиттак:
ValueTime(..:..:..)
ValueTimezone(\S+)
ValueWeekDay(\w+)
ValueMonth(\w+)
ValueMonthDay(\d+)
ValueYear(\d+)
Подсказкапоспецсимволам:
.-любойсимвол+-одноилиболееповторенийпредыдущегосимвола\S-всесимволы,кромеwhitespace\w-любаябукваилицифра\d-любаяцифра
Послеопределенияпеременных,должнаидтипустаястрокаисостояниеStart,апосле,начинаяспробелаисимвола ,идетправило(файлtemplates/sh_clock.template):
ValueTime(..:..:..)
ValueTimezone(\S+)
ValueWeekDay(\w+)
ValueMonth(\w+)
ValueMonthDay(\d+)
ValueYear(\d+)
Start
^${Time}.*${Timezone}${WeekDay}${Month}${MonthDay}${Year}->Record
ПримерыиспользованияTextFSM
437
Таккак,вданномслучае,ввыводевсегооднастрока,можнонеписатьвшаблонедействиеRecord.Нолучшеегоиспользоватьвситуациях,когданадозаписатьзначения,чтобыпривыкатькэтомусинтаксисуинеошибиться,когданужнаобработканесколькихстрок.
КогдаTextFSMобрабатываетстрокивывода,онподставляетвместопеременных,ихзначения.Витогеправилобудетвыглядетьтак:
^(..:..:..).*(\S+)(\w+)(\w+)(\d+)(\d+)
Когдаэторегулярноевыражениеприменяетсяввыводуshowclock,вкаждойгрупперегулярноговыражения,будетнаходитьсясоответствующеезначение:
1группа:15:10:442группа:UTC3группа:Sun4группа:Nov5группа:136группа:2016
Вправиле,кромеявногодействияRecord,котороеуказывает,чтозаписьнадопоместитьвфинальнуютаблицу,поумолчаниютакжеиспользуетсяправилоNext.Оноуказывает,чтонадоперейтикследующейстрокетекста.Таккакввыводекомандыshclock,толькооднастрока,обработказавершается.
Результатотработкискриптабудеттаким:
$pythonparse_output.pytemplates/sh_clock.templateoutput/sh_clock.txt
TimeTimezoneWeekDayMonthMonthDayYear
--------------------------------------------------
15:10:44UTCSunNov132016
showcdpneighborsdetail
Теперьпопробуемобработатьвыводкомандыshowcdpneighborsdetail.
Особенностьэтойкомандывтом,чтоданныенаходятсяневоднойстроке,авразных.
Вфайлеoutput/sh_cdp_n_det.txtнаходитсявыводкомандыshowcdpneighborsdetail:
SW1#showcdpneighborsdetail
-------------------------
DeviceID:SW2
Entryaddress(es):
IPaddress:10.1.1.2
ПримерыиспользованияTextFSM
438
Platform:ciscoWS-C2960-8TC-L,Capabilities:SwitchIGMP
Interface:GigabitEthernet1/0/16,PortID(outgoingport):GigabitEthernet0/1
Holdtime:164sec
Version:
CiscoIOSSoftware,C2960Software(C2960-LANBASEK9-M),Version12.2(55)SE9,RELEASES
OFTWARE(fc1)
TechnicalSupport:http://www.cisco.com/techsupport
Copyright(c)1986-2014byCiscoSystems,Inc.
CompiledMon03-Mar-1422:53byprod_rel_team
advertisementversion:2
VTPManagementDomain:''
NativeVLAN:1
Duplex:full
Managementaddress(es):
IPaddress:10.1.1.2
-------------------------
DeviceID:R1
Entryaddress(es):
IPaddress:10.1.1.1
Platform:Cisco3825,Capabilities:RouterSwitchIGMP
Interface:GigabitEthernet1/0/22,PortID(outgoingport):GigabitEthernet0/0
Holdtime:156sec
Version:
CiscoIOSSoftware,3800Software(C3825-ADVENTERPRISEK9-M),Version12.4(24)T1,RELEA
SESOFTWARE(fc3)
TechnicalSupport:http://www.cisco.com/techsupport
Copyright(c)1986-2009byCiscoSystems,Inc.
CompiledFri19-Jun-0918:40byprod_rel_team
advertisementversion:2
VTPManagementDomain:''
Duplex:full
Managementaddress(es):
-------------------------
DeviceID:R2
Entryaddress(es):
IPaddress:10.2.2.2
Platform:Cisco2911,Capabilities:RouterSwitchIGMP
Interface:GigabitEthernet1/0/21,PortID(outgoingport):GigabitEthernet0/0
Holdtime:156sec
Version:
CiscoIOSSoftware,2900Software(C3825-ADVENTERPRISEK9-M),Version15.2(2)T1,RELEAS
ESOFTWARE(fc3)
TechnicalSupport:http://www.cisco.com/techsupport
Copyright(c)1986-2009byCiscoSystems,Inc.
CompiledFri19-Jun-0918:40byprod_rel_team
ПримерыиспользованияTextFSM
439
advertisementversion:2
VTPManagementDomain:''
Duplex:full
Managementaddress(es):
Извыводакомандынадополучитьтакиеполя:
LOCAL_HOST-имяустройстваизприглашенияDEST_HOST-имясоседаMGMNT_IP-IP-адрессоседаPLATFORM-модельсоседнегоустройстваLOCAL_PORT-локальныйинтерфейс,которыйсоединенссоседомREMOTE_PORT-портсоседнегоустройстваIOS_VERSION-версияIOSсоседа
Шаблонвыглядиттакимобразом(файлtemplates/sh_cdp_n_det.template):
ValueLOCAL_HOST(\S+)
ValueDEST_HOST(\S+)
ValueMGMNT_IP(.*)
ValuePLATFORM(.*)
ValueLOCAL_PORT(.*)
ValueREMOTE_PORT(.*)
ValueIOS_VERSION(\S+)
Start
^${LOCAL_HOST}[>#].
^DeviceID:${DEST_HOST}
^.*IPaddress:${MGMNT_IP}
^Platform:${PLATFORM},
^Interface:${LOCAL_PORT},PortID\(outgoingport\):${REMOTE_PORT}
^.*Version${IOS_VERSION},
Результатвыполненияскрипта:
$pythonparse_output.pytemplates/sh_cdp_n_det.templateoutput/sh_cdp_n_det.txt
LOCAL_HOSTDEST_HOSTMGMNT_IPPLATFORMLOCAL_PORTREMOTE_PORT
IOS_VERSION
----------------------------------------------------------------------------
-------------------
SW1R210.2.2.2Cisco2911GigabitEthernet1/0/21GigabitEther
net0/015.2(2)T1
Несмотрянато,чтоправиласпеременнымиописанывразныхстроках,и,соответственно,работаютсразнымистроками,TextFSMсобираетихводнустрокутаблицы.Тоесть,переменные,которыеопределенывначалешаблона,задаютстроку
ПримерыиспользованияTextFSM
440
итоговойтаблицы.
Обратитевнимание,чтовфайлеsh_cdp_n_det.txtнаходитсявыводстремясоседями,автаблицетолькоодинсосед,последний.
Record
Такполучилосьиз-затого,чтовшаблоненеуказанодействиеRecord.Ивитоге,вфинальнойтаблицеосталасьтолькопоследняястрока.
Исправленыйшаблон:
ValueLOCAL_HOST(\S+)
ValueDEST_HOST(\S+)
ValueMGMNT_IP(.*)
ValuePLATFORM(.*)
ValueLOCAL_PORT(.*)
ValueREMOTE_PORT(.*)
ValueIOS_VERSION(\S+)
Start
^${LOCAL_HOST}[>#].
^DeviceID:${DEST_HOST}
^.*IPaddress:${MGMNT_IP}
^Platform:${PLATFORM},
^Interface:${LOCAL_PORT},PortID\(outgoingport\):${REMOTE_PORT}
^.*Version${IOS_VERSION},->Record
Теперьрезультатзапускаскриптавыглядиттак:
$pythonparse_output.pytemplates/sh_cdp_n_det.templateoutput/sh_cdp_n_det.txt
LOCAL_HOSTDEST_HOSTMGMNT_IPPLATFORMLOCAL_PORTRE
MOTE_PORTIOS_VERSION
----------------------------------------------------------------------------
-----------------------------
SW1SW210.1.1.2ciscoWS-C2960-8TC-LGigabitEthernet1/0/16Gi
gabitEthernet0/112.2(55)SE9
R110.1.1.1Cisco3825GigabitEthernet1/0/22Gi
gabitEthernet0/012.4(24)T1
R210.2.2.2Cisco2911GigabitEthernet1/0/21Gi
gabitEthernet0/015.2(2)T1
Выводполученсовсехтрёхустройств.Но,переменнаяLOCAL_HOSTотображаетсяневкаждойстроке,атольковпервой.
Filldown
ПримерыиспользованияTextFSM
441
Этосвязаностем,чтоприглашение,изкотороговзятозначениепеременной,появляетсятолькоодинраз.И,длятого,чтобыонопоявлялосьивпоследующихстроках,надоиспользоватьдействиеFilldownдляпеременнойLOCAL_HOST:
ValueFilldownLOCAL_HOST(\S+)
ValueDEST_HOST(\S+)
ValueMGMNT_IP(.*)
ValuePLATFORM(.*)
ValueLOCAL_PORT(.*)
ValueREMOTE_PORT(.*)
ValueIOS_VERSION(\S+)
Start
^${LOCAL_HOST}[>#].
^DeviceID:${DEST_HOST}
^.*IPaddress:${MGMNT_IP}
^Platform:${PLATFORM},
^Interface:${LOCAL_PORT},PortID\(outgoingport\):${REMOTE_PORT}
^.*Version${IOS_VERSION},->Record
Теперьмыполучилитакойвывод:
$pythonparse_output.pytemplates/sh_cdp_n_det.templateoutput/sh_cdp_n_det.txt
LOCAL_HOSTDEST_HOSTMGMNT_IPPLATFORMLOCAL_PORTRE
MOTE_PORTIOS_VERSION
----------------------------------------------------------------------------
-----------------------------
SW1SW210.1.1.2ciscoWS-C2960-8TC-LGigabitEthernet1/0/16Gi
gabitEthernet0/112.2(55)SE9
SW1R110.1.1.1Cisco3825GigabitEthernet1/0/22Gi
gabitEthernet0/012.4(24)T1
SW1R210.2.2.2Cisco2911GigabitEthernet1/0/21Gi
gabitEthernet0/015.2(2)T1
SW1
ТеперьзначениепеременнойLOCAL_HOSTпоявилосьвовсехтрёхстроках.Нопоявилсяещёодинстранныйэффект-последняястрока,вкоторойзаполненатолькоколонкаLOCAL_HOST.
Required
Деловтом,чтовсепеременные,которыемыопределили,опциональны.Ктомуже,однапеременнаяспараметромFilldown.И,чтобыизбавитьсяотпоследнейстроки,нужносделатьхотябыоднупеременнуюобязательной,спомощьюпараметраRequired:
ПримерыиспользованияTextFSM
442
ValueFilldownLOCAL_HOST(\S+)
ValueRequiredDEST_HOST(\S+)
ValueMGMNT_IP(.*)
ValuePLATFORM(.*)
ValueLOCAL_PORT(.*)
ValueREMOTE_PORT(.*)
ValueIOS_VERSION(\S+)
Start
^${LOCAL_HOST}[>#].
^DeviceID:${DEST_HOST}
^.*IPaddress:${MGMNT_IP}
^Platform:${PLATFORM},
^Interface:${LOCAL_PORT},PortID\(outgoingport\):${REMOTE_PORT}
^.*Version${IOS_VERSION},->Record
Теперьмыполучимкорректныйвывод:
$pythonparse_output.pytemplates/sh_cdp_n_det.templateoutput/sh_cdp_n_det.txt
LOCAL_HOSTDEST_HOSTMGMNT_IPPLATFORMLOCAL_PORTRE
MOTE_PORTIOS_VERSION
----------------------------------------------------------------------------
-----------------------------
SW1SW210.1.1.2ciscoWS-C2960-8TC-LGigabitEthernet1/0/16Gi
gabitEthernet0/112.2(55)SE9
SW1R110.1.1.1Cisco3825GigabitEthernet1/0/22Gi
gabitEthernet0/012.4(24)T1
SW1R210.2.2.2Cisco2911GigabitEthernet1/0/21Gi
gabitEthernet0/015.2(2)T1
showipinterfacebrief
Вслучае,когданужнообработатьданные,которыевыведеныстолбцами,шаблонTextFSM,наиболееудобен.
Шаблондлявыводакомандыshowipinterfacebrief(файлtemplates/sh_ip_int_br.template):
ValueINTF(\S+)
ValueADDR(\S+)
ValueSTATUS(up|down|administrativelydown)
ValuePROTO(up|down)
Start
^${INTF}\s+${ADDR}\s+\w+\s+\w+\s+${STATUS}\s+${PROTO}->Record
Вэтомслучае,правиломожноописатьоднойстрокой.
ПримерыиспользованияTextFSM
443
Выводкоманды(файлoutput/sh_ip_int_br.txt):
R1#showipinterfacebrief
InterfaceIP-AddressOK?MethodStatusProtocol
FastEthernet0/015.0.15.1YESmanualupup
FastEthernet0/110.0.12.1YESmanualupup
FastEthernet0/210.0.13.1YESmanualupup
FastEthernet0/3unassignedYESunsetupup
Loopback010.1.1.1YESmanualupup
Loopback100100.0.0.1YESmanualupup
Результатвыполнениябудеттаким:
$pythonparse_output.pytemplates/sh_ip_int_br.templateoutput/sh_ip_int_br.txt
INTADDRSTATUSPROTO
----------------------------------------
FastEthernet0/015.0.15.1upup
FastEthernet0/110.0.12.1upup
FastEthernet0/210.0.13.1upup
FastEthernet0/3unassignedupup
Loopback010.1.1.1upup
Loopback100100.0.0.1upup
showiprouteospf
Рассмотримслучай,когданамнужнообработатьвыводкомандыshowiprouteospfивтаблицемаршрутизацииестьнесколькомаршрутовкоднойсети.
Длямаршрутовкоднойитойжесети,вместонесколькихстрок,гдебудетповторятьсясеть,будетсозданаодназапись,вкоторойвседоступныеnext-hopадресасобранывсписок.
Примервыводакомандыshowiprouteospf(файлoutput/sh_ip_route_ospf.txt):
ПримерыиспользованияTextFSM
444
R1#shiprouteospf
Codes:L-local,C-connected,S-static,R-RIP,M-mobile,B-BGP
D-EIGRP,EX-EIGRPexternal,O-OSPF,IA-OSPFinterarea
N1-OSPFNSSAexternaltype1,N2-OSPFNSSAexternaltype2
E1-OSPFexternaltype1,E2-OSPFexternaltype2
i-IS-IS,su-IS-ISsummary,L1-IS-ISlevel-1,L2-IS-ISlevel-2
ia-IS-ISinterarea,*-candidatedefault,U-per-userstaticroute
o-ODR,P-periodicdownloadedstaticroute,H-NHRP,l-LISP
+-replicatedroute,%-nexthopoverride
Gatewayoflastresortisnotset
10.0.0.0/8isvariablysubnetted,10subnets,2masks
O10.0.24.0/24[110/20]via10.0.12.2,1w2d,Ethernet0/1
O10.0.34.0/24[110/20]via10.0.13.3,1w2d,Ethernet0/2
O10.2.2.2/32[110/11]via10.0.12.2,1w2d,Ethernet0/1
O10.3.3.3/32[110/11]via10.0.13.3,1w2d,Ethernet0/2
O10.4.4.4/32[110/21]via10.0.13.3,1w2d,Ethernet0/2
[110/21]via10.0.12.2,1w2d,Ethernet0/1
[110/21]via10.0.14.4,1w2d,Ethernet0/3
O10.5.35.0/24[110/20]via10.0.13.3,1w2d,Ethernet0/2
Дляэтогопримераупрощаемзадачуисчитаем,чтомаршрутымогутбытьтолькоOSPFисобозначением,толькоO(тоесть,тольковнутризональныемаршруты).
Перваяверсияшаблонавыглядиттак:
ValueNetwork(([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}))
ValueMask(\/\d{1,2})
ValueDistance(\d+)
ValueMetric(\d+)
ValueNextHop([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})
Start
^O+${Network}${Mask}\s\[${Distance}\/${Metric}\]\svia\s${NextHop},->Record
Результатполучилсятакой:
NetworkMaskDistanceMetricNextHop
------------------------------------------
10.0.24.0/241102010.0.12.2
10.0.34.0/241102010.0.13.3
10.2.2.2/321101110.0.12.2
10.3.3.3/321101110.0.13.3
10.4.4.4/321102110.0.13.3
10.5.35.0/241102010.0.13.3
ПримерыиспользованияTextFSM
445
Всёнормально,нопотерялисьвариантыпутейдлямаршрута10.4.4.4/32.Этологично,ведьнетправила,котороеподошлобыдлятакойстроки.
List
ВоспользуемсяопциейListдляпеременнойNextHop:
ValueNetwork(([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}))
ValueMask(\/\d{1,2})
ValueDistance(\d+)
ValueMetric(\d+)
ValueListNextHop([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})
Start
^O+${Network}${Mask}\s\[${Distance}\/${Metric}\]\svia\s${NextHop},->Record
Теперьвыводполучилсятаким:
NetworkMaskDistanceMetricNextHop
----------------------------------------------
10.0.24.0/2411020['10.0.12.2']
10.0.34.0/2411020['10.0.13.3']
10.2.2.2/3211011['10.0.12.2']
10.3.3.3/3211011['10.0.13.3']
10.4.4.4/3211021['10.0.13.3']
10.5.35.0/2411020['10.0.13.3']
Изменилосьто,чтовстолбцеNextHopотображаетсясписок,нопокасоднимэлементом.
Таккак,передзаписьюмаршрута,длякоторогоестьнесколькопутей,надодобавитькнемувседоступныеадресаNextHop,надоперенестидействиеRecord.
Дляэтого,записьпереноситсянамомент,когдавстречаетсяследующаястрокасмаршрутом.Вэтотмоментнадозаписатьпредыдущуюстрокуитолькопослеэтого,ужезаписыватьтекущую.Дляэтого,используетсятакаязапись:
^O->Continue.Record
ВнейдействиеRecordговорит,чтонадозаписатьтекущеезначениепеременных.А,таккаквэтомправиленетпеременных,записываетсято,чтобыловпредыдущихзначениях.
ДействиеContinueговорит,чтонадопродолжитьработатьстекущейстрокойтак,какбудтосовпадениянебыло.Засчетэтого,сработаетследующаястрока.
ПримерыиспользованияTextFSM
446
Остаетсядобавитьправило,котороебудетописыватьдополнительныемаршрутыксети(внихнетсетиимаски):
^\s+\[${Distance}\/${Metric}\]\svia\s${NextHop},
Итоговыйшаблонвыглядиттак(файлtemplates/sh_ip_route_ospf.template):
ValueNetwork(([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}))
ValueMask(\/\d{1,2})
ValueDistance(\d+)
ValueMetric(\d+)
ValueListNextHop([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})
Start
^O->Continue.Record
^O+${Network}${Mask}\s\[${Distance}\/${Metric}\]\svia\s${NextHop},
^\s+\[${Distance}\/${Metric}\]\svia\s${NextHop},
Этотпримерсложнеепредыдущих,чтобыеголучшепонять,попробуйтепостепенноперейтиспрошлоговарианташаблона,кпоследнему.
Врезультате,мыполучимтакойвывод:
NetworkMaskDistanceMetricNextHop
------------------------------------------------------------------------
10.0.24.0/2411020['10.0.12.2']
10.0.34.0/2411020['10.0.13.3']
10.2.2.2/3211011['10.0.12.2']
10.3.3.3/3211011['10.0.13.3']
10.4.4.4/3211021['10.0.13.3','10.0.12.2','10.0.14.4']
10.5.35.0/2411020['10.0.13.3']
showetherchannelsummary
TextFSMудобноиспользоватьдляразборавывода,которыйотображаетсястолбцамиилидляобработкивывода,которыйнаходитсявразныхстроках.Менееудобнымиполучаютсяшаблоны,когданадополучитьнесколькооднотипныхэлементовизоднойстроки.
Примервыводакомандыshowetherchannelsummary(файлoutput/sh_etherchannel_summary.txt):
ПримерыиспользованияTextFSM
447
sw1#shetherchannelsummary
Flags:D-downP-bundledinport-channel
I-stand-alones-suspended
H-Hot-standby(LACPonly)
R-Layer3S-Layer2
U-inusef-failedtoallocateaggregator
M-notinuse,minimumlinksnotmet
u-unsuitableforbundling
w-waitingtobeaggregated
d-defaultport
Numberofchannel-groupsinuse:2
Numberofaggregators:2
GroupPort-channelProtocolPorts
------+-------------+-----------+-----------------------------------------------
1Po1(SU)LACPFa0/1(P)Fa0/2(P)Fa0/3(P)
3Po3(SU)-Fa0/11(P)Fa0/12(P)Fa0/13(P)Fa0/14(P)
Вданномслучае,нужнополучить:
имяиномерport-channel.Например,Po1списоквсехпортоввнем.Например,['Fa0/1','Fa0/2','Fa0/3']
Сложностьтутвтом,чтопортынаходятсяводнойстроке,авTextFSMнельзяуказыватьоднуитужепеременнуюнесколькоразвстроке.Но,естьвозможностьнесколькоразискатьсовпадениевстроке.
Перваяверсияшаблонавыглядиттак:
ValueCHANNEL(\S+)
ValueListMEMBERS(\w+\d+\/\d+)
Start
^\d++${CHANNEL}\(\S++[\w-]++[\w]++${MEMBERS}\(->Record
Вшаблонедвепеременные:
CHANNEL-имяиномерагрегированногопортаMEMBERS-списокпортов,которыевходятвагрегированныйпорт.Дляэтойпеременнойуказантип-List
Результат:
ПримерыиспользованияTextFSM
448
CHANNELMEMBERS
-------------------
Po1['Fa0/1']
Po3['Fa0/11']
Покачтоввыводетолькопервыйпорт,анужночтобыпопаливсепорты.Вданномслучае,надопродолжитьобработкустрокиспортами,посленайденногосовпадения.Тоесть,использоватьдействиеContinueиописатьследующеевыражение.
Единственнаястрока,котораяестьвшаблоне,описываетпервыйпорт.Надодобавитьстроку,котораяописываетследующийпорт.
Следующаяверсияшаблона:
ValueCHANNEL(\S+)
ValueListMEMBERS(\w+\d+\/\d+)
Start
^\d++${CHANNEL}\(\S++[\w-]++[\w]++${MEMBERS}\(->Continue
^\d++${CHANNEL}\(\S++[\w-]++[\w]++\S++${MEMBERS}\(->Record
Втораястрокаописываеттакоежевыражение,нопеременнаяMEMBERSсмещаетсянаследующийпорт.
Результат:
CHANNELMEMBERS
-----------------------------
Po1['Fa0/1','Fa0/2']
Po3['Fa0/11','Fa0/12']
Аналогичнонадодописатьвшаблонстроки,которыеописываюттретийичетвертыйпорт.Но,таккакввыводеможетбытьпеременноеколичествопортов,надоперенестиправилоRecordнаотдельнуюстроку,чтобыононебылопривязанокконкретномуколичествупортоввстроке.
ЕслиRecordбудетнаходиться,например,послестроки,вкоторойописанычетырепорта,дляситуациикогдапортоввстрокеменьше,записьнебудетвыполняться.
Итоговыйшаблон(файлtemplates/sh_etherchannel_summary.txt):
ПримерыиспользованияTextFSM
449
ValueCHANNEL(\S+)
ValueListMEMBERS(\w+\d+\/\d+)
Start
^\d+.*->Continue.Record
^\d++${CHANNEL}\(\S++[\w-]++[\w]++\S++${MEMBERS}\(->Continue
^\d++${CHANNEL}\(\S++[\w-]++[\w]++(\S++){2}+${MEMBERS}\(->Continue
^\d++${CHANNEL}\(\S++[\w-]++[\w]++(\S++){3}+${MEMBERS}\(->Continue
Результатобработки:
CHANNELMEMBERS
-------------------------------------------------
Po1['Fa0/1','Fa0/2','Fa0/3']
Po3['Fa0/11','Fa0/12','Fa0/13','Fa0/14']
Теперьвсепортыпопаливвывод.
Шаблонпредполагает,чтоводнойстрокебудетмаксимумчетырепорта.Еслипортовможетбытьбольше,надодобавитьсоответствующиестрокившаблон.
Возможенещёодинвариантвыводакомандыshetherchannelsummary(файлoutput/sh_etherchannel_summary2.txt):
sw1#shetherchannelsummary
Flags:D-downP-bundledinport-channel
I-stand-alones-suspended
H-Hot-standby(LACPonly)
R-Layer3S-Layer2
U-inusef-failedtoallocateaggregator
M-notinuse,minimumlinksnotmet
u-unsuitableforbundling
w-waitingtobeaggregated
d-defaultport
Numberofchannel-groupsinuse:2
Numberofaggregators:2
GroupPort-channelProtocolPorts
------+-------------+-----------+-----------------------------------------------
1Po1(SU)LACPFa0/1(P)Fa0/2(P)Fa0/3(P)
3Po3(SU)-Fa0/11(P)Fa0/12(P)Fa0/13(P)Fa0/14(P)
Fa0/15(P)Fa0/16(P)
ПримерыиспользованияTextFSM
450
Втакомвыводепоявляетсяновыйвариант-строки,вкоторыхнаходятсятолькопорты.
Длятогочтобышаблонобрабатывалиэтотвариант,надоегомодифицировать(файлtemplates/sh_etherchannel_summary2.txt):
ValueCHANNEL(\S+)
ValueListMEMBERS(\w+\d+\/\d+)
Start
^\d+.*->Continue.Record
^\d++${CHANNEL}\(\S++[\w-]++[\w]++${MEMBERS}\(->Continue
^\d++${CHANNEL}\(\S++[\w-]++[\w]++\S++${MEMBERS}\(->Continue
^\d++${CHANNEL}\(\S++[\w-]++[\w]++(\S++){2}+${MEMBERS}\(->Continue
^\d++${CHANNEL}\(\S++[\w-]++[\w]++(\S++){3}+${MEMBERS}\(->Continue
^+${MEMBERS}->Continue
^+\S++${MEMBERS}->Continue
^+(\S++){2}+${MEMBERS}->Continue
^+(\S++){3}+${MEMBERS}->Continue
Результатбудеттаким:
CHANNELMEMBERS
---------------------------------------------------------------------
Po1['Fa0/1','Fa0/2','Fa0/3']
Po3['Fa0/11','Fa0/12','Fa0/13','Fa0/14','Fa0/15','Fa0/16']
НаэтоммызаканчиваемразбиратьсясшаблонамиTextFSM.
ПримерышаблоновдляCiscoидругогооборудованияможнопосмотретьвпроектеntc-ansible.
ПримерыиспользованияTextFSM
451
TextFSMCLITableБлагодаряTextFSM,можнообрабатыватьвыводкомандиполучатьструктурированныйрезультат.Но,всёещёнадовручнуюпрописыватькакимшаблономобрабатыватькомандыshow,каждыйраз,когдаиспользуетсяTextFSM.
Былобынамногоудобнейиметькакое-тосоответствиемеждукомандойишаблоном.Чтобыможнобылонаписатьобщийскрипт,которыйвыполняетподключениякустройствам,отправляеткоманды,самвыбираетшаблонипарситвыводвсоответствиисшаблоном.
ВTextFSMестьтакаявозможность.
Длятого,чтобыейможнобыловоспользоваться,надосоздатьфайлвкоторомописанысоответствиямеждукомандамиишаблонами.ВTextFSMонназываетсяindex.
Этотфайлдолженнаходитьсявкаталогесшаблонамиидолжениметьтакойформат:
перваястрока-названияколоноккаждаяследующаястрока-этосоответствиешаблонакомандеобязательныеколонки,местоположениекоторыхфиксировано(должныбытьобязательнопервойипоследней,соответственно):
перваяколонка-именашаблоновпоследняяколонка-соответствующаякоманда
вэтойколонкеиспользуетсяспециальныйформат,чтобыописатьто,чтокомандаможетбытьнаписананеполностью
остальныеколонкимогутбытьлюбыминапример,впримеренижебудутколонкиHostname,Vendor.Онипозволяютуточнитьинформациюобустройстве,чтобыопределитькакойшаблониспользовать.
например,командаshowversionможетбытьуоборудованияCiscoиHP.Соответственно,толькокомандынедостаточно,чтобыопределитькакойшаблониспользовать.Втакомслучае,можнопередатьинформациюотом,какойтипоборудованияиспользуется,вместескомандой,итогдаполучитсяопределитьправильныйшаблон.
вовсехстолбцах,кромепервого,поддерживаютсярегулярныевыражениявкомандах,внутри[[]]регулярныевыражениянеподдерживаются
Примерфайлаindex:
CLITable
452
Template,Hostname,Vendor,Command
sh_cdp_n_det.template,.*,Cisco,sh[[ow]]cdpne[[ighbors]]de[[tail]]
sh_clock.template,.*,Cisco,sh[[ow]]clo[[ck]]
sh_ip_int_br.template,.*,Cisco,sh[[ow]]ipint[[erface]]br[[ief]]
sh_ip_route_ospf.template,.*,Cisco,sh[[ow]]iprou[[te]]o[[spf]]
Обратитевниманиенато,какзаписаныкоманды:
sh[[ow]]ipint[[erface]]br[[ief]]
этазаписьбудетпреобразованаввыражениеsh((ow)?)?ipint((erface)?)?br((ief)?)?этозначит,чтоTextFSMсможетопределитькакойшаблониспользовать,дажеесликоманданабрананеполностьюнапример,такиевариантыкомандысработают:
shipintbrshowipinterbri
КакиспользоватьCLItable
Посмотримкакпользоватьсяклассомclitableифайломindex.
Вкаталогеtemplatesтакиешаблоныифайлindex:
sh_cdp_n_det.template
sh_clock.template
sh_ip_int_br.template
sh_ip_route_ospf.template
index
СначалапопробуемпоработатьсCLITableвipython,чтобыпосмотретькакиевозможностиестьуэтогокласса,азатемпосмотримнафинальныйскрипт.
Еслиприработесclitableвозникнетошибка,чтонетмодуляterminal,посмотритевкаталоге,вкоторомнаходитсяустановленныйTextFSM(взависимостиотОС),файлterminal.py.Еслиегонет,простосоздайтееговручнуюископируйтесодержимоеэтогофайлаизрепозиторияTextFSM.Определить,гденаходитсямодуль,можнотакимобразом:printtextfsm.__file__
Дляначала,импортируемклассclitable:
In[1]:importtextfsm.clitableasclitable
CLITable
453
Проверятьработуclitableбудемнапоследнемпримереизпрошлогораздела-выводекомандыshowiprouteospf.Считываемвывод,которыйхранитсявфайлеoutput/sh_ip_route_ospf.txt,встроку:
In[2]:output_sh_ip_route_ospf=open('output/sh_ip_route_ospf.txt').read()
Сначаланадоинициализироватькласс,передавемуимяфайла,вкоторомхранитсясоответствиемеждушаблонамиикомандами,иуказатьимякаталога,вкоторомхранятсяшаблоны:
In[3]:cli_table=clitable.CliTable('index','templates')
Надоуказатькакаякомандапередаетсяиуказатьдополнительныеатрибуты,которыепомогутидентифицироватьшаблон.Дляэтого,нужносоздатьсловарь,вкоторомключи-именастолбцов,которыеопределенывфайлеindex.Вданномслучае,необязательноуказыватьназваниевендора,таккаккомандеshiprouteospfсоответстветтолькоодиншаблон.
In[4]:attributes={'Command':'showiprouteospf','Vendor':'Cisco'}
МетодуParseCmdнадопередатьвыводкомандыисловарьспараметрами:
In[5]:cli_table.ParseCmd(output_sh_ip_route_ospf,attributes)
Врезультате,вобъектеcli_table,получаемобработанныйвыводкомандыshiprouteospf.
Методыcli_table(чтобыпосмотретьвсеметоды,надовызватьdir(cli_table)):
CLITable
454
In[6]:cli_table.
cli_table.AddColumncli_table.NewRowcli_table.indexcli_t
able.size
cli_table.AddKeyscli_table.ParseCmdcli_table.index_filecli_t
able.sort
cli_table.Appendcli_table.ReadIndexcli_table.nextcli_t
able.superkey
cli_table.CsvToTablecli_table.Removecli_table.rawcli_t
able.synchronised
cli_table.FormattedTablecli_table.Resetcli_table.rowcli_t
able.table
cli_table.INDEXcli_table.RowWithcli_table.row_classcli_t
able.template_dir
cli_table.KeyValuecli_table.extendcli_table.row_index
cli_table.LabelValueTablecli_table.headercli_table.separator
Например,есливызватьprintcli_table,получимтакойвывод:
In[7]:printcli_table
Network,Mask,Distance,Metric,NextHop
10.0.24.0,/24,110,20,['10.0.12.2']
10.0.34.0,/24,110,20,['10.0.13.3']
10.2.2.2,/32,110,11,['10.0.12.2']
10.3.3.3,/32,110,11,['10.0.13.3']
10.4.4.4,/32,110,21,['10.0.13.3','10.0.12.2','10.0.14.4']
10.5.35.0,/24,110,20,['10.0.13.3']
МетодFormattedTableпозволяетполучитьвыводввидетаблицы:
In[8]:printcli_table.FormattedTable()
NetworkMaskDistanceMetricNextHop
====================================================================
10.0.24.0/241102010.0.12.2
10.0.34.0/241102010.0.13.3
10.2.2.2/321101110.0.12.2
10.3.3.3/321101110.0.13.3
10.4.4.4/321102110.0.13.3,10.0.12.2,10.0.14.4
10.5.35.0/241102010.0.13.3
Такойвыводможетпригодитьсядляотображенияинформации.
Чтобыполучитьизобъектаcli_tableструктурированныйвывод,например,списоксписков,надообратитьсякобъектутакимобразом:
CLITable
455
In[9]:data_rows=[]
In[10]:forrowincli_table:
....:current_row=[]
....:forvalueinrow:
....:current_row.append(value)
....:data_rows.append(current_row)
....:
In[11]:data_rows
Out[11]:
[['10.0.24.0','/24','110','20',['10.0.12.2']],
['10.0.34.0','/24','110','20',['10.0.13.3']],
['10.2.2.2','/32','110','11',['10.0.12.2']],
['10.3.3.3','/32','110','11',['10.0.13.3']],
['10.4.4.4','/32','110','21',['10.0.13.3','10.0.12.2','10.0.14.4']],
['10.5.35.0','/24','110','20',['10.0.13.3']]]
Отдельноможнополучитьназваниястолбцов:
In[12]:cli_table.header.viewvalues()
Out[12]:dict_values([])
In[13]:header=[]
In[13]:fornameincli_table.header:
....:header.append(name)
....:
In[14]:header
Out[14]:['Network','Mask','Distance','Metric','NextHop']
Теперьвыводаналогичентому,которыйбылполученвпрошломразделе.
Соберемвсёводинскрипт(файлtextfsm_clitable.py):
CLITable
456
importtextfsm.clitableasclitable
output_sh_ip_route_ospf=open('output/sh_ip_route_ospf.txt').read()
cli_table=clitable.CliTable('index','templates')
attributes={'Command':'showiprouteospf','Vendor':'Cisco'}
cli_table.ParseCmd(output_sh_ip_route_ospf,attributes)
print"CLITableoutput:\n",cli_table
print"FormattedTable:\n",cli_table.FormattedTable()
data_rows=[]
forrowincli_table:
current_row=[]
forvalueinrow:
current_row.append(value)
data_rows.append(current_row)
header=[]
fornameincli_table.header:
header.append(name)
printheader
forrowindata_rows:
printrow
Вупражненияхкэтомуразделубудетзадание,вкоторомнадообъединитьописаннуюпроцедурувфункцию.Атакжевариантсполучениемспискасловарей.
Выводбудеттаким:
CLITable
457
$pythontextfsm_clitable.py
CLITableoutput:
Network,Mask,Distance,Metric,NextHop
10.0.24.0,/24,110,20,['10.0.12.2']
10.0.34.0,/24,110,20,['10.0.13.3']
10.2.2.2,/32,110,11,['10.0.12.2']
10.3.3.3,/32,110,11,['10.0.13.3']
10.4.4.4,/32,110,21,['10.0.13.3','10.0.12.2','10.0.14.4']
10.5.35.0,/24,110,20,['10.0.13.3']
FormattedTable:
NetworkMaskDistanceMetricNextHop
====================================================================
10.0.24.0/241102010.0.12.2
10.0.34.0/241102010.0.13.3
10.2.2.2/321101110.0.12.2
10.3.3.3/321101110.0.13.3
10.4.4.4/321102110.0.13.3,10.0.12.2,10.0.14.4
10.5.35.0/241102010.0.13.3
['Network','Mask','Distance','Metric','NextHop']
['10.0.24.0','/24','110','20',['10.0.12.2']]
['10.0.34.0','/24','110','20',['10.0.13.3']]
['10.2.2.2','/32','110','11',['10.0.12.2']]
['10.3.3.3','/32','110','11',['10.0.13.3']]
['10.4.4.4','/32','110','21',['10.0.13.3','10.0.12.2','10.0.14.4']]
['10.5.35.0','/24','110','20',['10.0.13.3']]
Теперь,спомощьюTextFSM,можнонетолькополучатьструктурированныйвывод,ноиавтоматическиопределятькакойшаблониспользовать,покомандеиопциональнымаргументам.
CLITable
458
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание14.1
Переделатьпример,которыйиспользовалсявразделеTextFSM,вфункцию.
Функциядолжнаназыватьсяparse_output.Параметрыфункции:
template-шаблонTextFSM(этодолжнобытьимяфайла,вкоторомнаходитсяшаблон)output-выводсоответствующейкомандыshow(строка)
Функциядолжнавозвращатьсписок:
первыйэлемент-этосписоксназваниямистолбцов(впримерениже,находитсявпеременнойheader)остальныеэлементыэтосписки,вкоторыхнаходятсярезультатыобработкивывода(впримерениже,находитсявпеременнойresult)
Проверитьработуфункциинакаком-тоизпримеровраздела.
Примеризраздела:
Задания
459
importsys
importtextfsm
fromtabulateimporttabulate
template=sys.argv[1]
output_file=sys.argv[2]
f=open(template)
output=open(output_file).read()
re_table=textfsm.TextFSM(f)
header=re_table.header
result=re_table.ParseText(output)
printtabulate(result,headers=header)
Задание14.1a
Переделатьфункциюparse_outputиззадания14.1такимобразом,чтобы,вместоспискасписков,онавозвращалаодинсписоксловарей:
ключи-названиястолбцов,значения,соответствующиезначениявстолбцах.
Тоесть,длякаждойстрокибудетодинсловарьвсписке.
Задание14.2
Вэтомзаданиинужноиспользоватьфункциюparse_outputиззадания14.1.Онаиспользуетсядлятого,чтобыполучитьструктурированныйвыводврезультатеобработкивыводакоманды.
ПолученныйвыводнужнозаписатьвCSVформате.
ДлязаписивыводавCSV,нужносоздатьфункциюlist_to_csv,котораяожидаеткакаргументы:
список:первыйэлемент-этосписоксназваниямизаголовковостальныеэлементыэтосписки,вкоторомнаходятсярезультатыобработкивывода
имяфайла,вкоторыйнужнозаписатьданныевCSVформате
Проверитьработуфункциинапримереобработкикомандыshipintbr(шаблонивыводестьвразделе).
Задания
460
Задание14.3
СделатьшаблонTextFSMдляобработкивыводаshipdhcpsnoopingbinding.Выводкомандынаходитсявфайлеoutput/sh_ip_dhcp_snooping.txt.
Шаблондолженобрабатыватьивозвращатьзначениятакихстолбцов:
MacAddressIpAddressVLANInterface
Проверитьработушаблонаспомощьюфункциииззадания14.1.
Задание14.4
Наосновепримераизразделаclitableсделатьфункциюparse_command_dynamic.
Параметрыфункции:
словарьатрибутов,вкоторомнаходятсятакиепарыключ:значение:'Command':команда'Vendor':вендор(обратитевнимание,чтофайлindexотличаетсяотпримера,которыйиспользовалсявразделе,поэтомувамнужноподставитьтутправильноезначение)
имяфайла,гдехранитсясоответствиемеждукомандамиишаблонами(строка)значениепоумолчаниюаргумента-index
каталог,гдехранятсяшаблоныифайлссоответствиями(строка)значениепоумолчаниюаргумента-templates
выводкоманды(строка)
Функциядолжнавозвращатьсписоксловарейсрезультатамиобработкивыводакоманды(каквзадании14.1a):
ключи-названиястолбцовзначения-соответствующиезначениявстолбцах
Проверитьработуфункциинапримеревыводакомандыshipintbr.
Примеризраздела:
Задания
461
importtextfsm.clitableasclitable
output_sh_ip_route_ospf=open('output/sh_ip_route_ospf.txt').read()
cli_table=clitable.CliTable('index','templates')
attributes={'Command':'showiprouteospf','Vendor':'Cisco'}
cli_table.ParseCmd(output_sh_ip_route_ospf,attributes)
print"CLITableoutput:\n",cli_table
print"FormattedTable:\n",cli_table.FormattedTable()
data_rows=[]
forrowincli_table:
current_row=[]
forvalueinrow:
current_row.append(value)
data_rows.append(current_row)
header=[]
fornameincli_table.header:
header.append(name)
printheader
forrowindata_rows:
printrow
Задание14.4a
Переделатьфункциюиззадания14.4:
добавитьаргументshow_output,которыйконтролируетбудетливыводитьсярезультатобработкикомандынастандартныйпотоквывода
поумолчаниюFalse,чтозначитрезультатнебудетвыводитьсярезультатдолженотображатьсяввиде,которыйвозвращаетметодFormattedTable(примерестьвразделе)
Задание14.5
ВэтомзаданиисоединяетсяфункциональностьTextFSMиподключениекоборудованию.
Задачатакая:
подключитьсякоборудованиювыполнитькомандуshowполученныйвыводпередаватьнаобработкуTextFSMвернутьрезультатобработки
Задания
462
Дляэтого,воспользуемсяфункциями,которыебылисозданыранее:
функциейsend_show_commandизупражнения12.1функциейparse_command_dynamicизупражнения14.4
Вэтомупражнениинужносоздатьфункциюsend_and_parse_command:
функциядолжнаиспользоватьвнутрисебяфункцииparse_command_dynamicиsend_show_command.какиеаргументыдолжныбытьуфункцииsend_and_parse_command,нужнорешитьсамостоятельно
но,надоиметьввиду,какиеаргументыожидаютдвеготовыефункции,которыемыиспользуем
функцияsend_and_parse_commandдолжнавозвращать:функцияsend_show_commandвозвращаетсловарьсрезультатамивыполнениякоманды:
ключ-IPустройствазначение-результатвыполнениякоманды
затем,нужноотправитьполученныйвыводнаобработкуфункцииparse_command_dynamicврезультате,долженполучитьсясловарь,вкотором:
ключ-IPустройствазначение-списоксловарей(тоесть,тотвывод,которыйбылполученизфункцииparse_command_dynamic)
Дляфункцииsend_show_commandсозданфайлdevices.yaml,вкоторомнаходятсяпараметрыподключениякустройствам.
Проверитьработуфункцииsend_and_parse_commandнакомандеshipintbr.
Задание14.6
Этозаданиепохоженазадание14.5,новэтомзаданииподключениянадовыполнятьпараллельно.
Дляэтогонадоиспользоватьфункцииconnect_sshиconn_processes(примеризразделаmultiprocessing)ифункциюparse_command_dynamicизупражнения14.4.
Вэтомупражнениинужносоздатьфункциюsend_and_parse_command_parallel:
онадолжнаиспользоватьвнутрисебяфункцииconnect_ssh,conn_processesиparse_command_dynamicкакиеаргументыдолжныбытьуфункцииsend_and_parse_command_parallel,нужнорешитьсамостоятельно
Задания
463
нонадоиметьввиду,какиеаргументыожидаюттриготовыефункции,которыеиспользуются
функцияsend_and_parse_command_parallelдолжнавозвращатьсловарь,вкотором:
ключ-IPустройствазначение-списоксловарей(тоесть,тотвывод,которыйбылполученизфункцииparse_command_dynamic)
Дляфункцииconn_processesсозданфайлdevices.yaml,вкоторомнаходятсяпараметрыподключениякустройствам.
Проверитьработуфункцииsend_and_parse_command_parallelнакомандеshipintbr.
Примеризразделаmultiprocessing:
Задания
464
importmultiprocessing
fromnetmikoimportConnectHandler
importsys
importyaml
COMMAND=sys.argv[1]
devices=yaml.load(open('devices.yaml'))
defconnect_ssh(device_dict,command,queue):
ssh=ConnectHandler(**device_dict)
ssh.enable()
result=ssh.send_command(command)
print"Connectiontodevice%s"%device_dict['ip']
queue.put({device_dict['ip']:result})
defconn_processes(function,devices,command):
processes=[]
queue=multiprocessing.Queue()
fordeviceindevices:
p=multiprocessing.Process(target=function,args=(device,command,queue)
)
p.start()
processes.append(p)
forpinprocesses:
p.join()
results=[]
forpinprocesses:
results.append(queue.get())
returnresults
print(conn_processes(connect_ssh,devices['routers'],COMMAND))
Задания
465
AnsibleAnsible-этосистемауправленияконфигурациями.Ansibleпозволяетавтоматизироватьиупроститьнастройку,обслуживаниеиразвертываниесерверов,служб,ПОидр.
Наданныймоментсуществуетнесколькосистемуправленияконфигурациями.
Однако,дляработыссетевымоборудованием,чащевсегоиспользуетсяAnsible.Связаноэтостем,чтоAnsibleнетребуетустановкиагентанауправляемыехосты.Особенноактуальноэтодляустройств,которыепозволяютработатьснимитолькочерезCLI.
Крометого,Ansibleактивноразвиваетсявсторонуподдержкисетевогооборудованияивнемпостояннопоявляютсяновыевозможностиимодулидляработыссетевымоборудованием.
Некотороесетевоеоборудованиеподдерживаетдругиесистемыуправленияконфигурациями(позволяетустановитьагента).
ОдноизважныхпреимуществAnsibleзаключаетсявтом,чтооноченьпроствиспользовании.Иегодовольнолегконачатьиспользовать.
Примерызадач,которыепоможетрешитьAnsible:
подключениепоSSHкустройствампаралелльноеподключениекустройствампоSSH(можноуказыватькосколькиустройствамподключатьсяодновременно)
отправкакоманднаустройстваудобныйсинтаксисописанияустройств:
можноразбиватьустройстванагруппыизатемотправлятькакие-токомандынавсюгруппу
поддержкашаблоновконфигурацийсJinja2
ЭтовсеголишьнескольковозможностейAnsible,которыеотносятсяксетевомуоборудованию.Ониперечисленыдлятого,чтобыпоказать,чтоэтизадачиAnsibleсразуснимаетиможнонеиспользоватьдляэтогокакие-тоскрипты.
УстановкаAnsible
15.Ansible
466
Ansibleнужноустанавливатьтольконатоймашине,скоторойбудетвыполнятьсяуправлениеустройствами.
Требованиякуправляющемухосту:
поддержкаPython2.7(или2.6)Windowsнеможетбытьуправляющимхостом
Ansibleдовольночастообновляется,поэтомулучшеустановитьеговвиртуальномокружении.
УстановитьAnsibleможнопо-разному.
Например,спомощьюpip:
$pipinstallansible==2.2.0.0
Покачто,лучшепоставитьAnsibleсявнымуказаниемверсии,таккаквверсии2.2.1.0быливнесеныизмененияиз-закоторыхчастьфункционалаработаетсошибками.Скореевсего,этоскороисправят,нопримоейпоследнейпроверке(05.02.2017),былипроблемы.
ПараметрыоборудованияВпримерахразделаиспользуютсятримаршрутизатораиодинкоммутатор.Книмнетникакихтребований,тольконастроенныйSSH.
Параметры,которыеиспользуютсявразделе:
пользователь:ciscoпароль:ciscoпарольнарежимenable:ciscoSSHверсии2IP-адреса:
R1:192.168.100.1R2:192.168.100.2R3:192.168.100.3SW1:192.168.100.100
Есливыбудетеиспользоватьдругиепараметры,изменитесоответственноинвентарныйфайл,конфигурационныйфайлAnsibleифайлgroup_vars/all.yml.
15.Ansible
467
15.Ansible
468
ОсновыAnsibleAnsible:
РаботаетбезустановкиагентанауправляемыехостыИспользуетSSHдляподключениякуправляемымхостамВыполняетизменения,спомощьюмодулейPython,которыевыполняютсянауправляемыххостахМожетвыполнятьдействиялокально,науправляющемхостеИспользуетYAMLдляописаниясценариевСодержитмножествомодулей(ихколичествопостояннорастет)Легкописатьсвоимодули
Терминология
Controlmachine—управляющийхост.СерверAnsible,скоторогопроисходитуправлениедругимихостамиManagenode—управляемыехостыInventory—инвентарныйфайл.Вэтомфайлеописываютсяхосты,группыхостов.АтакжемогутбытьсозданыпеременныеPlaybook—файлсценариевPlay—сценарий(наборзадач).Связываетзадачисхостами,длякоторыхэтизадачинадовыполнитьTask—задача.ВызываетмодульсуказаннымипараметрамиипеременнымиModule—модульAnsible.Реализуетопределенныефункции
Списоктерминоввдокументации.
Quickstart
СAnsibleоченьпростоначатьработать.Минимум,которыйнужендляначалаработы:
инвентарныйфайл-внемописываютсяустройстваизменитьконфигурациюAnsible,дляработыссетевымоборудованиемразобратьсясad-hocкомандами-этовозможностьвыполнятьпростыедействиясустройствамиизкоманднойстроки
например,спомощьюad-hocкоманд,можноотправитькомандуshowнанесколькоустройств
ОсновыAnsible
469
Намногобольшевозможностейпоявится,прииспользованииplaybook(файлысценариев).Ноad-hocкомандынамногопрощеначатьиспользовать.ИснимилегченачатьразбиратьсясAnsible.
ОсновыAnsible
470
ИнвентарныйфайлИнвентарныйфайл-этофайл,вкоторомописываютсяустройства,ккоторымAnsibleбудетподключаться.
Хостыигруппы
ВинвентарномфайлеустройствамогутуказыватьсяиспользуяIP-адресаилиимена.Устройствамогутбытьуказаныпоодномуилиразбитынагруппы.
ФайлописываетсявформатеINI.Примерфайла:
r5.example.com
[cisco-routers]
192.168.255.1
192.168.255.2
192.168.255.3
192.168.255.4
[cisco-edge-routers]
192.168.255.1
192.168.255.2
Название,котороеуказановквадратныхскобках-этоназваниегруппы.Вданномслучае,созданыдвегруппыустройств:cisco-routersиcisco-edge-routers.
Обратитевнимание,чтоадреса192.168.255.1и192.168.255.2находятсявдвухгруппах.Этонормальнаяситуация,одинитотжеадресилиимяхоста,можнопомещатьвразныегруппы.
Такимобразомможноприменятьотдельнокакие-тополитикидлягруппыcisco-edge-routers,новтожевремя,когданеобходимонастроитьчто-то,чтокасаетсявсехмаршрутизаторов,можноиспользоватьгруппуcisco-routers.
Поумолчанию,файлнаходитсяв/etc/ansible/hosts.
Номожносоздаватьсвойинвентарныйфайлииспользоватьего.Дляэтогонужно,либоуказатьегопризапускеansible,используяопцию-i<путь>,либоуказатьфайлвконфигурационномфайлеAnsible.
Есликакое-тоизустройствиспользуетнестандартныйпортSSH,портможноуказатьпослеимениилиадресаустройства,черездвоеточие(нижепоказанпример).
Инвентарныйфайл
471
ТакойвариантуказанияпортаработаеттолькосподключениямиOpenSSHинеработаетсparamiko.
Примеринвентарногофайла,сиспользованиемнестандартныхпортовдляSSH:
[cisco-routers]
192.168.255.1:22022
192.168.255.2:22022
192.168.255.3:22022
[cisco-switches]
192.168.254.1
192.168.254.2
Есливгруппунадодобавитьнесколькоустройствсоднотипнымиименами,можноиспользоватьтакойвариантзаписи:
[cisco-routers]
192.168.255.[1-5]
Такаязаписьозначает,чтовгруппупопадутустройствасадресами192.168.255.1-192.168.255.5.
Группаизгрупп
Ansibleтакжепозволяетобъединятьгруппыустройстввобщуюгруппу.Дляэтогоиспользуетсяспециальныйсинтаксис:
[cisco-routers]
192.168.255.1
192.168.255.2
192.168.255.3
[cisco-switches]
192.168.254.1
192.168.254.2
[cisco-devices:children]
cisco-routers
cisco-switches
Инвентарныйфайл
472
AdHocкоманды
Ad-hocкоманды-этовозможностьзапуститькакое-тодействиеAnsibleизкоманднойстроки.
Такойвариантиспользуется,какправило,втехслучаях,когданадочто-топроверить,например,работумодуля.Илипростовыполнитькакое-торазовоедействие,котороененужносохранять.
Влюбомслучае,этопростойибыстрыйспособначатьиспользоватьAnsible.
Сначаланужносоздатьвлокальномкаталогеинвентарныйфайл.Назовемегоmyhosts:
[cisco-routers]
192.168.100.1
192.168.100.2
192.168.100.3
[cisco-switches]
192.168.100.100
Приподключениикустройствампервыйраз,сначалалучшеподключитьсякнимвручную,чтобыключиустройствбылисохраненылокально.ВAnsibleестьвозможностьотключитьэтупервоначальнуюпроверкуключей.Вразделеоконфигурационномфайлемыпосмотримкакэтоделать(такойвариантможетпонадобиться,еслинадоподключатьсякбольшомуколичествуустройств).
Примерad-hocкоманды:
$ansiblecisco-routers-imyhosts-mraw-a"shipintbr"-ucisco--ask-pass
Разберемсяспараметрамикоманды:
cisco-routers-группаустройств,ккоторымнужноприменитьдействияэтагруппадолжнасуществоватьвинвентарномфайлеэтоможетбытьконкретноеимяилиадресеслинужноуказатьвсехостыизфайла,можноиспользоватьзначениеallили*Ansibleподдерживаетболеесложныевариантыуказанияхостов,срегулярнымивыражениямииразнымишаблонами.Подробнееобэтомвдокументации
-imyhosts-параметр-iпозволяетуказатьинвентарныйфайл
Ad-Hocкоманды
473
-mraw-a"shipintbr"-параметр-mrawозначает,чтоиспользуетсямодульraw
этотмодульпозволяетотправлятькомандывSSHсессии,ноприэтомнезагружаетнахостмодульPython.Тоесть,этотмодульпростоотправляетуказаннуюкомандукакстрокуивсёплюсмодуляrawвтом,чтоонможетиспользоватьсядлялюбойсистемы,которуюподдерживаетAnsible-a"shipintbr"-параметр-aуказываеткакуюкомандуотправить
-ucisco-подключениевыполняетсяотименипользователяcisco--ask-pass-параметркоторыйнужноуказать,чтобыаутентификациябылапопаролю,анепоключам
Результатвыполнениябудеттаким:
$ansiblecisco-routers-imyhosts-mraw-a"shipintbr"-ucisco--ask-pass
Ошибказначит,чтонужноустановитьпрограммуsshpass.Этаособенностьвозникаеттолькокогдаиспользуетсяаутентификациюпопаролю.
Установкаsshpass:
$sudoapt-getinstallsshpass
Командунадовыполнитьповторно:
$ansiblecisco-routers-imyhosts-mraw-a"shipintbr"-ucisco--ask-pass
Ad-Hocкоманды
474
Теперьвсёпрошлоуспешно.Командавыполниласьиотобразилсявыводскаждогоустройства.
Аналогичнымобразомможнопопробоватьвыполнятьидругиекомандыи/илинадругихкомбинацияхустройств.
Ad-Hocкоманды
475
КонфигурационныйфайлНастройкиAnsibleможноменятьвконфигурационномфайле.
КонфигурационныйфайлAnsibleможетхранитьсявразныхместах(файлыперечисленывпорядкеуменьшенияприоритетности):
ANSIBLE_CONFIG(переменнаяокружения)ansible.cfg(втекущемкаталоге).ansible.cfg(вдомашнемкаталогепользователя)/etc/ansible/ansible.cfg
Ansibleищетфайлконфигурациивуказанномпорядкеииспользуетпервыйнайденный(конфигурацияизразныхфайловнесовмещается).
Вконфигурационномфайлеможноменятьмножествопараметров.Полныйсписокпараметровиихописание,можнонайтивдокументации.
Втекущемкаталогедолженбытьинвентарныйфайлmyhosts:
[cisco-routers]
192.168.100.1
192.168.100.2
192.168.100.3
[cisco-switches]
192.168.100.100
Втекущемкаталогенадосоздатьтакойконфигурационныйфайлansible.cfg:
[defaults]
inventory=./myhosts
remote_user=cisco
ask_pass=True
Настройкивконфигурационномфайле:
[defaults]-этосекцияконфигурацииописываетобщиепараметрыпоумолчаниюinventory=./myhosts-параметрinventoryпозволяетуказатьместоположениеинвентарногофайла.
Еслинастроитьэтотпараметр,непридетсяуказывать,гденаходитсяфайл,прикаждомзапускеAnsible
Конфигурационныйфайл
476
remote_user=cisco-отименикакогопользователябудетподключатьсяAnsibleask_pass=True-этотпараметраналогиченопции--ask-passвкоманднойстроке.ЕслионвыставленвконфигурацииAnsible,тоужененужноуказыватьеговкоманднойстроке.
Теперьвызовad-hocкомандыбудетвыглядетьтак:
$ansiblecisco-routers-mraw-a"shipintbr"
Теперьненужноуказыватьинвентарныйфайл,пользователяиопцию--ask-pass.
gathering
Поумолчанию,Ansibleсобираетфактыобустройствах.
Факты-этоинформацияохостах,ккоторымподключаетсяAnsible.Этифактыможноиспользоватьвplaybookишаблонахкакпеременные.
Сборомфактов,поумолчанию,занимаетсямодульsetup.
Но,длясетевогооборудования,модульsetupнеподходит,поэтомусборфактовнадоотключить.ЭтоможносделатьвконфигурационномфайлеAnsibleиливplaybook.
Длясетевогооборудованиянужноиспользоватьотдельныемодулидлясборафактов(еслиониесть).Эторассматриваетсявразделеios_facts.
Отключениесборафактоввконфигурационномфайле:
gathering=explicit
host_key_checking
Параметрhost_key_checkingотвечаетзапроверкyключей,приподключениипоSSH.Еслиуказатьвконфигурационномфайлеhost_key_checking=False,проверкабудетотключена.
Этополезно,когдасуправляющегохостаAnsibleнадоподключитьсякбольшомуколичествуустройствпервыйраз.
Чтобыпроверитьэтотфункционал,надоудалитьсохраненныеключидляустройствCisco,ккоторымужевыполнялосьподкление.
Влинуксонинаходятсявфайле~/.ssh/known_hosts.
Есливыполнитьad-hocкоманду,послеудаленияключей,выводбудеттаким:
Конфигурационныйфайл
477
$ansiblecisco-routers-mraw-a"shipintbr"
Добавляемвконфигурационныйфайлпараметрhost_key_checking:
[defaults]
inventory=./myhosts
remote_user=cisco
ask_pass=True
host_key_checking=False
Иповторимad-hocкоманду:
$ansiblecisco-routers-mraw-a"shipintbr"
Конфигурационныйфайл
478
Обратитевниманиенастроки:
Warning:Permanentlyadded'192.168.100.1'(RSA)tothelistofknownhosts.
Ansibleсамдобавилключиустройстввфайл~/.ssh/known_hosts.Приподключениивследующийразэтогосообщенияуженебудет.
Другиепараметрыконфигурационногофайламожнопосмотретьвдокументации.ПримерконфигурационногофайлаврепозиторииAnsible.
Конфигурационныйфайл
479
Конфигурационныйфайл
480
МодулиAnsibleВместесустановкойAnsibleустанавливаетсятакжебольшоеколичествомодулей(библиотекамодулей).Втекущейбиблиотекемодулей,находитсяпорядка200модулей.
Модулиотвечаютзадействия,которыевыполняетAnsible.Приэтом,каждыймодуль,какправило,отвечаетзасвоюконкретнуюинебольшуюзадачу.
Модулиможновыполнятьотдельно,вad-hocкомандахилисобиратьвопределенныйсценарий(play),азатемвplaybook.
Какправило,привызовемодуля,емунужнопередатьаргументы.Какие-тоаргументыбудутуправлятьповедениемипараметрамимодуля,акакие-топередавать,например,команду,которуюнадовыполнить.
Например,мыужевыполнялиad-hocкоманды,используямодульraw.Ипередавалиемуаргументы:
$ansiblecisco-routers-imyhosts-mraw-a"shipintbr"-ucisco--ask-pass
Выполнениетакойжезадачивplaybookбудетвыглядетьтак(playbookрассматриваетсявследующемразделе):
-name:runshipintbr
raw:shipintbr|exunass
Послевыполнения,модульвозвращаетрезультатывыполнениявформатеJSON.
МодулиAnsible,какправило,идемпотентны.Этоозначает,чтомодульможновыполнятьсколькоугоднораз,ноприэтоммодульбудетвыполнятьизменения,толькоеслисистеманенаходитсявжелаемомсостоянии.
ВAnsibleмодулиразделенынадвекатегории:
core-этомодули,которыевсегдаустанавливаютсявместесAnible.ИхподдерживаетосновнаякомандаразработчиковAnsible.extra-этомодулинаданныймоментустанавливаютсясAnsible,нонетгарантии,чтоониидальшебудутустанавливатьсясAnsible.Возможно,вбудущем,ихнужнобудетустанавливатьотдельно.Большинствоэтихмодулейподдерживаютсясообществом.
Модули
481
ТакжевAnsibleмодулиразделеныпофункциональности.Списоквсехкатегорийнаходитсявдокументации.
Модули
482
ОсновыplaybooksPlaybook(файлсценариев)—этофайлвкоторомописываютсядействия,которыенужновыполнитьнакакой-тогруппехостов.
Внутриplaybook:
play-этонаборзадач,которыенужновыполнитьдлягруппыхостовtask-этоконкретнаязадача.Взадачеесть,какминимум:
описание(названиезадачиможнонеписать,нооченьрекомендуется)модульикоманда(действиевмодуле)
СинтаксисplaybookPlaybookописываютсявформатеYAML.
СинтаксисYAMLописанвразделеYAMLиливдокументацииAnsible.
Примерсинтаксисаplaybook
Всепримерыэтогоразделанаходятсявкаталоге2_playbook_basics
Примерplabook1_show_commands_with_raw.yml:
Основыplaybook
483
---
-name:Runshowcommandsonrouters
hosts:cisco-routers
gather_facts:false
tasks:
-name:runshipintbr
raw:shipintbr|exunass
-name:runshiproute
raw:shiproute
-name:Runshowcommandsonswitches
hosts:cisco-switches
gather_facts:false
tasks:
-name:runshintstatus
raw:shintstatus
-name:runshvlan
raw:showvlan
Вplaybookдвасценария(play):
name:Runshowcommandsonrouters-имясценария(play).Этотпараметробязательнодолженбытьвлюбомсценарииhosts:cisco-routers-сценарийбудетприменятьсякустройствамвгруппеcisco-routers
тутможетбытьуказаноинесколькогрупп,например,такимобразом:hosts:cisco-routers:cisco-switches.Подробнее,вдокументации
обычно,вplayнадоуказыватьпараметрremote_user.Но,таккакмыуказалиеговконфигурационномфайлеAnsible,можнонеуказыватьеговplay.gather_facts:false-отключениесборафактовобустройстве,таккакдлясетевогооборудованиянадоиспользоватьотдельныемодулидлясборафактов.
вразделеконфигурационныйфайлрассматривалоськакотключитьсборфактовпоумолчанию.Еслионотключен,топараметрgather_factsвplayненужноуказывать.
tasks:-дальшеидетпереченьзадачвкаждойзадаченастроеноимя(опционально)идействие.Действиеможетбытьтолькоодно.вдействииуказываетсякакоймодульиспользоватьипараметрымодуля.
Основыplaybook
484
Итотжеplaybookсотображениемэлементов:
Таквыглядитвыполнениеplaybook:
$ansible-playbook1_show_commands_with_raw.yml
Основыplaybook
485
Обратитевнимание,чтодлязапускаplaybookиспользуетсядругаякоманда.Дляad-hocкоманды,использоваласькомандаansible.Адляplaybook-ansible-playbook.
Длятого,чтобыубедиться,чтокоманды,которыеуказанывзадачах,выполнилисьнаустройствах,запуститеplaybookсопцией-v(выводсокращен):
$ansible-playbook1_show_commands_with_raw.yml-v
Основыplaybook
486
Вследующихразделахмынаучимсяотображатьэтиданныевнормальномформатеипосмотрим,чтоснимиможноделать.
Порядоквыполнениязадачисценариев
Сценарии(play)изадачи(task)выполняютсяпоследовательно,втомпорядке,вкоторомониописанывplaybook.
Есливсценарии,например,двезадачи,тосначалаперваязадачадолжнабытьвыполненадлявсехустройств,которыеуказанывпараметреhosts.Толькопослетого,какперваязадачабылавыполненадлявсеххостов,начинаетсявыполнениевторойзадачи.
Есливходевыполненияplaybook,возниклаошибкавзадаченакаком-тоустройстве,этоустройствоисключается,идругиезадачинанемвыполнятьсянебудут.
Например,заменимпарольпользователяciscoнаcisco123(правильныйcisco)намаршрутизаторе192.168.100.1,изапустимplaybookзаново:
$ansible-playbook1_show_commands_with_raw.yml
Основыplaybook
487
Обратитевниманиенаошибкуввыполнениипервойзадачидлямаршрутизатора192.168.100.1.
Вовторойзадаче'TASK[runshiproute]',Ansibleужеисключилмаршрутизаторивыполняетзадачутолькодлямаршрутизаторов192.168.100.2и192.168.100.3.
Ещеодинважныйаспект-Ansibleвыдалсообщение:
toretry,use:--limit@/home/nata/pyneng_course/chapter15/1_show_commands_with_raw.re
try
Если,привыполненииplaybook,накаком-тоустройствевозниклаошибка,Ansibleсоздаетспециальныйфайл,которыйназываетсяточнотакжекакplaybook,норасширениеменяетсянаretry.(Есливывыполняетезаданияпараллельно,тоэтотфайлдолженпоявитьсяувас)
Вэтомфайлехранитсяимяилиадресустройстванакоторомвозниклаошибка.Таквыглядитфайл1_show_commands_with_raw.retryсейчас:
192.168.100.1
Создаетсяэтотфайлдлятого,чтобыможнобылоперезапуститьplaybookзановотолькодляпроблемногоустройства(устройств).Тоесть,надоисправитьпроблемусустройством,изановозапуститьplaybook.
Настраиваемправильныйпарольнамаршрутизаторе192.168.100.1,азатемперезапускаемplaybookтакимобразом:
$ansible-playbook1_show_commands_with_raw.yml--limit@/home/nata/pyneng_course/chap
ter15/1_show_commands_with_raw.retry
Ansibleвзялсписокустройств,которыеперечисленывфайлеretryивыполнилplaybookтолькодляних.
Основыplaybook
488
Можнобылозапуститьplaybookитак(тоесть,писатьнеполныйпутькфайлуretry):
$ansible-playbook1_show_commands_with_raw.yml--limit@1_show_commands_with_raw.retr
y
Параметр--limitоченьполезнаявещь.Онпозволяетограничивать,длякакиххостовилигруппбудетвыполнятьсяplaybook,приэтом,неменяясамplaybook.
Например,такимобразомplaybookможнозапуститьтолькодлямаршрутизатора192.168.100.1:
$ansible-playbook1_show_commands_with_raw.yml--limit192.168.100.1
Идемпотентность
МодулиAnsibleидемпотентны.Этоозначает,чтомодульможновыполнятьсколькоугоднораз,ноприэтоммодульбудетвыполнятьизменения,толькоеслисистеманенаходитсявжелаемомсостоянии.
Но,естьисключенияизтакогоповедения.Например,модульrawвсегдавноситизменения.Поэтомуввыполненииplaybookвыше,всегдаотображалосьсостояниеchanged.
Но,если,например,взадачеуказано,чтонасерверLinuxнадоустановитьпакетhttpd,тоонбудетустановлентольковтомслучае,еслиегонет.Тоесть,действиенебудетповторятьсясноваиснова,прикаждомзапуске.Алишьтогда,когдапакетанет.
Аналогично,иссетевымоборудованием.Еслизадачамодулявыполнитькомандувконфигурационномрежиме,аонаужеестьнаустройстве,модульнебудетвноситьизменения.
Основыplaybook
489
ПеременныеПеременнойможетбыть,например:
информацияобустройстве,котораясобранакакфакт,азатемиспользуетсявшаблоне.впеременныеможнозаписыватьполученныйвыводкоманды.переменнаяможетбытьуказанавручнуювplaybook
ИменапеременныхВAnsibleестьопределенныеограниченияпоформатуименпеременных:
Переменныемогутсостоятьизбукв,чиселисимвола_Переменныедолжныначинатьсясбуквы
Крометого,можносоздаватьсловариспеременными(вформатеYAML):
R1:
IP:10.1.1.1/24
DG:10.1.1.100
Обращатьсякпеременнымвсловареможнодвумявариантами:
R1['IP']
R1.IP
Правда,прииспользованиивтороговарианта,могутбытьпроблемы,еслиназваниеключасовпадаетсзарезервированнымсловом(методомилиатрибутом)вPythonилиAnsible.
ГдеможноопределятьпеременныеПеременныеможносоздавать:
винвентарномфайлевplaybookвспециальныхфайлахдлягруппы/устройствавотдельныхфайлах,которыедобавляютсявplaybookчерезinclude(каквJinja2)
Переменные
490
вролях,которыезатемиспользуютсяможнодажепередаватьпеременныепривызовеplaybook
Такжеможноиспользоватьфакты,которыебылисобраныпроустройство,какпеременные.
Переменныевинвентарномфайле
Винвентарномфайлеможноуказыватьпеременныедлягруппы:
[cisco-routers]
192.168.100.1
192.168.100.2
192.168.100.3
[cisco-switches]
192.168.100.100
[cisco-routers:vars]
ntp_server=192.168.255.100
log_server=10.255.100.1
Переменыеntp_serverиlog_serverотносятсякгруппеcisco-routersимогутиспользоваться,например,пригенерацииконфигурации,наосновешаблона.
Переменныевplaybook
Переменныеможнозадаватьпрямовplaybook.Этоможетбытьудобнотем,чтопеременныенаходятсятамже,гдевседействия.
Например,можнозадатьпеременныеntp_serverиlog_serverвplaybookтакимобразом:
Переменные
491
---
-name:Runshowcommandsonrouters
hosts:cisco-routers
gather_facts:false
vars:
ntp_server:192.168.255.100
log_server:10.255.100.1
tasks:
-name:runshipintbr
raw:shipintbr|exunass
-name:runshiproute
raw:shiproute
Переменныевспециальныхфайлахдлягруппы/устройства
Ansibleпозволяетхранитьпеременныедлягруппы/устройствавспециальныхфайлах:
Длягруппустройств,переменныедолжнынаходитьсявкаталогеgroup_vars,вфайлах,которыеназываются,какимягруппы.
Крометого,можносоздаватьвкаталогеgroup_varsфайлall,вкоторомбудутнаходитьсяпеременные,которыеотносятсяковсемгруппам.
Дляконкретныхустройств,переменныедолжнынаходитьсявкаталогеhost_vars,вфайлах,которыесоответствуютимениилиадресухоста.Всефайлыспеременными,должныбытьвформатеYAML.Расширениефайламожетбытьтаким:yml,yaml,jsonилибезрасширениякаталогиgroup_varsиhost_varsдолжнынаходитьсявтомжекаталоге,чтоиplaybook.Илимогутнаходитьсявнутрикаталогаinventory(первыйвариантболеераспространенный).
есликаталогиифайлыназваныправильноирасположенывуказанныхкаталогах,Ansibleсамразпознаетфайлыибудетиспользоватьпеременные
Например,еслиинвентарныйфайлmyhostsвыглядиттак:
Переменные
492
[cisco-routers]
192.168.100.1
192.168.100.2
192.168.100.3
[cisco-switches]
192.168.100.100
Можносоздатьтакуюструктурукаталогов:
├──group_vars_
│├──all.yml|
│├──cisco-routers.yml|Каталогспеременнымидлягруппустройств
│└──cisco-switches.yml_|
|
├──host_vars_
│├──192.168.100.1|
│├──192.168.100.2|
│├──192.168.100.3|Каталогспеременнымидляустройств
│└──192.168.100.100_|
|
└──myhosts|Инвентарныйфайл
Нижепримерсодержимогофайловпеременныхдлягруппустройствидляотдельныххостов.
group_vars/all.yml(вэтомфайлеуказываютсязначенияпоумолчанию,которыеотносятсяковсемустройствам):
---
cli:
host:"{{inventory_hostname}}"
username:"cisco"
password:"cisco"
transport:cli
authorize:yes
auth_pass:"cisco"
Вданномслучае,указываютсяпеременные,которыепредопределенысамимAnsible.
Вфайлеgroup_vars/all.ymlсоздансловарьcli.Вэтомсловареперечисленытеаргументы,которыедолжнызадаватьсядляработыссетевымоборудованиемчерезвстроенныемодулиAnsible(рассматриваетсявразделесетевыемодули)
Интересныймоментвэтомфайле-переменнаяhost:"{{inventory_hostname}}":
Переменные
493
inventory_hostname-этоспециальнаяпеременная,котораяуказываетнатотхост,длякоторогоAnsibleвыполняетдействия.синтаксис{{inventory_hostname}}-этоподстановкапеременных.ИспользуетсяформатJinja
group_vars/cisco-routers.yml
---
log_server:10.255.100.1
ntp_server:10.255.100.1
users:
user1:pass1
user2:pass2
user3:pass3
Вфайлеgroup_vars/cisco-routers.ymlнаходятсяпеременные,которыеуказываютIP-адресLogиNTPсерверовинесколькихпользователей.Этипеременныемогутиспользоваться,например,вшаблонахконфигурации.
group_vars/cisco-switches.yml
---
vlans:
-10
-20
-30
Вфайлеgroup_vars/cisco-switches.ymlуказанапеременнуаяvlansсоспискомVLANов.
Файлыспеременнымидляхостоводнотипныивнихменяютсятолькоадресаиимена:
Файлhost_vars/192.168.100.1
---
hostname:london_r1
mgmnt_loopback:100
mgmnt_ip:10.0.0.1
ospf_ints:
-192.168.100.1
-10.0.0.1
-10.255.1.1
Переменные
494
Файлhost_vars/192.168.100.2
---
hostname:london_r2
mgmnt_loopback:100
mgmnt_ip:10.0.0.2
ospf_ints:
-192.168.100.2
-10.0.0.2
-10.255.2.2
Файлhost_vars/192.168.100.3
---
hostname:london_r3
mgmnt_loopback:100
mgmnt_ip:10.0.0.3
ospf_ints:
-192.168.100.3
-10.0.0.3
-10.255.3.3
Файлhost_vars/192.168.100.100
---
hostname:london_sw1
mgmnt_int:VLAN100
mgmnt_ip:10.0.0.100
ПриоритетностьпеременныхВэтомразделенерассматриваетсяразмещениепеременных:
вотдельныхфайлах,которыедобавляютсявplaybookчерезinclude(каквJinja2)вролях,которыезатемиспользуютсяпередачапеременныхпривызовеplaybook
ЭторассматриваетсявкурсеAnsibleдлясетевыхинженеров
Переменные
495
Чащевсего,переменнаясопределеннымименемтолькоодна.Но,иногдаможетпонадобитьсясоздатьпеременнуювразныхместахитогданужнопонимать,вкакомпорядкеAnsibleперезаписываетпеременные.
Приоритетпеременных(последниезначенияпереписываютпредыдущие):
Значенияпеременныхвроляхзадачивроляхбудутвидетьсобственныезначения.Задачи,которыеопределенывнероли,будутвидетьпоследниезначенияпеременныхроли
переменныевинвентарномфайлепеременныедлягруппыхостоввинвентарномфайлепеременныедляхостоввинвентарномфайлепеременныевкаталогеgroup_varsпеременныевкаталогеhost_varsфактыхостапеременныесценария(play)переменныесценария,которыезапрашиваютсячерезvars_promptпеременные,которыепередаютсявсценарийчерезvars_filesпеременныеполученныечерезпараметрregisterset_factsпеременныеизролиипомещенныечерезincludeпеременныеблока(переписываютдругиезначениятолькодляблока)переменныезадачи(task)(переписываютдругиезначениятолькодлязадачи)переменные,которыепередаютсяпривызовеplaybookчерезпараметр--extra-vars(всегданаиболееприоритетные)
Переменные
496
РаботасрезультатамивыполнениямодуляВэтомразделерассматриваютсянесколькоспособов,которыепозволяютпосмотретьнавывод,полученныйсустройств.
Примерыиспользуютмодульraw,ноаналогичныепринципыработаютисдругимимодулями.
verbose
Впредыдущихразделах,одинизспособовотобразитьрезультатвыполнениякоманд,ужеиспользовался-флагverbose.
Конечно,выводнеоченьудобночитать,но,какминимум,онпозволяетувидеть,чтокомандывыполнились.ТакжеэтотфлагпозволяетподробнопосмотретькакиешагивыполняетAnsible.
Примерзапускаplaybookсфлагомverbose(выводсокращен):
ansible-playbook1_show_commands_with_raw.yml-v
Приувеличенииколичествабуквvвфлаге,выводстановитсяболееподробным.Попробуйтевызыватьэтотжеplaybookидобавлятькфлагубуквыv(5ибольшепоказываютодинаковыйвывод),такимобразом:
ansible-playbook1_show_commands_with_raw.yml-vvv
Ввыводевиднырезультатывыполнениязадачи,онивозвращаютсявформатеJSON:
Результатвыполнениямодуля
497
changed-ключ,которыйуказываетбылиливнесеныизмененияrc-returncode.Этополепоявляетсяввыводетехмодулей,которыевыполняюткакие-токомандыstderr-ошибки,привыполнениикоманды.Этополепоявляетсяввыводетехмодулей,которыевыполняюткакие-токомандыstdout-выводкомандыstdout_lines-выводввидеспискакоманд,разбитыхпострочно
register
Параметрregisterсохраняетрезультатвыполнениямодулявпеременную.Затемэтапеременнаяможетиспользоватьсявшаблонах,впринятиирешенийоходесценарияилидляотображениявывода.
Попробуемсохранитьрезультатвыполнениякоманды.
Вplaybook2_register_vars.yml,спомощьюregister,выводкомандыshipintbrсохраненвпеременнуюsh_ip_int_br_result:
---
-name:Runshowcommandsonrouters
hosts:cisco-routers
gather_facts:false
tasks:
-name:runshipintbr
raw:shipintbr|exunass
register:sh_ip_int_br_result
Еслизапуститьэтотplaybook,выводнебудетотличаться,таккаквыводтолькозаписанвпеременную,носпеременнойневыполяетсяникакихдействий.Следующийшаг-отобразитьрезультатвыполнениякоманды,спомощьюмодуляdebug.
debug
Модульdebugпозволяетотображатьинформациюнастандартныйпотоквывода.Этоможетбытьпроизвольнаястрока,переменная,фактыобустройстве.
Дляотображениясохраненныхрезультатоввыполнениякоманды,вplaybook2_register_vars.ymlдобавленазадачасмодулемdebug:
Результатвыполнениямодуля
498
---
-name:Runshowcommandsonrouters
hosts:cisco-routers
gather_facts:false
tasks:
-name:runshipintbr
raw:shipintbr|exunass
register:sh_ip_int_br_result
-name:Debugregisteredvar
debug:var=sh_ip_int_br_result.stdout_lines
Обратитевнимание,чтовыводитсяневсёсодержимоепеременнойsh_ip_int_br_result,атолькосодержимоеstdout_lines.Вsh_ip_int_br_result.stdout_linesнаходитсясписокстрок,поэтомувыводбудутструктурирован.
Результатзапускаplaybookвыглядиттак:
$ansible-playbook2_register_vars.yml
Результатвыполнениямодуля
499
register,debug,when
Спомощьюключевогословаwhen,можноуказатьусловие,привыполнениикоторого,задачавыполняется.Еслиусловиеневыполняется,тозадачапропускается.
whenвAnsibleиспользуетсякакifвPython.
Примерplaybook3_register_debug_when.yml:
Результатвыполнениямодуля
500
---
-name:Runshowcommandsonrouters
hosts:cisco-routers
gather_facts:false
tasks:
-name:runshipintbr
raw:shipintbri|exunass
register:sh_ip_int_br_result
-name:Debugregisteredvar
debug:
msg:"Errorincommand"
when:"'invalid'insh_ip_int_br_result.stdout"
Впоследнемзаданиинесколькоизменений:
модульdebugотображаетнесодержимоесохраненнойпеременной,асообщение,котороеуказановпеременнойmsg.условиеwhenуказывает,чтоданнаязадачавыполнитсятолькопривыполненииусловия
when:"'invalid'insh_ip_int_br_result.stdout"-этоусловиеозначает,чтозадачабудетвыполненатольковтомслучае,есливвыводеsh_ip_int_br_result.stdoutбудетнайденастрокаinvalid(например,когданеправильновведенакоманда)
Модули,которыеработаютссетевымоборудованием,автоматическипроверяютошибки,привыполнениикоманд.ТутэтотпримериспользуетсядлядемонстрациивозможностейAnsible.
Выполнениеplaybook:
$ansible-playbook3_register_debug_when.yml
Результатвыполнениямодуля
501
Обратитевниманиенасообщенияskipping-этоозначает,чтозадачаневыполняласьдляуказанныхустройств.Невыполниласьонапотому,чтоусловиевwhenнебыловыполнено.
Выполнениетогожеplaybook,носошибкойвкоманде:
---
-name:Runshowcommandsonrouters
hosts:cisco-routers
gather_facts:false
tasks:
-name:runshipintbr
raw:shhipintbri|exunass
register:sh_ip_int_br_result
-name:Debugregisteredvar
debug:
msg:"Errorincommand"
when:"'invalid'insh_ip_int_br_result.stdout"
Теперьрезультатвыполнениятакой:
$ansible-playbook3_register_debug_when.yml
Результатвыполнениямодуля
502
Таккаккомандабыласошибкой,сработалоусловие,котороеописановwhenизадачавывеласообщение,спомощьюмодуляdebug.
Результатвыполнениямодуля
503
МодулидляработыссетевымоборудованиемВпредыдущихразделахдляотправкикоманднаоборудование,использовалсямодульraw.Онуниверсаленисегопомощьюможноотправлятькомандыналюбоеустройство.
Вэтомразделе,рассматриваютсямодули,которыеработаютссетевымоборудованием.
Глобально,модулидляработыссетевымоборудованием,можноразделитьнадвечасти:
модулидляоборудованиясподдержкойAPIмодулидляоборудования,котороеработаеттолькочерезCLI
ЕслиоборудованиеподдерживаетAPI,какнапример,NXOS,тодлянегосозданобольшоеколичествомодулей,которыевыполняютконкретныедействия,понастройкефункционала(например,дляNXOSсозданоболее60модулей).
Дляоборудования,котороеработаеттолькочерезCLI,Ansibleподдерживаеттакиетритипамодулей:
os_command-выполняеткомандыshowos_facts-собираетфактыобустройствахos_config-выполняеткомандыконфигурации
Соответственно,дляразныхоперационныхсистем,будутразныемодули.Например,дляCiscoIOS,модулибудутназываться:
ios_commandios_configios_facts
АналогичныетримодулядоступныдлятакихОС:
Dellos10Dellos6Dellos9EOSIOSIOSXRJUNOS
Сетевыемодули
504
SROSVyOS
Полныйсписоквсехсетевыхмодулей,которыеподдерживаетAnsibleвдокументации.
Обратитевнимание,чтоAnsibleоченьактивноразвиваетсявсторонуподдержкиработыссетевымоборудованием,ивследующейверсииAnsible,могутбытьдополнительныемодули.Поэтому,еслинамоментчтениякурса,ужеестьследующаястабильнаяверсияAnsible(версиявкурсе2.2),используйтееёипосмотритевдокументации,какиеновыевозможностиимодулипоявились.
Вкурсе,всерассматриваетсянапримеремодулейдляработысCiscoIOS:
ios_commandios_configios_facts
Аналогичныемодулиcommand,configиfactsдлядругихвендоровиОСработаютодинаково,поэтому,еслиразобратьсякакработатьсмодулямидляIOS,состальнымивсёбудетаналогично.
Крометого,вкурсерассматриваетсямодульntc-ansible,которыйневходитвcoreмодулиAnsible.
Вариантыподключения
Ansibleподдерживаеттакиетипыподключений:
paramikoSSH-OpenSSH.Используетсяпоумолчаниюlocal-действиявыполняютсялокально,науправляющемхосте
ПриподключениипоSSH,поумолчаниюиспользуютсяSSHключи,номожнопереключитьсянаиспользованиепаролей.
Поумолчанию,AnsibleзагружаетмодульPythonнаустройство,длятого,чтобывыполнитьдействия.ЕслижеоборудованиенеподдерживаетPython,каквслучаесдоступомксетевомуоборудованиючерезCLI,нужноуказать,чтомодульдолжензапускатьсялокально,науправляющемхостеAnsible.
Особенностиподключенияксетевомуоборудованию
Приработессетевымоборудованием,естьнесколькопараметроввplaybook,которыенужноменять:
Сетевыемодули
505
gather_facts-надоотключить,таккакдлясетевогооборудованияиспользуютсясвоимодулисборафактовconnection-управляеттем,какименнобудетпроисходитьподключение.Длясетевогооборудованиянеобходимоустановитьвlocal
Тоесть,длякаждогосценария(play),нужноуказывать:
gather_facts:falseconnection:local
Пример:
---
-name:Runshowcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
ВAnsibleпеременныеможноуказыватьвразныхместах,поэтомутеженастройкиможноуказатьпо-другому.
Например,вразделеоконфигурационномфайлерассматривалоськакотключитьсборфактовпоумолчанию(файлansible.cfg):
[defaults]
gathering=explicit
Такойвариантподходитвтомслучае,когдаAnsibleиспользуетсябольшедляподключенияксетевымустройствам(или,локальныеplaybookиспользуютсядляподключенияксетевомуоборудованию).
Втакомслучае,нужнобудетнаоборотявновключатьсборфактов,еслионнужен.
Указать,чтонужноиспользоватьлокальноеподключение,такжеможнопо-разному.
Винвентарномфайле:
Сетевыемодули
506
[cisco-routers]
192.168.100.1
192.168.100.2
192.168.100.3
[cisco-switches]
192.168.100.100
[cisco-routers:vars]
ansible_connection=local
Иливфайлахпеременных,например,вgroup_vars/all.yml:
---
ansible_connection:local
Вследующихразделахбудетиспользоватьсятакойвариант:
---
-name:Runshowcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
Вреальнойжизнинужновыбратьтотвариант,которыйнаиболееудобендляработы.
Аргументprovider
Модули,которыеиспользуютсядляработыссетевымоборудованием,требуютзаданиянесколькихаргументов.
Длякаждойзадачидолжныбытьуказанытакиеаргументы:
host-имяилиIP-адресудаленногоустройстваport-ккакомупортуподключатьсяusername-имяпользователяpassword-парольtransport-типподключения:CLIилиAPI.Поумолчанию-cliauthorize-нужнолипереходитьвпривилегированныйрежим(enable,дляCisco)auth_pass-парольдляпривилегированногорежима
ЕслидляподключенияAnsibleсозданотдельныйпользовательсprivilege15,можнонеиспользоватьпараметрыauthorizeиauth_pass.
Сетевыемодули
507
Но,Ansibleтакжепозволяетсобратьихводинаргумент-provider.
Примерзаданиявсехаргументоввзадаче(task):
tasks:
-name:runshowversion
ios_command:
commands:showversion
host:"{{inventory_hostname}}"
username:cisco
password:cisco
transport:cli
Аргументысозданыкакпеременнаяcliвplaybook,азатемпередаютсякакпеременнаяаргументуprovider:
vars:
cli:
host:"{{inventory_hostname}}"
username:cisco
password:cisco
transport:cli
tasks:
-name:runshowversion
ios_command:
commands:showversion
provider:"{{cli}}"
И,самыйудобныйвариант,задаватьаргументывкаталогеgroup_vars.
Например,еслиувсехустройстводинаковыезначенияаргументов,можнозадатьихвфайлеgroup_vars/all.yml:
---
cli:
host:"{{inventory_hostname}}"
username:cisco
password:cisco
transport:cli
authorize:yes
auth_pass:cisco
Затемпеременнаяиспользуетсявplaybookтакже,какивслучаеуказанияпеременныхвplaybook:
Сетевыемодули
508
tasks:
-name:runshowversion
ios_command:
commands:showversion
provider:"{{cli}}"
Крометого,Ansibleподдерживаетзаданиепараметроввпеременныхокружения:
ANSIBLE_NET_USERNAME-дляпеременнойusernameANSIBLE_NET_PASSWORD-passwordANSIBLE_NET_SSH_KEYFILE-ssh_keyfileANSIBLE_NET_AUTHORIZE-authorizeANSIBLE_NET_AUTH_PASS-auth_pass
Приоритетностьзначенийвпорядкевозрастанияприоритетности:
значенияпоумолчаниюзначенияпеременныхокруженияпараметрproviderаргументызадачи(task)
Подготовкакработессетевымимодулями
Вследующихразделахрассматриваетсяработасмодулямиios_command,ios_factsиios_config.Длятого,чтобывсепримерыplaybookработали,надосоздатьнесколькофайлов(проверить,чтоониесть).
Инвентарныйфайлmyhosts:
[cisco-routers]
192.168.100.1
192.168.100.2
192.168.100.3
[cisco-switches]
192.168.100.100
Конфигурационныйфайлansible.cfg:
[defaults]
inventory=./myhosts
remote_user=cisco
ask_pass=True
Сетевыемодули
509
Вфайлеgroup_vars/all.ymlнадосоздатьпеременнуюcli,чтобынеуказыватькаждыйразвсепараметры,которыенужнопередатьаргументуprovider:
---
cli:
host:"{{inventory_hostname}}"
username:"cisco"
password:"cisco"
transport:cli
authorize:yes
auth_pass:"cisco"
Сетевыемодули
510
Модульios_commandМодульios_command-отправляеткомандуshowнаустройствоподуправлениемIOSивозвращаетрезультатвыполнениякоманды.
Модульios_commandнеподдерживаетотправкукомандвконфигурационномрежиме.Дляэтогоиспользуетсяотдельныймодуль-ios_config.
Передотправкойсамойкоманды,модуль:
выполняетаутентификациюпоSSH,переходитврежимenableвыполняеткомандуterminallength0,чтобывыводкомандshowотражалсяполностью,анепостранично.
Примериспользованиямодуляios_command(playbook1_ios_command.yml):
---
-name:Runshowcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:runshipintbr
ios_command:
commands:showipintbr
provider:"{{cli}}"
register:sh_ip_int_br_result
-name:Debugregisteredvar
debug:var=sh_ip_int_br_result.stdout_lines
Модульios_commandожидаетпараметры:
commands-списоккоманд,которыенужноотправитьнаустройствоprovider-словарьспараметрамиподключения
внашемслучае,онуказанвфайлеgroup_vars/all.yml
Обратитевнимание,чтопараметрregisterнаходитсянаодномуровнесименемзадачиимодулем,аненауровнепараметровмодуляios_command.
ios_command
511
Результатвыполненияplaybook:
$ansible-playbook1_ios_command.yml
Вотличиеотиспользованиямодуляraw,playbookнеуказывает,чтобыливыполненыизменения.
Выполнениенесколькихкоманд
ios_command
512
Модульios_commandпозволяетвыполнятьнесколькокоманд.
Playbook2_ios_command.ymlвыполняетнесколькокомандиполучаетихвывод:
---
-name:Runshowcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:runshowcommands
ios_command:
commands:
-showipintbr
-shiproute
provider:"{{cli}}"
register:show_result
-name:Debugregisteredvar
debug:var=show_result.stdout_lines
Впервойзадачеуказываютсядвекоманды,поэтомусинтаксисдолженбытьнемногодругим-командыдолжныбытьуказаныкаксписок,вформатеYAML.
Результатвыполненияplaybook(выводсокращен):
$ansible-playbook2_ios_command.yml
ios_command
513
Обекомандывыполнилисьнавсехустройствах.
Еслимодулюпередаютсянесколькокоманд,результатвыполнениякоманднаходитсявпеременныхstdoutиstdout_linesвсписке.Выводбудетвтомпорядке,вкоторомкомандыописанывзадаче.
Засчетэтого,например,можновывестирезультатвыполненияпервойкоманды,указав:
-name:Debugregisteredvar
debug:var=show_result.stdout_lines[0]
Обработкаошибок
ios_command
514
Вмодулевстроенораспознаниеошибок.Поэтому,есликомандавыполненасошибкой,модульотобразит,чтовозниклаошибка.
Например,еслисделатьошибкувкоманде,изапуститьplaybookещераз
$ansible-playbook2_ios_command.yml
Ansibleобнаружилошибкуивозвращаетсообщениеошибки.Вданномслучае-'Invalidinput'.
Аналогичнымобразоммодульобнаруживаетошибки:
AmbiguouscommandIncompletecommand
ios_command
515
Модульios_factsМодульios_facts-собираетинформациюсустройствподуправлениемIOS.
Информацияберетсяизтакихкоманд:
dirshowversionshowmemorystatisticsshowinterfacesshowipv6interfaceshowlldpshowlldpneighborsdetailshowrunning-config
Длятого,чтобывидетькакиекомандыAnsibleвыполняетнаоборудовании,можнонастроитьEEMapplet,которыйбудетгенерироватьлогсообщенияовыполненныхкомандах.
Вмодулеможноуказыватькакиепараметрысобирать-можнособиратьвсюинформацию,аможнотолькоподмножество.Поумолчанию,модульсобираетвсюинформацию,кромеконфигурационногофайла.
Какуюинформациюсобирать,указываетсявпараметреgather_subset.Поддерживаютсятакиеварианты(указанытакжекоманды,которыебудутвыполнятьсянаустройстве):
allhardware
dirshowversionshowmemorystatistics
configshowversionshowrunning-config
interfacesdirshowversionshowinterfacesshowipv6interfaceshowlldp
ios_facts
516
showlldpneighborsdetail
Собратьвсефакты:
-ios_facts:
gather_subset:all
provider:"{{cli}}"
Собратьтолькоподмножествоinterfaces:
-ios_facts:
gather_subset:
-interfaces
provider:"{{cli}}"
Собратьвсё,кромеhardware:
-ios_facts:
gather_subset:
-"!hardware"
provider:"{{cli}}"
Ansibleсобираеттакиефакты:
ansible_net_all_ipv4_addresses-списокIPv4адресовнаустройствеansible_net_all_ipv6_addresses-списокIPv6адресовнаустройствеansible_net_config-конфигурация(дляCiscoshrun)ansible_net_filesystems-файловаясистемаустройстваansible_net_gather_subset-какаяинформациясобирается(hardware,default,interfaces,config)ansible_net_hostname-имяустройстваansible_net_image-имяипутьОСansible_net_interfaces-словарьсовсемиинтерфейсамиустройства.Именаинтерфейсов-ключи,аданные-параметрыкаждогоинтерфейсаansible_net_memfree_mb-сколькосвободнойпамятинаустройствеansible_net_memtotal_mb-сколькопамятинаустройствеansible_net_model-модельустройстваansible_net_serialnum-серийныйномерansible_net_version-версияIOS
Использованиемодуля
ios_facts
517
Примерplaybook1_ios_facts.ymlсиспользованиеммодуляios_facts(собираютсявсефакты):
---
-name:CollectIOSfacts
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:Facts
ios_facts:
gather_subset:all
provider:"{{cli}}"
$ansible-playbook1_ios_facts.yml
Длятого,чтобыпосмотреть,какиеименнофактысобираютсясустройства,можнодобавитьфлаг-v(информациясокращена):
$ansible-playbook1_ios_facts.yml-v
Using/home/nata/pyneng_course/chapter15/ansible.cfgasconfigfile
ios_facts
518
Послетого,какAnsibleсобралфактысустройства,всефактыдоступныкакпеременныевplaybook,шаблонахит.д.
Например,можноотобразитьсодержимоефактаспомощьюdebug(playbook2_ios_facts_debug.yml):
---
-name:CollectIOSfacts
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:Facts
ios_facts:
gather_subset:all
provider:"{{cli}}"
-name:Showansible_net_all_ipv4_addressesfact
debug:var=ansible_net_all_ipv4_addresses
-name:Showansible_net_interfacesfact
debug:var=ansible_net_interfaces['Ethernet0/0']
Результатвыполненияplaybook:
$ansible-playbook2_ios_facts_debug.yml
ios_facts
519
Сохранениефактов
Втомвиде,вкотороминформацияотображаетсяврежимеverbose,довольносложнопонятькакаяинформациясобираетсяобустройствах.Длятого,чтобылучшепонятькакаяинформациясобираетсяобустройствах,вкакомформате,скопируемполученнуюинформациювфайл.
Дляэтогобудетиспользоватьсямодульcopy.
Playbook3_ios_facts.ymlсобираетвсюинформациюобустройствахизаписываетвразныефайлы(создайтекаталогall_factsпередзапускомplaybookилираскомментируйтезадачуCreateall_factsdirиAnsibleсоздасткаталогсам):
ios_facts
520
---
-name:CollectIOSfacts
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:Facts
ios_facts:
gather_subset:all
provider:"{{cli}}"
register:ios_facts_result
#-name:Createall_factsdir
#file:
#path:./all_facts/
#state:directory
#mode:0755
-name:Copyfactstofiles
copy:
content:"{{ios_facts_result|to_nice_json}}"
dest:"all_facts/{{inventory_hostname}}_facts.json"
Модульcopyпозволяеткопироватьфайлысуправляющегохоста(накоторомустановленAnsible)наудаленныйхост.Но,таккаквэтомслучае,указанпараметрconnection:local,файлыбудутскопированыналокальныйхост.
Чащевсего,модульcopyиспользуетсятакимобразом:
-copy:
src:/srv/myfiles/foo.conf
dest:/etc/foo.conf
Но,вданномслучае,нетисходногофайла,содержимоекоторогонужноскопировать.Вместоэтого,естьсодержимоепеременнойios_facts_result,котороенужноперенестивфайлall_facts/{{inventory_hostname}}_facts.json.
Длятогочтобыперенестисодержимоепеременнойвфайл,вмодулеcopy,вместоsrc,используетсяпараметрcontent.
Встрокеcontent:"{{ios_facts_result|to_nice_json}}"
параметрto_nice_json-этофильтрJinja2,которыйпреобразуетинформациюпеременнойвформат,вкоторомудобнейчитатьинформациюпеременнаявформатеJinja2должнабытьзаключенавдвойныефигурные
ios_facts
521
скобки,атакжеуказанавдвойныхкавычках
Таккаквпутиdestиспользуютсяименаустройств,будутсгенерированыуникальныефайлыдлякаждогоустройства.
Результатвыполненияplaybook:
$ansible-playbook3_ios_facts.yml
Послеэтого,вкаталогеall_factsнаходятсятакиефайлы:
192.168.100.1_facts.json
192.168.100.2_facts.json
192.168.100.3_facts.json
Содержимоефайлаall_facts/192.168.100.1_facts.json:
{
"ansible_facts":{
"ansible_net_all_ipv4_addresses":[
"192.168.200.1",
"192.168.100.1",
"10.1.1.1"
],
"ansible_net_all_ipv6_addresses":[],
"ansible_net_config":"Buildingconfiguration...\n\nCurrentconfiguration:
...
ios_facts
522
Сохранениеинформацииобустройствах,нетолькопоможетразобраться,какаяинформациясобирается,ноиможетбытьполезнымдлядальнейшегоиспользованияинформации.Например,можноиспользоватьфактыобустройствевшаблоне.
Приповторномвыполненииplaybook,Ansibleнебудетизменятьинформациювфайлах,еслифактыобустройственеизменились
Еслиинформацияизменилась,длясоответствующегоустройства,будетвыставленстатусchanged.Такимобразом,повыполнениюplaybookвсегдапонятно,когдакакая-тоинформацияизменилась.
Повторныйзапускplaybook(безизменений):
$ansible-playbook3_ios_facts.yml
Изменениясопцией--diff
ВAnsibleможнонетолькоувидеть,чтоизмененияпроизошли,ноиувидетькакиеименноизменениябылисделаны.Например,вситуацииссохранениемфактовобустройствеэтоможетбытьоченьполезно.
Примерзапускаplaybookсопцией--diffисвнесеннымиизмененияминаодномизустройств:
$ansible-playbook3_ios_facts.yml--diff--limit=192.168.100.1
ios_facts
523
Вэтомвыводевиднонетолькото,чтобыливнесеныизменения,ноито,накакомустройствеикакиеименноизменения.
ios_facts
524
Модульios_configМодульios_config-позволяетнастраиватьустройстваподуправлениемIOS,атакже,генерироватьшаблоныконфигурацийилиотправлятькомандынаоснованиишаблона.
Параметрымодуля:
after-какиедействиявыполнитьпослекомандbefore-какиедействиявыполнитьдокомандbackup-параметр,которыйуказываетнужнолиделатьрезервнуюкопиютекущейконфигурацииустройствапередвнесениемизменений.Файлбудеткопироватьсявкаталогbackup,относительнокаталогавкоторомнаходитсяplaybookconfig-параметр,которыйпозволяетуказатьбазовыйфайлконфигурации,скоторымбудутсравниватьсяизменения.Еслионуказан,модульнебудетскачиватьконфигурациюсустройства.defaults-параметруказываетнужнолисобиратьвсюинформациюсустройства,втомчисле,изначенияпоумолчанию.Есливключитьэтотпараметр,томодульбудетсобиратьтекущуюкофигурациюспомощьюкомандыshrunall.Поумолчаниюэтотпараметротключениконфигурацияпроверяетсякомандойshrunlines(commands)-списоккоманд,которыедолжныбытьнастроены.Командынужноуказыватьбезсокращенийировновтомвиде,вкоторомонибудутвконфигурации.match-параметруказываеткакименнонужносравниватькомандыparents-названиесекции,вкоторойнужноприменитькоманды.Есликоманданаходитсявнутривложеннойсекции,нужноуказыватьвесьпуть.Еслиэтотпараметрнеуказан,тосчитается,чтокомандадолжныбытьвглобальномрежимеконфигурацииreplace-параметруказываеткаквыполнятьнастройкуустройстваsave-сохранятьлитекущуюконфигурациювстартовую.Поумолчаниюконфигурациянесохраняетсяsrc-параметруказываетпутькфайлу,вкоторомнаходитсяконфигурацияилишаблонконфигурации.Взаимоисключающийпараметрсlines(тоесть,можноуказыватьилиlinesилиsrc).Заменяетмодульios_template,которыйскоробудетудален.
ios_config
525
lines(commands)Самыйпростойспособиспользоватьмодульios_config-отправлятькомандыглобальногоконфигурационногорежимаспараметромlines.
Дляпараметраlinesестьaliascommands,тоесть,можновместоlinesписатьcommands.
Примерplaybook1_ios_config_lines.yml:
---
-name:Runcfgcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:Configpasswordencryption
ios_config:
lines:
-servicepassword-encryption
provider:"{{cli}}"
Используетсяпеременнаяcli,котораяуказанавфайлеgroup_vars/all.yml.
Результатвыполненияplaybook:
$ansible-playbook1_ios_config_lines.yml
Ansibleвыполняеттакиекоманды:
ios_config
526
terminallength0enableshowrunning-config-чтобыпроверитьестьлиэтакоманданаустройстве.Есликомандаесть,задачавыполнятьсянебудет.Есликомандынет,задачавыполнитсяесликоманды,котораяуказанавзадаченетвконфигурации:
configureterminalservicepassword-encryptionend
Таккакмодулькаждыйразпроверяетконфигурацию,преждечемпримениткоманду,модульидемпотентен.Тоесть,еслиещёраззапуститьplaybook,изменениянебудутвыполнены:
$ansible-playbook1_ios_config_lines.yml
Обязательнопишитекомандыполностью,анесокращенно.Иобращайтевнимание,что,длянекоторыхкоманд,IOSсамдобавляетпараметры.Еслиписатькомандуневтомвиде,вкоторомонареальновиднавконфигурационномфайле,модульнебудетидемпотентен.Онбудетвсёвремясчитать,чтокомандынетивноситьизменениякаждыйраз.
Параметрlinesпозволяетотправлятьинесколькокоманд(playbook1_ios_config_mult_lines.yml):
ios_config
527
---
-name:Runcfgcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:Sendconfigcommands
ios_config:
lines:
-servicepassword-encryption
-noiphttpserver
-noiphttpsecure-server
-noipdomainlookup
provider:"{{cli}}"
Результатвыполнения:
$ansible-playbook1_ios_config_mult_lines.yml
ios_config
528
parentsПараметрparentsиспользуется,чтобыуказатьвкакомподрежимеприменитькоманды.
Например,необходимоприменитьтакиекоманды:
linevty04
loginlocal
transportinputssh
Втакомслучае,playbook2_ios_config_parents_basic.ymlбудетвыглядетьтак:
---
-name:Runcfgcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:Configlinevty
ios_config:
parents:
-linevty04
lines:
-loginlocal
-transportinputssh
provider:"{{cli}}"
Запускбудетвыполнятьсяаналогичнопредыдущимplaybook:
$ansible-playbook2_ios_config_parents_basic.yml
ios_config
529
Есликоманданаходитсявнесколькихвложенныхрежимах,подрежимыуказываютсявспискеparents.
Например,необходимовыполнитьтакиекоманды:
policy-mapOUT_QOS
classclass-default
shapeaverage1000000001000000
Тогдаplaybook2_ios_config_parents_mult.ymlбудетвыглядетьтак:
---
-name:Runcfgcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:ConfigQoSpolicy
ios_config:
parents:
-policy-mapOUT_QOS
-classclass-default
lines:
-shapeaverage1000000001000000
provider:"{{cli}}"
ios_config
530
ОтображениеобновленийВэтомразделерассматриваютсявариантыотображенияинформациюобобновлениях,которыевыполнилмодульios_config.
Playbook2_ios_config_parents_basic.yml:
---
-name:Runcfgcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:Configlinevty
ios_config:
parents:
-linevty04
lines:
-loginlocal
-transportinputssh
provider:"{{cli}}"
Длятого,чтобыplaybookчто-томенял,нужносначалаотменитькоманды.Либовручную,либоизменивplaybook.Например,намаршрутизаторе192.168.100.1,вместострокиtransportinputssh,вручнуюпрописатьстрокуtransportinputall.
Например,можновыполнитьplaybookсфлагомverbose:
$ansible-playbook2_ios_config_parents_basic.yml-v
ios_config
531
Ввыводе,вполеupdatesвидно,какиеименнокомандыAnsibleотправилнаустройство.Изменениябыливыполненытольконамаршрутизаторе192.168.100.1.
Обратитевнимание,чтокомандаloginlocalнеотправлялась,таккаконанастроена.
Полеupdatesввыводеестьтольковтомслучае,когдаестьизменения.
Врежимеverbose,информациявиднаобовсехустройствах.Но,былобыудобней,чтобыинформацияотображаласьтолькодлятехустройств,длякоторыхпроизошлиизменения.
Новыйplaybook3_ios_config_debug.ymlнаоснове2_ios_config_parents_basic.yml:
---
-name:Runcfgcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:Configlinevty
ios_config:
parents:
-linevty04
lines:
-loginlocal
-transportinputssh
provider:"{{cli}}"
register:cfg
-name:Showconfigupdates
debug:var=cfg.updates
when:cfg.changed
ios_config
532
Изменениявplaybook:
результатработыпервойзадачисохраняетсявпеременнуюcfg.вследующейзадачемодульdebugвыводитсодержимоеполяupdates.
нотаккакполеupdatesввыводеестьтольковтомслучае,когдаестьизменения,ставитсяусловиеwhen,котороепроверяетбылилиизменениязадачабудетвыполнятьсятолькоеслинаустройствебыливнесеныизменения.вместоwhen:cfg.changedможнонаписатьwhen:cfg.changed==true
Еслизапуститьповторноplaybook,когдаизмененийнебыло,задачаShowconfigupdates,пропускается:
$ansible-playbook3_ios_config_debug.yml
Есливнестиизменениявконфигурациюмаршрутизатора192.168.100.1(изменитьtransportinputsshнаtransportinputall):
$ansible-playbook3_ios_config_debug.yml
ios_config
533
Теперьвтороезаданиеотображаетинформациюотом,какиеименноизменениябыливнесенынамаршрутизаторе.
ios_config
534
saveПараметрsaveпозволяетуказатьнужнолисохранятьтекущуюконфигурациювстартовую.Поумолчанию,значениепараметра-no.
Доступныевариантызначений:
no(илиfalse)yes(илиtrue)
Ксожалению,наданныймомент(версияansible2.2),этотпараметрнеотрабатываеткорректно,таккакнаустройствоотправляетсякомандаcopyrunning-configstartup-config,но,приэтом,неотправляетсяподтверждениенасохранение.Из-заэтого,призапускеplaybookспараметромsaveвыставленнымвyes,появляетсятакаяошибка:
$ansible-playbook4_ios_config_save.yml
Но,можносамостоятельносделатьсохранение,используямодульios_command.
Playbook4_ios_config_save.yml:
ios_config
535
---
-name:Runcfgcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:Configlinevty
ios_config:
parents:
-linevty04
lines:
-loginlocal
-transportinputssh
#save:yes-вверсии2.2неработаеткорректно
provider:"{{cli}}"
register:cfg
-name:Saveconfig
ios_command:
commands:
-write
provider:"{{cli}}"
when:cfg.changed
Надовнестиизменениянамаршрутизаторе192.168.100.1.Например,изменитьстрокуtransportinputallнаtransportinputssh.
Выполнениеplaybook:
$ansible-playbook4_ios_config_save.yml
ios_config
536
ios_config
537
backupПараметрbackupуказываетнужнолиделатьрезервнуюкопиютекущейконфигурацииустройствапередвнесениемизменений.Файлбудеткопироватьсявкаталогbackup,относительнокаталогавкоторомнаходитсяplaybook(есликаталогнесуществует,онбудетсоздан).
Playbook5_ios_config_backup.yml:
---
-name:Runcfgcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:Configlinevty
ios_config:
parents:
-linevty04
lines:
-loginlocal
-transportinputssh
backup:yes
provider:"{{cli}}"
Теперь,каждыйраз,когдавыполняетсяplaybook(дажееслиненужновноситьизменениявконфигурацию),вкаталогbackupбудеткопироватьсятекущаяконфигурация:
$ansible-playbook5_ios_config_backup.yml-v
ios_config
538
Вкаталогеbackupтеперьнаходятсяфайлытакоговида(прикаждомзапускеplaybookониперезаписываются):
192.168.100.1_config.2016-12-10@10:42:34
192.168.100.2_config.2016-12-10@10:42:34
192.168.100.3_config.2016-12-10@10:42:34
ios_config
539
defaultsПараметрdefaultsуказываетнужнолисобиратьвсюинформациюсустройства,втомчислеизначенияпоумолчанию.Есливключитьэтотпараметр,модульбудетсобиратьтекущуюкофигурациюспомощьюкомандыshrunall.Поумолчаниюэтотпараметротключениконфигурацияпроверяетсякомандойshrun.
Этотпараметрполезенвтомслучае,есливнастройкахуказываетсякоманда,котораяневиднавконфигурации.Например,такоеможетбыть,когдауказанпараметр,которыйитакиспользуетсяпоумолчанию.
Еслинеиспользоватьпараметрdefaults,иуказатькоманду,котораянастроенапоумолчанию,топрикаждомзапускеplaybook,будутвноситьсяизменения.
Присходитэтопотому,чтоAnsibleкаждыйразвначалепроверяетналичиекомандвсоответствующемрежиме.Есликоманднет,тосоответствующаязадачавыполняется.
Например,втакомplaybook,каждыйразбудутвноситьсяизменения(попробуйтезапуститьегосамостоятельно):
---
-name:Runcfgcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:Configinterface
ios_config:
parents:
-interfaceEthernet0/2
lines:
-ipaddress192.168.200.1255.255.255.0
-ipmtu1500
provider:"{{cli}}"
Еслидобавитьпараметрdefaults:yes,измененияуженебудутвнесены,еслинехваталотолькокомандыipmtu1500(playbook6_ios_config_defaults.yml):
ios_config
540
---
-name:Runcfgcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:Configinterface
ios_config:
parents:
-interfaceEthernet0/2
lines:
-ipaddress192.168.200.1255.255.255.0
-ipmtu1500
defaults:yes
provider:"{{cli}}"
Запускplaybook:
$ansible-playbook6_ios_config_defaults.yml
ios_config
541
afterПараметрafterуказываеткакиекомандывыполнитьпослекомандвспискеlines(илиcommands).
Команды,которыеуказанывпараметреafter:
выполняютсятолькоеслидолжныбытьвнесеныизменения.приэтомонибудутвыполнены,независимооттогоестьонивконфигурацииилинет.
Параметрafterоченьполезенвситуациях,когданеобходимовыполнитькоманду,котораянесохраняетсявконфигурации.
Например,командаnoshutdownнесохраняетсявконфигурациимаршрутизатора.И,еслидобавитьеёвсписокlines,изменениябудутвноситьсякаждыйраз,привыполненииplaybook.
Но,еслинаписатькомандуnoshutdownвспискеafter,тоонабудетпримененатольковтомслучае,еслинужновноситьизменения(согласноспискаlines).
Примериспользованияпараметраafterвplaybook7_ios_config_after.yml:
---
-name:Runcfgcommandsonrouter
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:Configinterface
ios_config:
parents:
-interfaceEthernet0/3
lines:
-ipaddress192.168.230.1255.255.255.0
after:
-noshutdown
provider:"{{cli}}"
Первыйзапускplaybook,свнесениемизменений:
$ansible-playbook7_ios_config_after.yml-v
ios_config
542
Второйзапускplaybook(измененийнет,поэтомукомандаnoshutdownневыполняется):
$ansible-playbook7_ios_config_after.yml-v
Рассмотримещёодинпримериспользованияafter.
Спомощьюafterможносохранятьконфигурациюустройства(playbook7_ios_config_after_save.yml):
ios_config
543
---
-name:Runcfgcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:Configlinevty
ios_config:
parents:
-linevty04
lines:
-loginlocal
-transportinputssh
after:
-end
-write
provider:"{{cli}}"
Результатвыполненияplaybook(изменениятольконамаршрутизаторе192.168.100.1):
$ansible-playbook7_ios_config_after_save.yml-v
ios_config
544
beforeПараметрbeforeуказываеткакиедействиявыполнитьдокомандвспискеlines.
Команды,которыеуказанывпараметреbefore:
выполняютсятолькоеслидолжныбытьвнесеныизменения.приэтомонибудутвыполнены,независимооттогоестьонивконфигурацииилинет.
Параметрbeforeполезенвситуациях,когдакакие-тодействиянеобходимовыполнитьпередвыполнениемкомандвспискеlines.
Приэтом,какиafter,параметрbeforeневлияетнато,какиекомандысравниваютсясконфигурацией.Тоесть,по-прежнему,сравниваютсятолькокомандывспискеlines.
Playbook8_ios_config_before.yml:
---
-name:Runcfgcommandsonrouter
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:ConfigACL
ios_config:
before:
-noipaccess-listextendedIN_to_OUT
parents:
-ipaccess-listextendedIN_to_OUT
lines:
-permittcp10.0.1.00.0.0.255anyeqwww
-permittcp10.0.1.00.0.0.255anyeq22
-permiticmpanyany
provider:"{{cli}}"
Вplaybook8_ios_config_before.ymlACLIN_to_OUTсначалаудалятся,спомощьюпараметраbefore,азатемсоздаетсязаново.
ТакимобразомвACLвсегданаходятсятолькотестроки,которыезаданывспискеlines.
Запускplaybookсизменениями:
ios_config
545
$ansible-playbook8_ios_config_before.yml-v
Запускplaybookбезизменений(командавспискеbeforeневыполняется):
$ansible-playbook8_ios_config_before.yml-v
ios_config
546
matchПараметрmatchуказываеткакименнонужносравниватькоманды(чтосчитаетсяизменением):
line-командыпроверяютсяпострочно.Этотрежимиспользуетсяпоумолчаниюstrict-должнысовпастьнетолькосамикоманды,ноихположениеотносительнодругдругаexact-командыдолжнывточностисопадатьсконфигурациейинедолжнобытьникакихлишнихстрокnone-модульнебудетсравниватькомандыстекущейконфигурацией
match:line
Режимmatch:lineиспользуетсяпоумолчанию.
Вэтомрежиме,модульпроверяеттольконаличиестрок,перечисленныхвспискеlinesвсоответствующемрежиме.Приэтом,непроверяетсяпорядокстрок.
Намаршрутизаторе192.168.100.1настроентакойACL:
R1#shrun|saccess
ipaccess-listextendedIN_to_OUT
permittcp10.0.1.00.0.0.255anyeq22
Примериспользованияplaybook9_ios_config_match_line.ymlврежимеline:
ios_config
547
---
-name:Runcfgcommandsonrouter
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:ConfigACL
ios_config:
parents:
-ipaccess-listextendedIN_to_OUT
lines:
-permittcp10.0.1.00.0.0.255anyeqwww
-permittcp10.0.1.00.0.0.255anyeq22
-permiticmpanyany
provider:"{{cli}}"
Результатвыполненияplaybook:
$ansible-playbook9_ios_config_match_line.yml-v
Обратитевнимание,чтовспискеupdatesтолькодвеизтрёхстрокACL.Таккакврежимеlinesмодульсравниваеткомандынезависимодруготдруга,онобнаружил,чтонехватаеттолькодвухкомандизтрех.
Витогеконфигурациянамаршрутизаторевыглядиттак:
R1#shrun|saccess
ipaccess-listextendedIN_to_OUT
permittcp10.0.1.00.0.0.255anyeq22
permittcp10.0.1.00.0.0.255anyeqwww
permiticmpanyany
ios_config
548
Тоесть,порядоккомандпоменялся.И,хотявэтомслучае,этоневажно,иногдаэтоможетпривестисовсемнектемрезультатам,которыеожидались.
Еслиповторнозапуститьplaybook,притакойконфигурации,оннебудетвыполнятьизменения,таккаквсестрокибылинайдены.
match:exact
Пример,вкоторомпорядоккомандважен.
ACLнамаршрутизаторе:
R1#shrun|saccess
ipaccess-listextendedIN_to_OUT
permittcp10.0.1.00.0.0.255anyeq22
permittcp10.0.1.00.0.0.255anyeqwww
denyipanyany
Playbook9_ios_config_match_exact.yml(будетпостепеннодополняться):
---
-name:Runcfgcommandsonrouter
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:ConfigACL
ios_config:
parents:
-ipaccess-listextendedIN_to_OUT
lines:
-permittcp10.0.1.00.0.0.255anyeqwww
-permittcp10.0.1.00.0.0.255anyeq22
-permiticmpanyany
-denyipanyany
provider:"{{cli}}"
Еслизапуститьplaybook,результатбудеттаким:
$ansible-playbook9_ios_config_match_exact.yml-v
ios_config
549
ТеперьACLвыглядиттак:
R1#shrun|saccess
ipaccess-listextendedIN_to_OUT
permittcp10.0.1.00.0.0.255anyeq22
permittcp10.0.1.00.0.0.255anyeqwww
denyipanyany
permiticmpanyany
Конечноже,втакомслучае,последнееправилоникогданесработает.
МожнодобавитькэтомуplaybookпараметрbeforeисначалаудалитьACL,азатемприменятькоманды:
---
-name:Runcfgcommandsonrouter
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:ConfigACL
ios_config:
before:
-noipaccess-listextendedIN_to_OUT
parents:
-ipaccess-listextendedIN_to_OUT
lines:
-permittcp10.0.1.00.0.0.255anyeqwww
-permittcp10.0.1.00.0.0.255anyeq22
-permiticmpanyany
-denyipanyany
provider:"{{cli}}"
ios_config
550
Еслиприменитьplaybookкпоследнемусостояниюмаршрутизатора,тоизмененийнебудетникаких,таккаквсестрокиужеесть.
ПопробуемначатьстакогосостоянияACL:
R1#shrun|saccess
ipaccess-listextendedIN_to_OUT
permittcp10.0.1.00.0.0.255anyeq22
permittcp10.0.1.00.0.0.255anyeqwww
denyipanyany
Результатбудеттаким:
$ansible-playbook9_ios_config_match_exact.yml-v
И,соответственно,намаршрутизаторе:
R1#shrun|saccess
ipaccess-listextendedIN_to_OUT
permiticmpanyany
ТеперьвACLосталасьтолькооднастрока:
МодульпроверилкакихкоманднехватаетвACL(таккакрежимпоумолчаниюmatch:line),обнаружил,чтонехватаеткомандыpermiticmpanyanyидобавилеё
Но,таккаквplaybookACLсначалаудаляется,азатемприменяетсясписоккомандlines,получилось,чтовитогевACLоднастрока.
Поможет,втакойситуации,вариантmatch:exact:
ios_config
551
---
-name:Runcfgcommandsonrouter
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:ConfigACL
ios_config:
before:
-noipaccess-listextendedIN_to_OUT
parents:
-ipaccess-listextendedIN_to_OUT
lines:
-permittcp10.0.1.00.0.0.255anyeqwww
-permittcp10.0.1.00.0.0.255anyeq22
-permiticmpanyany
-denyipanyany
match:exact
provider:"{{cli}}"
Применениеplaybook9_ios_config_match_exact.ymlктекущемусостояниюмаршрутизатора(вACLоднастрока):
$ansible-playbook9_ios_config_match_exact.yml-v
Теперьрезультаттакой:
ios_config
552
R1#shrun|saccess
ipaccess-listextendedIN_to_OUT
permittcp10.0.1.00.0.0.255anyeqwww
permittcp10.0.1.00.0.0.255anyeq22
permiticmpanyany
denyipanyany
Тоесть,теперьACLвыглядитточнотакже,какистрокивспискеlinesивтомжепорядке.
ВверсииAnsible2.1match:exactработалпо-другомуитакойрезультатдостигалсякомбинациейпараметровmatch:exactиreplace:block.Вверсии2.2достаточноmatch:exact.
И,длятогочтобыокончательноразобратьсяспараметромmatch:exact,ещёодинпример.
ЗакомментируемвplaybookстрокисудалениемACL:
---
-name:Runcfgcommandsonrouter
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:ConfigACL
ios_config:
#before:
#-noipaccess-listextendedIN_to_OUT
parents:
-ipaccess-listextendedIN_to_OUT
lines:
-permittcp10.0.1.00.0.0.255anyeqwww
-permittcp10.0.1.00.0.0.255anyeq22
-permiticmpanyany
-denyipanyany
match:exact
provider:"{{cli}}"
ВначалоACLдобавленастрока:
ios_config
553
ipaccess-listextendedIN_to_OUT
permitudpanyany
permittcp10.0.1.00.0.0.255anyeqwww
permittcp10.0.1.00.0.0.255anyeq22
permiticmpanyany
denyipanyany
Тоесть,последние4строкивыглядяттак,какнужно,ивтомпорядке,которомнужно.Но,приэтом,естьлишняястрока.Длявариантаmatch:exact-этоуженесовпадение.
Втакомварианте,playbookбудетвыполнятьсякаждыйразипытатьсяприменитьвсекомандыизспискаlines,чтонебудетвлиятьнасодержимоеACL:
$ansible-playbook9_ios_config_match_exact.yml-v
Этозначит,чтоприиспользованииmatch:exact,важно,чтобыбылкакой-тоспособудалитьконфигурацию,еслионанесоответствуеттому,чтодолжнобыть(иличтобыкомандыперезаписывались).Иначе,этазадачабудетвыполнятьсякаждыйраз,призапускеplaybook.
match:strict
Вариантmatch:strictнетребует,чтобыобъектбылвточностикакуказановзадаче,но,команды,которыеуказанывспискеlines,должныбытьвтомжепорядке.
Еслиуказансписокparents,командывспискеlinesдолжныидтисразузакомандамиparents.
НамаршрутиазаторетакойACL:
ios_config
554
ipaccess-listextendedIN_to_OUT
permittcp10.0.1.00.0.0.255anyeqwww
permittcp10.0.1.00.0.0.255anyeq22
permiticmpanyany
denyipanyany
Playbook9_ios_config_match_strict.yml:
---
-name:Runcfgcommandsonrouter
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:ConfigACL
ios_config:
before:
-noipaccess-listextendedIN_to_OUT
parents:
-ipaccess-listextendedIN_to_OUT
lines:
-permittcp10.0.1.00.0.0.255anyeqwww
-permittcp10.0.1.00.0.0.255anyeq22
-permiticmpanyany
match:strict
provider:"{{cli}}"
Выполнениеplaybook:
$ansible-playbook9_ios_config_match_strict.yml-v
Таккакизмененийнебыло,ACLосталсятакимже.
ios_config
555
Втакойжеситуации,прииспользованииmatch:exact,былобыобнаруженоизменениеиACLбысостоялтолькоизстроквспискеlines.
match:none
Использованиеmatch:noneотключаетидемпотентностьзадачи:каждыйразпривыполненииplaybook,будутотправлятьсякоманды,которыеуказанывзадаче.
Примерplaybook9_ios_config_match_none.yml:
---
-name:Runcfgcommandsonrouter
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:ConfigACL
ios_config:
before:
-noipaccess-listextendedIN_to_OUT
parents:
-ipaccess-listextendedIN_to_OUT
lines:
-permittcp10.0.1.00.0.0.255anyeqwww
-permittcp10.0.1.00.0.0.255anyeq22
-permiticmpanyany
match:none
provider:"{{cli}}"
Каждыйразпризапускеplaybookрезультатбудеттаким:
$ansible-playbook9_ios_config_match_none.yml-v
ios_config
556
Использованиеmatch:noneподходитвтехслучаях,когда,независимооттекущейконфигурации,нужноотправитьвсекоманды.
ios_config
557
replaceПараметрreplaceуказываеткакименнонужнозаменятьконфигурацию:
line-вэтомрежимеотправляютсятолькотекоманды,которыхнетвконфигурации.Этотрежимиспользуетсяпоумолчаниюblock-вэтомрежимеотправляютсявсекоманды,еслихотябыоднойкомандынет
replace:line
Режимreplace:line-эторежимработыпоумолчанию.Вэтомрежиме,еслибылиобнаруженыизменения,отправляютсятольконедостающиестроки.
Например,намаршрутизаторетакойACL:
R1#shrun|saccess
ipaccess-listextendedIN_to_OUT
permittcp10.0.1.00.0.0.255anyeqwww
permittcp10.0.1.00.0.0.255anyeq22
permiticmpanyany
Попробуемзапуститьтакойplaybook10_ios_config_replace_line.yml:
---
-name:Runcfgcommandsonrouter
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:ConfigACL
ios_config:
before:
-noipaccess-listextendedIN_to_OUT
parents:
-ipaccess-listextendedIN_to_OUT
lines:
-permittcp10.0.1.00.0.0.255anyeqwww
-permittcp10.0.1.00.0.0.255anyeq22
-permiticmpanyany
-denyipanyany
provider:"{{cli}}"
ios_config
558
Выполнениеplaybook:
$ansible-playbook10_ios_config_replace_line.yml-v
ПослеэтогонамаршрутизаторетакойACL:
R1#shrun|saccess
ipaccess-listextendedIN_to_OUT
denyipanyany
Вданномслучае,модульпроверилкакихкоманднехватаетвACL(таккакрежимпоумолчаниюmatch:line),обнаружил,чтонехватаеткомандыdenyipanyanyидобавилеё.Но,таккакACLсначалаудаляется,азатемприменяетсясписоккомандlines,получилось,чтоутеперьACLсоднойстрокой.
Втакихситуацияхподходитрежимreplace:block.
replace:block
Врежимеreplace:blockотправляютсявсекомандыизспискаlines(иparents),еслинаустройственетхотябыоднойизэтихкоманд.
Повторимпредыдущийпример.
ACLнамаршрутизаторе:
R1#shrun|saccess
ipaccess-listextendedIN_to_OUT
permittcp10.0.1.00.0.0.255anyeqwww
permittcp10.0.1.00.0.0.255anyeq22
permiticmpanyany
Playbook10_ios_config_replace_block.yml:
ios_config
559
---
-name:Runcfgcommandsonrouter
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:ConfigACL
ios_config:
before:
-noipaccess-listextendedIN_to_OUT
parents:
-ipaccess-listextendedIN_to_OUT
lines:
-permittcp10.0.1.00.0.0.255anyeqwww
-permittcp10.0.1.00.0.0.255anyeq22
-permiticmpanyany
-denyipanyany
replace:block
provider:"{{cli}}"
Выполнениеplaybook:
$ansible-playbook10_ios_config_replace_block.yml-v
ВрезультатенамаршрутизаторетакойACL:
R1#shrun|saccess
ipaccess-listextendedIN_to_OUT
permittcp10.0.1.00.0.0.255anyeqwww
permittcp10.0.1.00.0.0.255anyeq22
permiticmpanyany
denyipanyany
ios_config
560
ios_config
561
srcПараметрsrcпозволяетуказыватьпутькфайлуконфигурацииилишаблонуконфигурации,которуюнужнозагрузитьнаустройство.
Этотпараметрвзаимоисключающийсlines(тоесть,можноуказыватьилиlinesилиsrc).Онзаменяетмодульios_template,которыйскоробудетудален.
Конфигурация
Примерplaybook11_ios_config_src.yml:
---
-name:Runcfgcommandsonrouter
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:ConfigACL
ios_config:
src:templates/acl_cfg.txt
provider:"{{cli}}"
Вфайлеtemplates/acl_cfg.txtнаходитсятакаяконфигурация:
ipaccess-listextendedIN_to_OUT
permittcp10.0.1.00.0.0.255anyeqwww
permittcp10.0.1.00.0.0.255anyeq22
permiticmpanyany
denyipanyany
УдаляемнамаршрутизатореэтотACL,еслионосталсяспрошлыхразделов,изапускаемplaybook:
$ansible-playbook11_ios_config_src.yml-v
ios_config
562
Неприятнаяособенностьпараметраsrcвтом,чтоневиднокакиеизменениябыливнесены.Но,возможно,вследующихверсияхAnsibleэтобудетисправлено.
ТеперьнамаршрутизаторенастроенACL:
R1#shrun|saccess
ipaccess-listextendedIN_to_OUT
permittcp10.0.1.00.0.0.255anyeqwww
permittcp10.0.1.00.0.0.255anyeq22
permiticmpanyany
denyipanyany
Еслизапуститьplaybookещёраз,ноникакихизмененийнебудет,таккакэтотпараметртакжеидемпотентен:
$ansible-playbook11_ios_config_src.yml-v
ШаблонJinja2
ВпараметреsrcможноуказыватьшаблонJinja2.
Примершаблона(файлtemplates/ospf.j2):
ios_config
563
routerospf1
router-id{{mgmnt_ip}}
ispf
auto-costreference-bandwidth10000
{%foripinospf_ints%}
network{{ip}}0.0.0.0area0
{%endfor%}
Вшаблонеиспользуютсядвепеременные:
mgmnt_ip-IP-адрес,которыйбудетиспользоватьсякакrouter-idospf_ints-списокIP-адресовинтерфейсов,накоторыхнужновключитьOSPF
ДлянастройкиOSPFнатрёхмаршрутизаторах,нужноиметьвозможностьиспользоватьразныезначенияэтихпеременныхдляразныхустройств.Длятакихзадачиспользуютсяфайлыспеременнымивкаталогеhost_vars.
Вкаталогеhost_varsнужносоздатьтакиефайлы(еслиониещёнесозданы):
Файлhost_vars/192.168.100.1:
---
hostname:london_r1
mgmnt_loopback:100
mgmnt_ip:10.0.0.1
ospf_ints:
-192.168.100.1
-10.0.0.1
-10.255.1.1
Файлhost_vars/192.168.100.2:
---
hostname:london_r2
mgmnt_loopback:100
mgmnt_ip:10.0.0.2
ospf_ints:
-192.168.100.2
-10.0.0.2
-10.255.2.2
Файлhost_vars/192.168.100.3:
ios_config
564
---
hostname:london_r3
mgmnt_loopback:100
mgmnt_ip:10.0.0.3
ospf_ints:
-192.168.100.3
-10.0.0.3
-10.255.3.3
Теперьможносоздаватьplaybook11_ios_config_src_jinja.yml:
---
-name:Runcfgcommandsonrouter
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:ConfigOSPF
ios_config:
src:templates/ospf.j2
provider:"{{cli}}"
ТаккакAnsibleсамнайдетпеременныевкаталогеhost_vars,ихненужноуказывать.Можносразузапускатьplaybook:
$ansible-playbook11_ios_config_src_jinja.yml-v
ТеперьнавсехмаршрутизаторахнастроенOSPF:
ios_config
565
R1#shrun|sospf
routerospf1
router-id10.0.0.1
ispf
auto-costreference-bandwidth10000
network10.0.0.10.0.0.0area0
network10.255.1.10.0.0.0area0
network192.168.100.10.0.0.0area0
R2#shrun|sospf
routerospf1
router-id10.0.0.2
ispf
auto-costreference-bandwidth10000
network10.0.0.20.0.0.0area0
network10.255.2.20.0.0.0area0
network192.168.100.20.0.0.0area0
routerospf1
router-id10.0.0.3
ispf
auto-costreference-bandwidth10000
network10.0.0.30.0.0.0area0
network10.255.3.30.0.0.0area0
network192.168.100.30.0.0.0area0
Еслизапуститьplaybookещёраз,ноникакихизмененийнебудет:
$ansible-playbook11_ios_config_src_jinja.yml-v
Совмещениесдругимипараметрами
Параметрsrcсовместимстакимипараметрами:
ios_config
566
backupconfigdefaultssave(ноусамогоsaveвAnsible2.2проблемысработой)
ios_config
567
ntc-ansiblentc-ansible-этомодульдляработыссетевымоборудованием,которыйнетольковыполняеткомандынаоборудовании,ноиобрабатываетвыводкомандипреобразуетспомощьюTextFSM.
ЭтотмодульневходитвчислоcoreмодулейAnsible,поэтомуегонужноустановить.
НопрежденужноуказатьAnsible,гдеискатьсторонниемодули.Указываетсяпутьвфайлеansible.cfg:
[defaults]
inventory=./myhosts
remote_user=cisco
ask_pass=True
library=./library
Послеэтого,нужноклонироватьрепозиторийntc-ansible,находясьвкаталогеlibrary:
[~/pyneng_course/chapter15/library]
$gitclonehttps://github.com/networktocode/ntc-ansible--recursive
Cloninginto'ntc-ansible'...
remote:Countingobjects:2063,done.
remote:Compressingobjects:100%(5/5),done.
remote:Total2063(delta1),reused0(delta0),pack-reused2058
Receivingobjects:100%(2063/2063),332.15KiB|334.00KiB/s,done.
Resolvingdeltas:100%(1157/1157),done.
Checkingconnectivity...done.
Submodule'ntc-templates'(https://github.com/networktocode/ntc-templates)registered
forpath'ntc-templates'
Cloninginto'ntc-templates'...
remote:Countingobjects:902,done.
remote:Compressingobjects:100%(34/34),done.
remote:Total902(delta16),reused0(delta0),pack-reused868
Receivingobjects:100%(902/902),161.11KiB|0bytes/s,done.
Resolvingdeltas:100%(362/362),done.
Checkingconnectivity...done.
Submodulepath'ntc-templates':checkedout'89c57342b47c9990f0708226fb3f268c6b8c1549'
Азатемустановитьзависимостимодуля:
ntc_ansible
568
pipinstallntc-ansible
Еслиприустановкевозникнутпроблемы,посмотритедругиевариантыустановкиврепозиториипроекта.
ТаккаквтекущейверсииAnsibleужеестьмодули,которыеработаютссетевымоборудованиемипозволяютвыполнятькоманды,извсехвозможностейntc-ansible,наиболееполезнойбудетотправкакомандshowиполучениеструктурированноговывода.Заэтоотвечаетмодульntc_show_command.
ntc_show_commandМодульиспользуетnetmikoдляподключениякоборудованию(netmikoдолженбытьустановлен)и,послевыполнениякоманды,преобразуетвыводкомандыshowспомощьюTextFSMвструктурированныйвывод(списоксловарей).
Преобразованиебудетвыполнятьсявтомслучае,есливфайлеindexбыланайденакомандаидлякомандыбылнайденшаблон.
Какиспредыдущимисетевымимодулями,вntc-ansibleнужноуказыватьрядпараметровдляподключения:
connection-тутвозможныдваварианта:ssh(подключениеnetmiko)илиoffline(чтениеизфайладлятестовыхцелей)platform-платформа,котораясуществуетвindexфайле(library/ntc-ansible/ntc-templates/templates/index)command-команда,которуюнужновыполнитьнаустройствеhost-IP-адресилиимяустройстваusername-имяпользователяpassword-парольtemplate_dir-путьккаталогувкоторомнаходятсяшаблоны(втекущемвариантеустановкионинаходятсявкаталогеlibrary/ntc-ansible/ntc-templates/templates
Примерplaybook1_ntc_ansible.yml:
ntc_ansible
569
---
-name:Runshowcommandsonrouter
hosts:192.168.100.1
gather_facts:false
connection:local
tasks:
-name:Runshipintbr
ntc_show_command:
connection:ssh
platform:"cisco_ios"
command:"shipintbr"
host:"{{inventory_hostname}}"
username:"cisco"
password:"cisco"
template_dir:"library/ntc-ansible/ntc-templates/templates"
register:result
-debug:var=result
Результатвыполненияplaybook:
$ansible-playbook1_ntc-ansible.yml
ntc_ansible
570
Впеременнойresponseнаходитсяструктурированныйвыводввидеспискасловарей.Ключивсловаряхполученынаоснованиипеременных,которыеописанывшаблонеlibrary/ntc-ansible/ntc-templates/templates/cisco_ios_show_ip_int_brief.template(единственноеотличие-регистр):
ntc_ansible
571
ValueINTF(\S+)
ValueIPADDR(\S+)
ValueSTATUS(up|down|administrativelydown)
ValuePROTO(up|down)
Start
^${INTF}\s+${IPADDR}\s+\w+\s+\w+\s+${STATUS}\s+${PROTO}->Record
Длятого,чтобыполучитьвыводпропервыйинтерфейс,можнопоменятьвыводмодуляdebug,такимобразом:
-debug:var=result.response[0]
Сохранениерезультатоввыполнениякоманды
Длятого,чтобысохранитьвывод,можноиспользоватьтотжеприем,которыйиспользовалсядлямодуляios_facts.
Примерplaybook2_ntc_ansible_save.ymlссохранениемрезультатовкоманды:
---
-name:Runshowcommandsonrouters
hosts:cisco-routers
gather_facts:false
connection:local
tasks:
-name:Runshipintbr
ntc_show_command:
connection:ssh
platform:"cisco_ios"
command:"shipintbr"
host:"{{inventory_hostname}}"
username:"cisco"
password:"cisco"
template_dir:"library/ntc-ansible/ntc-templates/templates"
register:result
-name:Copyfactstofiles
copy:
content:"{{result.response|to_nice_json}}"
dest:"all_facts/{{inventory_hostname}}_sh_ip_int_br.json"
Результатвыполнения:
ntc_ansible
572
$ansible-playbook2_ntc-ansible_save.yml
Врезультате,вкаталогеall_factsповяляютсясоответствующиефайлыдлякаждогомаршрутизатора.Примерфайлаall_facts/192.168.100.1_sh_ip_int_br.json:
ntc_ansible
573
[
{
"intf":"Ethernet0/0",
"ipaddr":"192.168.100.1",
"proto":"up",
"status":"up"
},
{
"intf":"Ethernet0/1",
"ipaddr":"192.168.200.1",
"proto":"up",
"status":"up"
},
{
"intf":"Ethernet0/2",
"ipaddr":"unassigned",
"proto":"down",
"status":"administrativelydown"
},
{
"intf":"Ethernet0/3",
"ipaddr":"unassigned",
"proto":"up",
"status":"up"
},
{
"intf":"Loopback0",
"ipaddr":"10.1.1.1",
"proto":"up",
"status":"up"
}
]
ШаблоныJinja2ДляCiscoIOSвntc-ansibleестьтакиешаблоны:
ntc_ansible
574
cisco_ios_dir.template
cisco_ios_show_access-list.template
cisco_ios_show_aliases.template
cisco_ios_show_archive.template
cisco_ios_show_capability_feature_routing.template
cisco_ios_show_cdp_neighbors_detail.template
cisco_ios_show_cdp_neighbors.template
cisco_ios_show_clock.template
cisco_ios_show_interfaces_status.template
cisco_ios_show_interfaces.template
cisco_ios_show_interface_transceiver.template
cisco_ios_show_inventory.template
cisco_ios_show_ip_arp.template
cisco_ios_show_ip_bgp_summary.template
cisco_ios_show_ip_bgp.template
cisco_ios_show_ip_int_brief.template
cisco_ios_show_ip_ospf_neighbor.template
cisco_ios_show_ip_route.template
cisco_ios_show_lldp_neighbors.template
cisco_ios_show_mac-address-table.template
cisco_ios_show_processes_cpu.template
cisco_ios_show_snmp_community.template
cisco_ios_show_spanning-tree.template
cisco_ios_show_standby_brief.template
cisco_ios_show_version.template
cisco_ios_show_vlan.template
cisco_ios_show_vtp_status.template
Списоквсехшаблоновможнопосмотретьлокально,еслиntc-ansibleустановлен:
ls-lslibrary/ntc-ansible/ntc-templates/templates/
Иливрепозиториипроекта.
ИспользуяTextFSMможносамостоятельносоздаватьдополнительныешаблоны.
И,длятого,чтобыntc-ansibleихиспользовалавтоматически,добавитьихвфайлindex(library/ntc-ansible/ntc-templates/templates/index):
ntc_ansible
575
#Firstlineistheheaderfieldsforcolumnsandismandatory.
#Regularexpressionsaresupportedinallfieldsexceptthefirst.
#Lastfieldsupportsvariablelengthcommandcompletion.
#abc[[xyz]]isexpandedtoabc(x(y(z)?)?)?,regexpinside[[]]isnotsupported
#
Template,Hostname,Platform,Command
cisco_asa_dir.template,.*,cisco_asa,dir
cisco_ios_show_archive.template,.*,cisco_ios,sh[[ow]]arc[[hive]]
cisco_ios_show_capability_feature_routing.template,.*,cisco_ios,sh[[ow]]cap[[abil
ity]]f[[eature]]r[[outing]]
cisco_ios_show_aliases.template,.*,cisco_ios,sh[[ow]]alia[[ses]]
...
СинтаксисшаблоновифайлаindexописанывразделеTextFSM.
ntc_ansible
576
ПодробнееобAnsibleМырассмотрелиосновныеаспектыAnsible,которыенужныдляработыссетевымоборудованием.ИхдостаточночтобыначатьработатьсAnsible,но,скореевсего,впроцессеработывампонадобитсябольшеинформации.
Например,каксделатьтак,чтобыненужнобылоповторятьодниитежезадачиилисценариисноваиснова,иликакорганизовыватьболеесложныеplaybook.
А,возможно,накаком-тоэтапе,понадобитсянаписатьсвоймодуль.
ВсёэтоAnsibleпозволяетсделать,ноэтовыходитзарамкиэтогокурса.ЭтаинформациявынесенавотдельныйкурсAnsibleдлясетевыхинженеров.Основы,которыерассматриваютсятут,вкурсеповторяются,поэтому,есливыпрочиталивесьразделAnsibleвэтомкурсе,можетеначатьсразусчетвертогоразделаPlaybook.
Есликакие-тотемынерассмотренывкурсе"Ansibleдлясетевыхинженеров",незабывайте,чтоуAnsibleотличнаядокументация.
ПодробнееобAnsible
577
ЗаданияВсезаданияивспомогательныефайлыможноскачатьоднимархивомzipилиtar.gz.
Такжедлякурсаподготовленыдвевиртуальныемашинынавыбор:VagrantилиVMware.Внихустановленывсепакеты,которыеиспользуютсявкурсе.
Есливзаданияхразделаестьзаданиясбуквами(например,5.2a),томожновыполнитьсначалазаданиябезбукв,азатемсбуквами.Заданиясбуквами,какправило,немногосложнеезаданийбезбуквиразвивают/усложняютидеювсоответствующемзаданиибезбуквы.
Например,есливразделеестьзадания:5.1,5.2,5.2a,5.2b,5.3,5.3a.Сначала,можновыполнитьзадания5.1,5.2,5.3.Азатем5.2a,5.2b,5.3a.
Однако,еслизаданиясбуквамиполучаетсясделатьсразу,можноделатьихпопорядку.
Задание15.1
Создайтеplaybook,которыйвыполняеттакиезадачи:
подключаетсякмаршрутизаторамивыполняеткомандуsharpрезультатзаписываетвпеременнуюsh_arp_output
отображаетсодержимоепеременнойsh_arp_output
Проверьтеработуplaybookнамаршрутизаторах.
Задание15.1a
Создайтеplaybook,которыйвыполняеттакиезадачи:
подключаетсякмаршрутизаторамивыполняеткомандуsharpрезультатзаписываетвпеременнуюsh_arp_output
отображаетрезультатвыполнениякоманды,ввидеспискастрок,гдекаждаястрокаэтооднастрокавыводакоманды
Проверьтеработуplaybookнамаршрутизаторах.
Задание15.1b
Задания
578
Создайтеplaybook,которыйвыполняеттакиезадачи:
подключаетсякмаршрутизаторамивыполняеткомандыsharpиshipintbrобекомандыдолжнывыполнятьсяводнойзадачерезультатзаписываетвпеременнуюresult
втораязадачаотображаетрезультатвыполнениякоманд
Проверьтеработуplaybookнамаршрутизаторах.
Задание15.1c
Создайтеplaybook,которыйвыполняеттакиезадачи:
подключаетсякмаршрутизаторамивыполняеткомандыsharpиshipintbrобекомандыдолжнывыполнятьсяводнойзадачерезультатзаписываетвпеременнуюresult
втораязадачаотображаетрезультатвыполнениякомандыsharpтретяязадачаотображаетрезультатвыполнениякомандыshipintbr
Втораяитретяязадачидолжныотображатьвыводкомандыввидеспискастрок.
Проверьтеработуplaybookнамаршрутизаторах.
Задание15.2
Создайтеplaybook,которыйвыполняеттакиезадачи:
собираетвсефактысмаршрутизатороврезультатнельзязаписыватьвпеременную
отображаетсодержимоефактаобинтерфейсах(вфактенаходитсясловарьсинтерфейсамииихпараметрами)
Проверьтеработуplaybookнамаршрутизаторах.
Задание15.2a
Создайтеplaybook,которыйвыполняеттакиезадачи:
собираетвсефактысмаршрутизатороврезультатнельзязаписыватьвпеременную
записываетсодержимоефактаобинтерфейсахвфайлвкаталогall_facts:имяфайладолжнобытьтакоговида:hostname_intf_facts.yamlhostname-этоимятекущегоустройства,длякоторогособираютсяфактыфайлдолженбытьвформатеYAML,ввиде,которыйудобнейдлячтения
Задания
579
человеком
Проверьтеработуplaybookнамаршрутизаторах.
Задание15.2b
Создайтеplaybook,которыйвыполняеттакиезадачи:
собираетвсефактысмаршрутизатороврезультатнезаписыватьвпеременную
выполняеткомандуshipv6intbrвыводкомандызаписываетвпеременнуюshow_result
отображаетсодержимоепеременнойshow_result,нотольковтомслучае,когдафакт,вкоторомсодержатсяIPv6адресаввидесписка,непустой
Проверьтеработуplaybookнамаршрутизаторах.
Задание15.3
Вplaybooktask_15_3.ymlописанаодназадача.
Попробуйтевыполнитьего,какминимум,двараза.Обратитевнимание,чтоизменениявносилиськаждыйраз.
Изменитеplaybookтакимобразом,чтобыизменениявносилисьтольковтомслучае,когданастроеннеправильныйрежимлогированиявконсоль.
Задание15.4
Создайтеplaybook,которыйвыполняеттакуюзадачу:
создаетACLINET-to-LANиприменяетегокинтерфейсуEthernet0/1
Приэтом,подразумевается,чтонастройкаACLвыполняетсятолькоспомощьюPlaybook.Поэтому,вACLдолжныбытьтолькотестроки,которыеуказанывзадачеplaybook.
Задачадолжнавыполнятьтакиедействия:
удалитьACLсинтерфейсаудалитьACLсоздатьACLинастроитьправилаACLприменитьACLкинтерфейсу
ACLдолженбытьтаким:
Задания
580
ipaccess-listextendedINET-to-LAN
permittcp10.0.1.00.0.0.255anyeqwww
permittcp10.0.1.00.0.0.255anyeq22
permiticmpanyany
ПроверьтеработуplaybookнамаршрутизатореR1.
Задание15.4a
Проверьтеработуplaybookиззадания15.4,вситуации,когдавACLдобавленаещёоднастрока.
Если,последобавлениястрокивзадачеивыполненияplaybook,ACLнамаршрутизаторевыглядиттакже,какописановplaybook,значитзаданиевыполнено.
Еслинет,исправьтесоответственнозадачу.
Добавьте,например,такуюстрокувACL:
permittcp10.0.1.00.0.0.255anyeqtelnet
ПроверьтеработуplaybookнамаршрутизатореR1.
Задание15.4b
Добавьтевplaybookиззадания15.4aещёоднузадачу:
онадолжнаотображать,какиекомандыбылиотправленынаоборудование,впервойзадаче
командыдолжныотображатьсятольковтомслучае,еслибыливыполненыизменения
еслинужно,можноизменятьипервуюзадачу
ПроверьтеработуplaybookнамаршрутизатореR1.
Задание15.4c
Изменитеplaybookиззадания15.4bтакимобразом,чтобыимяинтерфейса,которыйуказываетсявзадаче,указывалоськакпеременнаяoutside_intf.
СоздайтепеременнуюдлямаршрутизатораR1,всоответствующемфайлекаталогаhost_vars.
ПроверьтеработуplaybookнамаршрутизатореR1.
Задания
581
Задания
582
ДополнительнаяинформацияВэтомразделесобранаинформация,котораяневошлавосновныеразделыкурса,нокоторая,темнеменее,можетбытьполезна.
Дополнительнаяинформация
583
ПолезныемодулиВэтомразделеописанытакиемодули:
subprocessosargparseipaddress
Полезныемодули
584
МодульsubprocessМодульsubprocessпозволяетсоздаватьновыепроцессы.Приэтом,онможетподключатьсякстандартнымпотокамввода/вывода/ошибокиполучатькодвозврата.
Спомощьюsubprocess,можно,например,выполнятьлюбыекомандыLinuxизскрипта.И,взависимостиотситуации,получатьвыводилитолькопроверять,чтокомандавыполниласьбезошибок.
Функцияsubprocess.call()
Функцияcall():
позволяетвыполнитькомандуприэтом,онаожидаетзавершениякоманды.
функциявозращаеткодвозврата
Примервыполнениякомандыls:
In[1]:importsubprocess
In[2]:result=subprocess.call('ls')
LICENSE.mdcourse_presentationsfaq.md
README.mdcourse_presentations.ziphowto.md
SUMMARY.mdcover.jpgimages
ToDo.mdexamplesresources
about.mdexamples.zipschedule.md
bookexercises
book.jsonexercises.zip
Впеременнойresultтеперьсодержитсякодвозврата(код0означает,чтопрограммавыполниласьуспешно):
In[3]:printresult
0
Обратитевнимание,что,еслинеобходимовызватькомандусаргументами,еёнужнопередаватьтакимобразом(каксписок):
Модульsubprocess
585
In[4]:result=subprocess.call(['ls','-ls'])
total3624
8-rw-r--r--1natanata372Dec1021:34LICENSE.md
16-rw-r--r--1natanata4528Jan1209:16README.md
32-rw-r--r--1natanata12480Jan2311:15SUMMARY.md
8-rw-r--r--1natanata2196Jan2309:16ToDo.md
8-rw-r--r--1natanata70Dec1021:34about.md
0drwxr-xr-x19natanata646Jan2311:05book
8-rw-r--r--1natanata355Jan1209:16book.json
0drwxr-xr-x16natanata544Dec1021:34course_presentations
2176-rw-r--r--1natanata1111234Dec1021:34course_presentations.zip
528-rw-r--r--@1natanata267824Dec1108:25cover.jpg
0drwxr-xr-x20natanata680Jan2313:05examples
360-rw-r--r--1natanata181075Jan2114:10examples.zip
0drwxr-xr-x19natanata646Jan1710:24exercises
416-rw-r--r--1natanata210621Jan2114:10exercises.zip
32-rw-r--r--1natanata14684Jan1805:33faq.md
16-rw-r--r--1natanata7043Jan1710:28howto.md
0drwxr-xr-x4natanata136Jan1411:01images
0drwxr-xr-x10natanata340Jan1708:44resources
16-rw-r--r--@1natanata6219Jan1711:37schedule.md
Всефайлы,срасширениемmd:
In[5]:result=subprocess.call(['ls','-ls','*md'])
ls:*md:Nosuchfileordirectory
Возниклаошибка.
Чтобывызыватькоманды,вкоторыхиспользуютсярегулярныевыражения,нужнодобавлятьпараметрshell:
In[6]:result=subprocess.call(['ls','-ls','*md'],shell=True)
LICENSE.mdcourse_presentationsfaq.md
README.mdcourse_presentations.ziphowto.md
SUMMARY.mdcover.jpgimages
ToDo.mdexamplesresources
about.mdexamples.zipschedule.md
bookexercises
book.jsonexercises.zip
Еслиустанавленаргументshell=True,указаннаякомандавыполняетсячерезshell.Втакомслучае,командуможноуказыватьтак:
Модульsubprocess
586
In[7]:result=subprocess.call('ls-ls*md',shell=True)
8-rw-r--r--1natanata372Dec1021:34LICENSE.md
16-rw-r--r--1natanata4528Jan1209:16README.md
32-rw-r--r--1natanata12480Jan2311:15SUMMARY.md
8-rw-r--r--1natanata2196Jan2309:16ToDo.md
8-rw-r--r--1natanata70Dec1021:34about.md
32-rw-r--r--1natanata14684Jan1805:33faq.md
16-rw-r--r--1natanata7043Jan1710:28howto.md
16-rw-r--r--@1natanata6219Jan1711:37schedule.md
Ещёоднаособенностьфункцииcall()-онаожидаетзавершениявыполнениякоманды.Еслипопробовать,например,запуститькомандуping,тоэтотаспектбудетзаметен:
In[8]:reply=subprocess.call(['ping','-c','3','-n','8.8.8.8'])
PING8.8.8.8(8.8.8.8):56databytes
64bytesfrom8.8.8.8:icmp_seq=0ttl=48time=49.868ms
64bytesfrom8.8.8.8:icmp_seq=1ttl=48time=49.243ms
64bytesfrom8.8.8.8:icmp_seq=2ttl=48time=50.029ms
---8.8.8.8pingstatistics---
3packetstransmitted,3packetsreceived,0.0%packetloss
round-tripmin/avg/max/stddev=49.243/49.713/50.029/0.339ms
Особенно,еслипопробоватьпинганутькакой-тонедоступныйIP-адрес.
Функцияcall()подходит,еслинужно:
подождатьвыполненияпрограммы,преждечемвыполнятьследующиешагинужнополучитьтолькокодвыполненияиненуженвывод
Ещёодинаспектработыфункцииcall(),онавыводитрезультатвыполнениякоманды,настандартныйпотоквывода.
Файлsubprocess_call.py:
importsubprocess
reply=subprocess.call(['ping','-c','3','-n','8.8.8.8'])
ifreply==0:
print"Alive"
else:
print"Unreachable"
Результатвыполнениябудеттаким:
Модульsubprocess
587
$pythonsubprocess_call.py
PING8.8.8.8(8.8.8.8):56databytes
64bytesfrom8.8.8.8:icmp_seq=0ttl=48time=49.930ms
64bytesfrom8.8.8.8:icmp_seq=1ttl=48time=48.981ms
64bytesfrom8.8.8.8:icmp_seq=2ttl=48time=48.360ms
---8.8.8.8pingstatistics---
3packetstransmitted,3packetsreceived,0.0%packetloss
round-tripmin/avg/max/stddev=48.360/49.090/49.930/0.646ms
Alive
Тоесть,результатвыполнениякоманды,выводитсянастандартныйпотоквывода.
Еслинужноэтоотключитьиневыводитьрезультатвыполнения,надоперенаправитьstdoutвdevnull(файлsubprocess_call_devnull.py):
importsubprocess
importos
DNULL=open(os.devnull,'w')
reply=subprocess.call(['ping','-c','3','-n','8.8.8.8'],stdout=DNULL)
ifreply==0:
print"Alive"
else:
print"Unreachable"
Теперьрезультатвыполнениябудеттаким:
$pythonsubprocess_call_devnull.py
Alive
Функцияsubprocess.check_output()
Функцияcheck_output():
позволяетвыполнитькомандуприэтом,онаожидаетзавершениякоманды.
есликомандаотработалакорректно(кодвозврата0),функциявозращаетрезультатвыполнениякомандыесливозниклаошибка,привыполнениикоманды,функциягенерируетисключение
Примериспользованияфункцииcheck_output()(файлsubprocess_check_output.py):
Модульsubprocess
588
importsubprocess
reply=subprocess.check_output(['ping','-c','3','-n','8.8.8.8'])
print"Result:"
printreply
Результатвыполнения(еслиубратьстрокуprintreply,настандартныйпотоквыводаничегонебудетвыведено):
$pythonsubprocess_check_output.py
Result:
PING8.8.8.8(8.8.8.8):56databytes
64bytesfrom8.8.8.8:icmp_seq=0ttl=48time=49.785ms
64bytesfrom8.8.8.8:icmp_seq=1ttl=48time=57.231ms
64bytesfrom8.8.8.8:icmp_seq=2ttl=48time=51.071ms
---8.8.8.8pingstatistics---
3packetstransmitted,3packetsreceived,0.0%packetloss
round-tripmin/avg/max/stddev=49.785/52.696/57.231/3.250ms
Есливыполнитькоманду,котораявызоветошибкуи,соответственно,кодвозратабудетне0(файлsubprocess_check_output_catch_exception.py):
$pythonsubprocess_check_output_catch_exception.py
ping:cannotresolvea:Unknownhost
Traceback(mostrecentcalllast):
File"subprocess_check_output_catch_exception.py",line3,in<module>
reply=subprocess.check_output(['ping','-c','3','-n','a'])
File"/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/p
ython2.7/subprocess.py",line573,incheck_output
raiseCalledProcessError(retcode,cmd,output=output)
subprocess.CalledProcessError:Command'['ping','-c','3','-n','a']'returnednon-z
eroexitstatus68
ВозниклоисключениеCalledProcessErrorисоответствующеесообщениеобошибке.
Функцияcheck_output()всегдабудетвозвращатьэтоисключение,когдакодвозвратанеравен0.
Этозначит,чтовскриптеможнонаписатьвыражениеtry/except,спомощьюкоторогобудетвыполнятьсяпроверкакорректнолиотработалакоманда(дополняемфайлsubprocess_check_output_catch_exception.py):
Модульsubprocess
589
importsubprocess
try:
reply=subprocess.check_output(['ping','-c','3','-n','a'])
exceptsubprocess.CalledProcessErrorase:
print"Erroroccurred"
print"Returncode:",e.returncode
Результатвыполнения:
$pythonsubprocess_check_output_catch_exception.py
ping:cannotresolvea:Unknownhost
Erroroccurred
Returncode:68
Теперьпрограммазавершиласькорректноивывеласообщениеобошибкеикодвозврата.И,хотясообщениеобошибке,невыводилось,онопопалонастандартныйпотоквывода.
Попробуемсобратьвсёвфинальнуюфункциюидобавимперехватсообщенияобошибке:
importsubprocess
fromtempfileimportTemporaryFile
defping_ip(ip_address):
"""
PingIPaddressandreturntuple:
Onsuccess:
*returncode=0
*commandoutput
Onfailure:
*returncode
*erroroutput(stderr)
"""
withTemporaryFile()astemp:
try:
output=subprocess.check_output(['ping','-c','3','-n',ip_address],
stderr=temp)
return0,output
exceptsubprocess.CalledProcessErrorase:
temp.seek(0)
returne.returncode,temp.read()
printping_ip('8.8.8.8')
printping_ip('a')
Модульsubprocess
590
Результатвыполнениябудеттаким:
$pythonsubprocess_ping_function.py
(0,'PING8.8.8.8(8.8.8.8):56databytes\n64bytesfrom8.8.8.8:icmp_seq=0ttl=48t
ime=46.106ms\n64bytesfrom8.8.8.8:icmp_seq=1ttl=48time=46.114ms\n64bytesfrom
8.8.8.8:icmp_seq=2ttl=48time=47.390ms\n\n---8.8.8.8pingstatistics---\n3packet
stransmitted,3packetsreceived,0.0%packetloss\nround-tripmin/avg/max/stddev=4
6.106/46.537/47.390/0.603ms\n')
(68,'ping:cannotresolvea:Unknownhost\n')
Впримереиспользованыидеиизответанаstackoverflow
МодульtempfileвходитвстандартнуюбиблиотекуPythonииспользуетсятутдлятого,чтобысохранитьсообщениеобошибке.ФункцияTemporaryFileсоздаетвременныйфайлиудаляетегоавтоматически,послетого,какфайлзакрывается.
ПодробнееомодулеtempfileможнопочитатьнасайтеPyMOTW.
Наосновеэтойфункции,можносделатьфункцию,котораябудетпроверятьсписокIP-адресовивозвращать,врезультатевыполнения,двасписка:доступныеинедоступныеадреса.
ЕсликоличествоIP-адресов,которыенужнопроверить,большое,можноиспользоватьмодульmultiprocessing,чтобыускоритьпроверку.
Этовынесеновзаданиякразделу.
Этодалеконевсевозможностимодуляsubprocess.ПодробнееонёмможнопочитатьвдокументациииливстатьеPyMOTW
Модульsubprocess
591
МодульosМодульosпозволяетработатьсфайловойсистемой,сокружением,управлятьпроцессами.
Мырассмотримлишьнесколькополезныхвозможностей.Заболееполнымописаниемвозможностеймодуля,выможетеобратитьсякдокументацииилистатьенасайтеPyMOTW.
Модульosпозволяетсоздаватькаталоги:
In[1]:importos
In[2]:os.mkdir('test')
In[3]:ls-ls
total0
0drwxr-xr-x2natanata68Jan2318:58test/
Крометого,вмодулеестьсоответствующиепроверкинасуществование.Например,еслипопробоватьповторносоздатькаталог,возникнетошибка:
In[4]:os.mkdir('test')
---------------------------------------------------------------------------
OSErrorTraceback(mostrecentcalllast)
<ipython-input-4-cbf3b897c095>in<module>()
---->1os.mkdir('test')
OSError:[Errno17]Fileexists:'test'
Втакомслучае,пригодитсяпроверкаos.path.exists:
In[5]:os.path.exists('test')
Out[5]:True
In[6]:ifnotos.path.exists('test'):
...:os.mkdir('test')
...:
Методlistdir,позволяетпосмотретьсодержимоекаталога:
In[7]:os.listdir('.')
Out[7]:['cover3.png','dir2','dir3','README.txt','test']
Модульos
592
Спомощьюпроверокos.path.isdirиos.path.isfile,можнополучитьотдельносписокфайловисписоккаталогов:
In[8]:dirs=[dfordinos.listdir('.')ifos.path.isdir(d)]
In[9]:dirs
Out[9]:['dir2','dir3','test']
In[10]:files=[fforfinos.listdir('.')ifos.path.isfile(f)]
In[11]:files
Out[11]:['cover3.png','README.txt']
Такжевмодулеестьотдельныеметодыдляработыспутями:
In[12]:os.path.basename(file)
Out[12]:'README.md'
In[13]:os.path.dirname(file)
Out[13]:'Programming/PyNEng/book/16_additional_info'
In[14]:os.path.split(file)
Out[14]:('Programming/PyNEng/book/16_additional_info','README.md')
Модульos
593
Модульargparseargparse-этомодульдляобработкиаргументовкоманднойстроки.
Примерытого,чтопозволяетделатьмодуль:
создаватьаргументыиопции,скоторымиможетвызыватьсяскриптуказыватьтипыаргументов,значенияпоумолчаниюуказыватькакиедействиясоответствуютаргументамвыполнятьвызовфункции,приуказанииаргументаотображатьсообщениясподсказамипоиспользованиюскрипта
argparseнеединственныймодульдляобработкиаргументовкоманднойстроки.Идаже,неединственныйтакоймодульвстандартнойбиблиотеке.
Мыбудемрассматриватьтолькоargparse.Но,есливыстолкнетесьснеобходимостьюиспользоватьподобныемодули,обязательнопосмотритеинатемодули,которыеневходятвстандартнуюбиблиотекуPython.Например,наclick.
Оченьхорошаястатья,котораясравниваетразныемодулиобработкиаргументовкоманднойстроки(рассматриваютсяargparse,clickиdocopt).
Примерскриптаping_function.py:
Модульargparse
594
importsubprocess
fromtempfileimportTemporaryFile
importargparse
defping_ip(ip_address,count=3):
"""
PingIPaddressandreturntuple:
Onsuccess:(returncode=0,commandoutput)
Onfailure:(returncode,erroroutput(stderr))
"""
withTemporaryFile()astemp:
try:
output=subprocess.check_output(['ping','-c',str(count),'-n',ip_addre
ss],
stderr=temp)
return0,output
exceptsubprocess.CalledProcessErrorase:
temp.seek(0)
returne.returncode,temp.read()
parser=argparse.ArgumentParser(description='Pingscript')
parser.add_argument('-a',action="store",dest="ip")
parser.add_argument('-c',action="store",dest="count",default=2,type=int)
args=parser.parse_args()
printargs
rc,message=ping_ip(args.ip,args.count)
printmessage
Созданиепарсера:
parser=argparse.ArgumentParser(description='Pingscript')
Добавлениеаргументов:
parser.add_argument('-a',action="store",dest="ip")
аргумент,которыйпередаетсяпослеопции-a,сохранитсявпеременнуюipparser.add_argument('-c',action="store",dest="count",default=2,type=int)
аргумент,которыйпередаетсяпослеопции-c,будетсохраненвпеременнуюcount,но,прежде,будетконвертированвчисло.Еслиаргументнебылоуказан,поумолчанию,будетзначение2
Строкаargs=parser.parse_args()указывается,послетогокакопределенывсеаргументы.
Модульargparse
595
Послееёвыполнения,впеременнойargsсодержатсявсеаргументы,которыебылипереданыскрипту.Книмможнообращаться,используясинтаксисargs.ip.
Попробуемвызватьскриптсразнымиаргументами.
Еслипереданыобааргумента:
$pythonping_function.py-a8.8.8.8-c5
Namespace(count=5,ip='8.8.8.8')
PING8.8.8.8(8.8.8.8):56databytes
64bytesfrom8.8.8.8:icmp_seq=0ttl=48time=48.673ms
64bytesfrom8.8.8.8:icmp_seq=1ttl=48time=49.902ms
64bytesfrom8.8.8.8:icmp_seq=2ttl=48time=48.696ms
64bytesfrom8.8.8.8:icmp_seq=3ttl=48time=50.040ms
64bytesfrom8.8.8.8:icmp_seq=4ttl=48time=48.831ms
---8.8.8.8pingstatistics---
5packetstransmitted,5packetsreceived,0.0%packetloss
round-tripmin/avg/max/stddev=48.673/49.228/50.040/0.610ms
Namespaceэтообъект,которыйвозвращаетметодparse_args()
ПередаемтолькоIP-адрес:
$pythonping_function.py-a8.8.8.8
Namespace(count=2,ip='8.8.8.8')
PING8.8.8.8(8.8.8.8):56databytes
64bytesfrom8.8.8.8:icmp_seq=0ttl=48time=48.563ms
64bytesfrom8.8.8.8:icmp_seq=1ttl=48time=49.616ms
---8.8.8.8pingstatistics---
2packetstransmitted,2packetsreceived,0.0%packetloss
round-tripmin/avg/max/stddev=48.563/49.090/49.616/0.526ms
Вызовскриптабезаргументов:
Модульargparse
596
$pythonping_function.py
Namespace(count=2,ip=None)
Traceback(mostrecentcalllast):
File"ping_function.py",line31,in<module>
rc,message=ping_ip(args.ip,args.count)
File"ping_function.py",line16,inping_ip
stderr=temp)
File"/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/p
ython2.7/subprocess.py",line566,incheck_output
process=Popen(stdout=PIPE,*popenargs,**kwargs)
File"/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/p
ython2.7/subprocess.py",line710,in__init__
errread,errwrite)
File"/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/p
ython2.7/subprocess.py",line1335,in_execute_child
raisechild_exception
TypeError:execv()arg2mustcontainonlystrings
Еслибыфункциябылавызванабезаргументов,когданеиспользуетсяargparse,возниклаошибка,чтоневсеаргументыуказаны.
Но,из-заargparse,фактическиаргументпередается,толькоонравенNone.ЭтовидновстрокеNamespace(count=2,ip=None).
Втакомскрипте,очевидно,IP-адреснеобходимоуказыватьвсегда.Ивargparseможноуказать,чтоаргументявляетсяобязательным.
Надоизменитьопцию-a:добавитьвконцеrequired=True:
parser.add_argument('-a',action="store",dest="ip",required=True)
Теперь,есливызватьскриптбезаргументов,выводбудеттаким:
$pythonping_function.py
usage:ping_function.py[-h]-aIP[-cCOUNT]
ping_function.py:error:argument-aisrequired
Теперьотображаетсяпонятноесообщение,чтонадоуказатьобязательныйаргумент.Иподсказкаusage.
Также,благодаряargparse,доступенhelp:
Модульargparse
597
$pythonping_function.py-h
usage:ping_function.py[-h]-aIP[-cCOUNT]
Pingscript
optionalarguments:
-h,--helpshowthishelpmessageandexit
-aIP
-cCOUNT
Обратитевнимание,чтовсообщении,всеопциинаходятсявсекцииoptionalarguments.argparseсамопределяет,чтоуказаныопцию,таккакониначинаютсяс-ивименитолькооднабуква.
ЗададимIP-адрес,какпозиционныйаргумент.
Файлping_function_ver2.py:
Модульargparse
598
importsubprocess
fromtempfileimportTemporaryFile
importargparse
defping_ip(ip_address,count=3):
"""
PingIPaddressandreturntuple:
Onsuccess:(returncode=0,commandoutput)
Onfailure:(returncode,erroroutput(stderr))
"""
withTemporaryFile()astemp:
try:
output=subprocess.check_output(['ping','-c',str(count),'-n',ip_addre
ss],
stderr=temp)
return0,output
exceptsubprocess.CalledProcessErrorase:
temp.seek(0)
returne.returncode,temp.read()
parser=argparse.ArgumentParser(description='Pingscript')
parser.add_argument('host',action="store",help="IPornametoping")
parser.add_argument('-c',action="store",dest="count",default=2,type=int,
help="Numberofpackets")
args=parser.parse_args()
printargs
rc,message=ping_ip(args.host,args.count)
printmessage
Теперь,вместоуказанияопции-a,можнопростопередатьIP-адрес.Онбудетавтоматическисохраненвпеременнойhost.Иавтоматическисчитаетсяобязательным.
Тоесть,теперьненужноуказыватьrequired=Trueиdest="ip".
Крометого,вскриптеуказанысообщения,которыебудутвыводиться,привызовеhelp.
Теперьвызовскриптавыглядиттак:
Модульargparse
599
$pythonping_function_ver2.py8.8.8.8-c2
Namespace(host='8.8.8.8',count=2)
PING8.8.8.8(8.8.8.8):56databytes
64bytesfrom8.8.8.8:icmp_seq=0ttl=48time=49.203ms
64bytesfrom8.8.8.8:icmp_seq=1ttl=48time=51.764ms
---8.8.8.8pingstatistics---
2packetstransmitted,2packetsreceived,0.0%packetloss
round-tripmin/avg/max/stddev=49.203/50.484/51.764/1.280ms
Асообщениеhelpтак:
$pythonping_function_ver2.py-h
usage:ping_function_ver2.py[-h][-cCOUNT]host
Pingscript
positionalarguments:
hostIPornametoping
optionalarguments:
-h,--helpshowthishelpmessageandexit
-cCOUNTNumberofpackets
Вложенныепарсеры
Рассмотримодинизспособоворганизацииболеесложнойиерархииаргументов.
Этотпримерпокажетбольшевозможностейargparse,ноониэтимнеограничиваются,поэтому,есливыбудетеиспользоватьargparse,обязательнопосмотритедокументациюмодуляилистатьюнаPyMOTW.
Файлparse_dhcp_snooping.py:
#-*-coding:utf-8-*-
importargparse
#Defaultvalues:
DFLT_DB_NAME='dhcp_snooping.db'
DFLT_DB_SCHEMA='dhcp_snooping_schema.sql'
defcreate(args):
print"CreatingDB%swithDBschema%s"%(args.name,args.schema)
defadd(args):
ifargs.sw_true:
Модульargparse
600
print"Addingswitchdatatodatabase"
else:
print"Readinginfofromfile(s)\n%s"%','.join(args.filename)
print"\nAddingdatatodb%s"%args.db_file
defget(args):
ifargs.keyandargs.value:
print"GetingdatafromDB:%s"%args.db_file
print"Requestdataforhost(s)with%s%s"%(args.key,args.value)
elifargs.keyorargs.value:
print"Pleasegivetwoorzeroargs\n"
else:
print"Showing%scontent..."%args.db_file
parser=argparse.ArgumentParser()
subparsers=parser.add_subparsers(title='subcommands',
description='validsubcommands',
help='description')
create_parser=subparsers.add_parser('create_db',help='createnewdb')
create_parser.add_argument('-n',metavar='db-filename',dest='name',
default=DFLT_DB_NAME,help='dbfilename')
create_parser.add_argument('-s',dest='schema',default=DFLT_DB_SCHEMA,
help='dbschemafilename')
create_parser.set_defaults(func=create)
add_parser=subparsers.add_parser('add',help='adddatatodb')
add_parser.add_argument('filename',nargs='+',help='file(s)toaddtodb')
add_parser.add_argument('--db',dest='db_file',default=DFLT_DB_NAME,help='dbname')
add_parser.add_argument('-s',dest='sw_true',action='store_true',
help='addswitchdataifset,elseaddnormaldata')
add_parser.set_defaults(func=add)
get_parser=subparsers.add_parser('get',help='getdatafromdb')
get_parser.add_argument('--db',dest='db_file',default=DFLT_DB_NAME,help='dbname')
get_parser.add_argument('-k',dest="key",
choices=['mac','ip','vlan','interface','switch'],
help='hostkey(parameter)tosearch')
get_parser.add_argument('-v',dest="value",help='valueofkey')
get_parser.add_argument('-a',action='store_true',help='showdbcontent')
get_parser.set_defaults(func=get)
if__name__=='__main__':
args=parser.parse_args()
args.func(args)
Модульargparse
601
Теперьсоздаетсянетолькопарсер,каквпрошломпримере,ноивложенныепарсеры.Вложенныепарсерыбудутотображатьсякаккоманды.Но,фактически,онибудутиспользоватьсякакобязательныеаргументы.
Спомощьювложенныхпарсеров,создаетсяиерархияаргументовиопций.Аргументы,которыедобавленывовложенныйпарсер,будутдоступныкакаргументыэтогопарсера.
Например,вэтойчасти,созданвложенныйпарсерcreate_dbикнемудобавленаопция-n:
create_parser=subparsers.add_parser('create_db',help='createnewdb')
create_parser.add_argument('-n',dest='name',default=DFLT_DB_NAME,
help='dbfilename')
Синтаксиссозданиявложенныхпарсеровидобавлениякнимаргументов,одинаков:
create_parser=subparsers.add_parser('create_db',help='createnewdb')
create_parser.add_argument('-n',metavar='db-filename',dest='name',
default=DFLT_DB_NAME,help='dbfilename')
create_parser.add_argument('-s',dest='schema',default=DFLT_DB_SCHEMA,
help='dbschemafilename')
create_parser.set_defaults(func=create)
Методadd_argumentдобавляетаргумент.Тутсинтаксисточнотакойже,какибезиспользованиявложенныхпарсеров.
Встроке``create_parser.set_defaults(func=create)указывается,что,привызовепарсераcreate_parser```,будетвызванафункцияcreate.
Функцияcreateполучаеткакаргумент,всеаргументы,которыебылипереданы.И,внутрифункции,можнообращатьсякнужным:
defcreate(args):
print"CreatingDB%swithDBschema%s"%(args.name,args.schema)
Есливызватьhelpдляэтогоскрипта,выводбудеттаким:
Модульargparse
602
$pythonparse_dhcp_snooping.py-h
usage:parse_dhcp_snooping.py[-h]{create_db,add,get}...
optionalarguments:
-h,--helpshowthishelpmessageandexit
subcommands:
validsubcommands
{create_db,add,get}description
create_dbcreatenewdb
addadddatatodb
getgetdatafromdb
Обратитевнимание,чтокаждыйвложенныйпарсер,которыйсозданвскрипте,отображаетсякаккомандавподсказкеusage:
usage:parse_dhcp_snooping.py[-h]{create_db,add,get}...
Укаждоговложенногопарсератеперьестьсвойhelp:
$pythonparse_dhcp_snooping.pycreate_db-h
usage:parse_dhcp_snooping.pycreate_db[-h][-ndb-filename][-sSCHEMA]
optionalarguments:
-h,--helpshowthishelpmessageandexit
-ndb-filenamedbfilename
-sSCHEMAdbschemafilename
Кромевложенныхпарсеров,вэтомпримеретакжеестьнескольконовыхвозможностейargparse.
metavar
Впарсереcreate_parserиспользуетсяновыйаргумент-metavar:
create_parser.add_argument('-n',metavar='db-filename',dest='name',
default=DFLT_DB_NAME,help='dbfilename')
create_parser.add_argument('-s',dest='schema',default=DFLT_DB_SCHEMA,
help='dbschemafilename')
Аргументmetavarпозволяетуказыватьимяаргументадлявыводавсообщенииusageиhelp:
Модульargparse
603
$pythonparse_dhcp_snooping.pycreate_db-h
usage:parse_dhcp_snooping.pycreate_db[-h][-ndb-filename][-sSCHEMA]
optionalarguments:
-h,--helpshowthishelpmessageandexit
-ndb-filenamedbfilename
-sSCHEMAdbschemafilename
Посмотритенаразницумеждуопциями-nи-s:
послеопции-n,ивusage,ивhelp,указываетсяимя,котороеуказановпараметреmetavarпослеопции-sуказываетсяимяпеременной,вкоторуюсохраняетсязначение
nargs
Впарсереadd_parserиспользуетсяnargs:
add_parser.add_argument('filename',nargs='+',help='file(s)toaddtodb')
nargsпозволяетуказать,чтовэтотаргументдолжнопопастьопределенноеколичествоэлементов.Вэтомслучае,всеаргументы,которыебылипереданыскрипту,послеимениаргументаfilename,попадутвсписокnargs.Нодолженбытьпереданхотябыодинаргумент.
Сообщениеhelp,втакомслучае,выглядиттак:
$pythonparse_dhcp_snooping.pyadd-h
usage:parse_dhcp_snooping.pyadd[-h][--dbDB_FILE][-s]
filename[filename...]
positionalarguments:
filenamefile(s)toaddtodb
optionalarguments:
-h,--helpshowthishelpmessageandexit
--dbDB_FILEdbname
-saddswitchdataifset,elseaddnormaldata
Еслипередатьнесколькофайлов,онипопадутвсписок.А,таккакфункцияadd,простовыводитименафайлов,выводполучитсятаким:
Модульargparse
604
$pythonparse_dhcp_snooping.pyaddfilenametest1.txttest2.txt
Readinginfofromfile(s)
filename,test1.txt,test2.txt
Addingdatatodbdhcp_snooping.db
nargsподдерживаеттакиезначения:
N-должнобытьуказанноеколичествоаргументов.Аргументыбудутвсписке(даже,еслиуказан1)?-0или1аргумент*-всеаргументыпопадутвсписок+-всеаргумнетыпопадутвсписок,нодолженбытьпередан,хотябы,одинаргумент
choices
Впарсереget_parserиспользуетсяchoices:
get_parser.add_argument('-k',dest="key",
choices=['mac','ip','vlan','interface','switch'],
help='hostkey(parameter)tosearch')
Длянекоторыхаргументов,важно,чтобызначениебыловыбранотолькоизорпределенныхвариантов.Длятакихслучаев,можноуказыватьchoices.
Дляэтогопарсера,helpвыглядиттак:
$pythonparse_dhcp_snooping.pyget-h
usage:parse_dhcp_snooping.pyget[-h][--dbDB_FILE]
[-k{mac,ip,vlan,interface,switch}]
[-vVALUE][-a]
optionalarguments:
-h,--helpshowthishelpmessageandexit
--dbDB_FILEdbname
-k{mac,ip,vlan,interface,switch}
hostkey(parameter)tosearch
-vVALUEvalueofkey
-ashowdbcontent
А,есливыбратьнеправильныйвариант:
Модульargparse
605
$pythonparse_dhcp_snooping.pyget-ktest
usage:parse_dhcp_snooping.pyget[-h][--dbDB_FILE]
[-k{mac,ip,vlan,interface,switch}]
[-vVALUE][-a]
parse_dhcp_snooping.pyget:error:argument-k:invalidchoice:'test'(choosefrom'm
ac','ip','vlan','interface','switch')
Вданномпримереважноуказатьвариантынавыбор,таккакзатем,наоснованиивыбранноговарианта,генерируетсяSQL-запрос.И,благодаряchoices,нетвозможноуказатькакой-топараметр,кромеразрешенных.
Импортпарсера
Вфайлеparse_dhcp_snooping.py,последниедвестрокибудутвыполнятьсятольковтомслучае,еслискриптбылвызванкакосновной.
if__name__=='__main__':
args=parser.parse_args()
args.func(args)
Азначит,еслиимпортироватьфайл,этистрокинебудутвызваны.
Попробуемимпортироватьпарсервдругойфайл(файлcall_pds.py):
fromparse_dhcp_snoopingimportparser
args=parser.parse_args()
args.func(args)
Вызовсообщенияhelp:
$pythoncall_pds.py-h
usage:call_pds.py[-h]{create_db,add,get}...
optionalarguments:
-h,--helpshowthishelpmessageandexit
subcommands:
validsubcommands
{create_db,add,get}description
create_dbcreatenewdb
addadddatatodb
getgetdatafromdb
Модульargparse
606
Вызоваргумента:
$pythoncall_pds.pyaddtest.txttest2.txt
Readinginfofromfile(s)
test.txt,test2.txt
Addingdatatodbdhcp_snooping.db
Всёработаетбезпроблем.
Передачааргументоввручную
И,последняяособенностьargparse-возможностьпередаватьаргументывручную.
Аргументыможнопередатькаксписок,привызовеметодаparse_args()(файлcall_pds2.py):
fromparse_dhcp_snoopingimportparser,get
args=parser.parse_args('addtest.txttest2.txt'.split())
args.func(args)
Необходимоиспользоватьметодsplit(),таккакметодparse_args,ожидаетсписокаргументов.
Результатбудеттаким,какеслибыскриптбылвызвансаргументами:
$pythoncall_pds2.py
Readinginfofromfile(s)
test.txt,test2.txt
Addingdatatodbdhcp_snooping.db
Модульargparse
607
МодульipaddressМодульipaddressможетпригодитсядляработысIP-адресами.
ОнневходитвстандартнуюбиблиотекуPython,поэтомуегонужноустановить:
$pipinstallipaddress
Дляработысмодулемipaddress,нужно,чтобыстроки,вкоторыхописываетсяIP-адрес,быливформатеunicode.
Дляэтого,надолибоконвертироватьстрокивunicode(еслиадресзадаетсявручную):
ipaddress.ip_address(u'1.2.3.4')
илитак:
ipaddress.ip_address(unicode('1.2.3.4'))
либоимпортироватьunicode_literalsтакимобразом:
from__future__importunicode_literals
Подробнееонюансахиспользованияunicode_literalsвPython2
Впоследнемслучае,всестрокибудутunicode.
Такоеособенностисвязаныстем,чтомодульipaddressсоздавалсядляPython3.Авнёмвсестрокиunicode.
ipaddress.ip_address()
Функцияipaddress.ip_address()позволяетсоздаватьобъектIPv4AddressилиIPv6Address,соответственно.
IPv4адрес:
Модульipaddress
608
In[1]:importipaddress
In[2]:ipv4=ipaddress.ip_address(u'10.0.1.1')
In[3]:ipv4
Out[3]:IPv4Address(u'10.0.1.1')
In[4]:printipv4
10.0.1.1
Уобъектаестьнесколькометодовиатрибутов:
In[5]:ipv4.
ipv4.compressedipv4.is_loopbackipv4.is_unspecifiedipv4.version
ipv4.explodedipv4.is_multicastipv4.max_prefixlen
ipv4.is_globalipv4.is_privateipv4.packed
ipv4.is_link_localipv4.is_reservedipv4.reverse_pointer
Спомощьюатрибутовis_можнопроверитьккакомудиапазонупринадлежитадрес:
In[6]:ipv4.is_loopback
Out[6]:False
In[7]:ipv4.is_multicast
Out[7]:False
In[8]:ipv4.is_reserved
Out[8]:False
In[9]:ipv4.is_private
Out[9]:True
Сполученнымиобъектами,можновыполнятьразличныеоперации:
Модульipaddress
609
In[10]:ip1=ipaddress.ip_address(u'10.0.1.1')
In[11]:ip2=ipaddress.ip_address(u'10.0.2.1')
In[12]:ip1>ip2
Out[12]:False
In[13]:ip2>ip1
Out[13]:True
In[14]:ip1==ip2
Out[14]:False
In[15]:ip1!=ip2
Out[15]:True
In[16]:str(ip1)
Out[16]:'10.0.1.1'
In[17]:int(ip1)
Out[17]:167772417
In[18]:ip1+5
Out[18]:IPv4Address(u'10.0.1.6')
In[19]:ip1-5
Out[19]:IPv4Address(u'10.0.0.252')
ipaddress.ip_network()
Функцияipaddress.ip_network()позволяетсоздатьобъект,которыйописываетсеть(IPv4илиIPv6).
СетьIPv4:
In[20]:subnet1=ipaddress.ip_network(u'80.0.1.0/28')
Какиуадреса,усетиестьразличныеатрибутыиметоды:
Модульipaddress
610
In[21]:subnet1.broadcast_address
Out[21]:IPv4Address(u'80.0.1.15')
In[22]:subnet1.with_netmask
Out[22]:u'80.0.1.0/255.255.255.240'
In[23]:subnet1.with_hostmask
Out[23]:u'80.0.1.0/0.0.0.15'
In[24]:subnet1.prefixlen
Out[24]:28
In[25]:subnet1.num_addresses
Out[25]:16
Методhosts()возвращаетгенератор,поэтому,чтобыпосмотретьвсехосты,надоприменитьфункциюlist:
In[26]:list(subnet1.hosts())
Out[26]:
[IPv4Address(u'80.0.1.1'),
IPv4Address(u'80.0.1.2'),
IPv4Address(u'80.0.1.3'),
IPv4Address(u'80.0.1.4'),
IPv4Address(u'80.0.1.5'),
IPv4Address(u'80.0.1.6'),
IPv4Address(u'80.0.1.7'),
IPv4Address(u'80.0.1.8'),
IPv4Address(u'80.0.1.9'),
IPv4Address(u'80.0.1.10'),
IPv4Address(u'80.0.1.11'),
IPv4Address(u'80.0.1.12'),
IPv4Address(u'80.0.1.13'),
IPv4Address(u'80.0.1.14')]
Методsubnetsпозволяетразбиватьнаподсети.Поумолчанию,онразбиваетнадвеподсети:
In[27]:list(subnet1.subnets())
Out[27]:[IPv4Network(u'80.0.1.0/29'),IPv4Network(u'80.0.1.8/29')]
Но,можнопередатьпараметрprefixlen_diff,чтобыуказатьколичествобитдляподсетей:
Модульipaddress
611
In[28]:list(subnet1.subnets(prefixlen_diff=2))
Out[28]:
[IPv4Network(u'80.0.1.0/30'),
IPv4Network(u'80.0.1.4/30'),
IPv4Network(u'80.0.1.8/30'),
IPv4Network(u'80.0.1.12/30')]
Или,спомощьюпараметраnew_prefix,простоуказатькакаямаскадолжнабытьуподсетей:
In[29]:list(subnet1.subnets(new_prefix=30))
Out[29]:
[IPv4Network(u'80.0.1.0/30'),
IPv4Network(u'80.0.1.4/30'),
IPv4Network(u'80.0.1.8/30'),
IPv4Network(u'80.0.1.12/30')]
In[30]:list(subnet1.subnets(new_prefix=29))
Out[30]:[IPv4Network(u'80.0.1.0/29'),IPv4Network(u'80.0.1.8/29')]
ПоIP-адресамвсетиможнопроходитсявцикле:
In[31]:foripinsubnet1:
....:printip
....:
80.0.1.0
80.0.1.1
80.0.1.2
80.0.1.3
80.0.1.4
80.0.1.5
80.0.1.6
80.0.1.7
80.0.1.8
80.0.1.9
80.0.1.10
80.0.1.11
80.0.1.12
80.0.1.13
80.0.1.14
80.0.1.15
Илиобращатьсякконкретномуадресу:
Модульipaddress
612
In[32]:subnet1[0]
Out[32]:IPv4Address(u'80.0.1.0')
In[33]:subnet1[5]
Out[33]:IPv4Address(u'80.0.1.5')
ТакимобразомможнопроверятьнаходитсялиIP-адресвсети:
In[34]:ip1=ipaddress.ip_address(u'80.0.1.3')
In[35]:ip1insubnet1
Out[35]:True
ipaddress.ip_interface()
Функцияipaddress.ip_interface()позволяетсоздаватьобъектIPv4InterfaceилиIPv6Interface,соответственно.
Попробуемсоздатьинтерфейс:
In[36]:int1=ipaddress.ip_interface(u'10.0.1.1/24')
ИспользуяметодыобъектаIPv4Interface,можнополучатьадрес,маскуилисетьинтерфеса:
In[37]:int1.ip
Out[37]:IPv4Address(u'10.0.1.1')
In[38]:int1.network
Out[38]:IPv4Network(u'10.0.1.0/24')
In[39]:int1.netmask
Out[39]:IPv4Address(u'255.255.255.0')
Примериспользованиямодуля
Таккаквмодульвстроеныпроверкикорректностиадресов,можноимипользоваться,например,чтобыпроверитьявляетсялиадресадресомсетиилихоста:
Модульipaddress
613
In[40]:IP1=u'10.0.1.1/24'
In[41]:IP2=u'10.0.1.0/24'
In[42]:defcheck_if_ip_is_network(ip_address):
....:try:
....:ipaddress.ip_network(ip_address)
....:returnTrue
....:exceptValueError:
....:returnFalse
....:
In[43]:check_if_ip_is_network(IP1)
Out[43]:False
In[44]:check_if_ip_is_network(IP2)
Out[44]:True
Модульipaddress
614
ПолезныефункцииВэтомразделеописанытакиефункции:
formatsortedlambdazipmapfilterall,any
Полезныефункции
615
ФункцияsortedФункцияsorted()-возвращаетновыйотсортированныйсписок,которыйполученизпоследовательности,котораябылапереданакакаргумент.Функциятакжеподдерживаетдополнительныепараметры,которыепозволяютуправлятьсортировкой.
Первыйаспектнакоторыйважнообратитьвнимание-sortedвозвращаетсписок.
Соответственно,еслисортироватьсписокэлементов,товозвращаетсяновыйсписок:
In[1]:list_of_words=['one','two','list','','dict']
In[2]:sorted(list_of_words)
Out[2]:['','dict','list','one','two']
Сортировкакортежа:
In[3]:tuple_of_words=('one','two','list','','dict')
In[4]:sorted(tuple_of_words)
Out[4]:['','dict','list','one','two']
Сортировкамножества:
In[5]:set_of_words={'one','two','list','','dict'}
In[6]:sorted(set_of_words)
Out[6]:['','dict','list','one','two']
Сортировкастроки:
In[7]:string_to_sort='longstring'
In[8]:sorted(string_to_sort)
Out[8]:['','g','g','i','l','n','n','o','r','s','t']
Еслипередатьsortedсловарь,функциявернетотсортированныйсписокключей:
Функцияsorted
616
In[9]:dict_for_sort={
...:'id':1,
...:'name':'London',
...:'IT_VLAN':320,
...:'User_VLAN':1010,
...:'Mngmt_VLAN':99,
...:'to_name':None,
...:'to_id':None,
...:'port':'G1/0/11'
...:}
In[10]:sorted(dict_for_sort)
Out[10]:
['IT_VLAN',
'Mngmt_VLAN',
'User_VLAN',
'id',
'name',
'port',
'to_id',
'to_name']
reverse
Флагreverseпозволяетуправлятьпорядкомсортировки.Поумолчаниюсортировкабудетповозрастаниюэлементов.
Указавфлагreverse,можнопоменятьпорядок:
In[11]:list_of_words=['one','two','list','','dict']
In[12]:sorted(list_of_words)
Out[12]:['','dict','list','one','two']
In[13]:sorted(list_of_words,reverse=True)
Out[13]:['two','one','list','dict','']
key
Спомощьюпараметраkeyможноуказыватькакименновыполнятьсортировку.Параметрkeyожидаетфункцию,спомощьюкоторойдолжнобытьвыполненосравнение.
Например,такимобразомможноотсортироватьсписокстрокподлинестроки:
Функцияsorted
617
In[14]:list_of_words=['one','two','list','','dict']
In[15]:sorted(list_of_words,key=len)
Out[15]:['','one','two','list','dict']
Еслинужноотсортироватьключисловаря,ноприэтомигнорироватьрегистрстрок:
In[16]:dict_for_sort={
...:'id':1,
...:'name':'London',
...:'IT_VLAN':320,
...:'User_VLAN':1010,
...:'Mngmt_VLAN':99,
...:'to_name':None,
...:'to_id':None,
...:'port':'G1/0/11'
...:}
In[17]:sorted(dict_for_sort,key=str.lower)
Out[17]:
['id',
'IT_VLAN',
'Mngmt_VLAN',
'name',
'port',
'to_id',
'to_name',
'User_VLAN']
Кромевстроенныхфункций,можнотакжеиспользоватьсвоифункции.Такжетутудобноиспользоватьанонимнуюфункциюlambda.
Также,спомощьюпараметраkey,можносортироватьобъектынепопервомуэлементу,аполюбомудругому.Нодляэтогонадоиспользоватьилифункциюlambdaилиспециальныефункцииизмодуляoperator.
Например,чтобыотсортироватьсписоккортежейиздвухэлементовповторомуэлементу,надоиспользоватьтакойприем:
Функцияsorted
618
In[18]:fromoperatorimportitemgetter
In[19]:list_of_tuples=[('IT_VLAN',320),
...:('Mngmt_VLAN',99),
...:('User_VLAN',1010),
...:('DB_VLAN',11)]
In[20]:sorted(list_of_tuples,key=itemgetter(1))
Out[20]:[('DB_VLAN',11),('Mngmt_VLAN',99),('IT_VLAN',320),('User_VLAN',1010)]
Функцияsorted
619
АнонимнаяфункцияlambdaВPythonвыражениеlambdaпозволяетсоздаватьанонимныефункции-функции,которыенепривязаныкимени.
Ванонимнойфункцииlambda:
можетсодержатьсятолькоодновыражениеаргументовможетпередаватьсясколькоугодно
Стандартнаяфункция:
In[1]:defsum_arg(a,b):returna+b
In[2]:sum_arg(1,2)
Out[2]:3
Аналогичнаяанонимнаяфункцияlanbda:
In[3]:sum_arg=lambdaa,b:a+b
In[4]:sum_arg(1,2)
Out[4]:3
Обратитевнимание,чтовопределенииlambdaнетоператораreturn,таккаквэтойфункцииможетбытьтолькоодновыражение,котороевсегдавозвращаетзначениеизавершаетработуфункции.
Функциюlambdaудобноиспользоватьввыражениях,гдетребуетсянаписатьнебольшуюфункциюдляобработкиданных.
Например,вфункцииsortedlambdaможноиспользоватьдляуказанияключадлясортировки:
In[5]:list_of_tuples=[('IT_VLAN',320),
...:('Mngmt_VLAN',99),
...:('User_VLAN',1010),
...:('DB_VLAN',11)]
In[6]:sorted(list_of_tuples,key=lambdax:x[1])
Out[6]:[('DB_VLAN',11),('Mngmt_VLAN',99),('IT_VLAN',320),('User_VLAN',1010)]
Функцияlambda
620
Такжефункцияlambdaпригодитсявфункцияхmapиfilter,которыебудутрассматриватьсявследующихразделах.
Функцияlambda
621
ФункцияzipФункцияzip():
навходфункциипередаютсяпоследовательностиzip()возвращаетсписоккортежей,вкоторомn-ыйкортежсостоитизn-ыхэлементовпоследовательностей,которыебылипереданыкакаргументы
например,десятыйкортежбудетсодержатьдесятыйэлементкаждойизпереданныхпоследовательностей
еслинавходбылипереданыпоследовательностиразнойдлинны,товсеонибудутотрезаныпосамойкороткойпоследовательностипорядокэлементовсоблюдается
Примериспользованияzip:
In[1]:a=[1,2,3]
In[2]:b=[100,200,300]
In[3]:zip(a,b)
Out[3]:[(1,100),(2,200),(3,300)]
Использованиеzip()соспискамиразнойдлины:
In[4]:a=[1,2,3,4,5]
In[5]:b=[10,20,30,40,50]
In[6]:c=[100,200,300]
In[7]:zip(a,b,c)
Out[7]:[(1,10,100),(2,20,200),(3,30,300)]
Использованиеzipдлясозданиясловаря:
Функцияzip
622
In[8]:d_keys=['hostname','location','vendor','model','IOS','IP']
In[8]:d_values=['london_r1','21NewGlobeWalk','Cisco','4451','15.4','10.255
.0.1']
In[9]:zip(d_keys,d_values)
Out[9]:
[('hostname','london_r1'),
('location','21NewGlobeWalk'),
('vendor','Cisco'),
('model','4451'),
('IOS','15.4'),
('IP','10.255.0.1')]
In[10]:dict(zip(d_keys,d_values))
Out[10]:
{'IOS':'15.4',
'IP':'10.255.0.1',
'hostname':'london_r1',
'location':'21NewGlobeWalk',
'model':'4451',
'vendor':'Cisco'}
In[11]:r1=dict(zip(d_keys,d_values))
Функцияzip
623
ФункцияmapФункцияmapприменяетфункциюккаждомуэлементупоследовательностиивозвращаетсписокрезультатов.
Например,спомощьюmapможновыполнятьпреобразованияэлементов.Перевестивсестрокивверхнийрегистр:
In[1]:list_of_words=['one','two','list','','dict']
In[2]:map(str.upper,list_of_words)
Out[2]:['ONE','TWO','LIST','','DICT']
Конвертациявчисла:
In[3]:list_of_str=['1','2','5','10']
In[4]:map(int,list_of_str)
Out[4]:[1,2,5,10]
Вместесmapудобноиспользоватьlambda:
In[5]:vlans=[100,110,150,200,201,202]
In[6]:map(lambdax:'vlan{}'.format(x),vlans)
Out[6]:['vlan100','vlan110','vlan150','vlan200','vlan201','vlan202']
Еслифункция,которуюиспользуетmap(),ожидаетдвааргумента,топередаютсядвасписка:
In[7]:nums=[1,2,3,4,5]
In[8]:nums2=[100,200,300,400,500]
In[9]:map(lambdax,y:x*y,nums,nums2)
Out[9]:[100,400,900,1600,2500]
Функцияmap
624
ФункцияfilterФункцияfilter()применяетфункциюковсемэлементампоследовательности,ивозвращаеттеобъекты,длякоторыхфункциявернулаTrue.
Например,вернутьтолькотестроки,вкоторыхнаходятсячисла:
In[1]:list_of_strings=['one','two','list','','dict','100','1','50']
In[2]:filter(str.isdigit,list_of_strings)
Out[2]:['100','1','50']
Например,изспискачиселоставитьтольконечетные:
In[3]:filter(lambdax:x%2,[10,111,102,213,314,515])
Out[3]:[111,213,515]
Аналогично,толькочетные:
In[4]:filter(lambdax:notx%2,[10,111,102,213,314,515])
Out[4]:[10,102,314]
Изспискасловоставитьтолькоте,укоторыхколичествобуквбольшедвух:
In[5]:list_of_words=['one','two','list','','dict']
In[6]:filter(lambdax:len(x)>2,list_of_words)
Out[6]:['one','two','list','dict']
Функцияfilter
625
ФункцияallФункцияall()возвращаетTrue,есливсеэлементыистина(илиобъектпустой).
In[1]:all([False,True,True])
Out[1]:False
In[2]:all([True,True,True])
Out[2]:True
In[3]:all([])
Out[3]:True
Например,спомощьюallможнопроверитьвселиоктетывIP-адресеявляютсячислами:
In[4]:IP='10.0.1.1'
In[5]:all(i.isdigit()foriinIP.split('.'))
Out[5]:True
In[6]:all(i.isdigit()foriin'10.1.1.a'.split('.'))
Out[6]:False
ФункцияanyФункцияany()возвращаетTrue,еслихотябыодинэлементистина(илиобъектпустой).
In[7]:any([False,True,True])
Out[7]:True
In[8]:any([False,False,False])
Out[8]:False
In[9]:any([])
Out[9]:False
In[10]:any(i.isdigit()foriin'10.1.1.a'.split('.'))
Out[10]:True
Функцииanyиall
626
Функцииanyиall
627
СоглашениеобименахВPythonестьопределенныесоглашенияобименованииобъектов.
Вцелом,лучшепридерживатьсяэтихсоглашений.Однако,есливопределеннойбиблиотекеилимодулеиспользуютсядругиесоглашения,тостоитпридерживатьсятогостиля,которыйиспользуетсявних.
Вэтомразделеописаныневсеправила.ПодробнееможнопочитатьвдокументеPEP8наанглийскомилинарусском.
Именапеременных
Именапеременныхнедолжныпересекатьсясоператорамииназваниямимодулейилидругихзарезервированныхзначений.
Именапеременныхобычнопишутсяполностьюбольшимиилималенькимибуквами.Впределаходногоскрипта/модуля/пакеталучшепридерживатьсяодногоизвариантов.
Еслипеременные-константыдлямодуля,толучшеиспользоватьименанаписанныезаглавнымибуквами:
DB_NAME='dhcp_snooping.db'
TESTING=True
Дляобычныхпеременныхлучшеиспользоватьименавнижнемрегистре:
db_name='dhcp_snooping.db'
testing=True
Именамодулейипакетов
Именамодулейипакетовзадаютсямаленькимибуквами.
Модулимогутиспользоватьподчеркиваниямеждусловамидлятогочтобыименабылиболеепонятными.Дляпакетовлучшевыбиратькороткиеимена.
Именафункций
Именафункцийзадаютсямаленькимибуквами,сподчеркиваниямимеждусловами.
Соглашениеобименах
628
defignore_command(command,ignore):
ignore_command=False
forwordinignore:
ifwordincommand:
returnTrue
returnignore_command
Именаклассов
Именаклассовзадаютсясловамисзаглавнымибуквами,безпробелов.
classCiscoSwitch:
def__init__(self,name,vendor='cisco',model='3750'):
self.name=name
self.vendor=vendor
self.model=model
Соглашениеобименах
629
ПодчеркиваниевименахВPythonподчеркиваниевначалеиливконцеимениуказываетнаспециальныеимена.Чащевсего,этовсеголишьдоговоренность,ноиногдаэтодействительновлияетнаповедениеобъекта.
Подчеркиваниекакимя
ВPythonодноподчеркиваниеиспользуетсядляобозначениятого,чтоданныепростовыбрасываются.
Например,еслиизстрокиlineнадополучитьMAC-адрес,IP-адрес,VLANиинтерфейсиотброситьостальныеполя,можноиспользоватьтакойвариант:
In[1]:line='00:09:BB:3D:D6:5810.1.10.286250dhcp-snooping10FastEthernet0
/1'
In[2]:mac,ip,_,_,vlan,intf=line.split()
In[3]:printmac,ip,vlan,intf
00:09:BB:3D:D6:5810.1.10.210FastEthernet0/1
Такаязаписьговоритотом,чтонамненужнытретийичетвертыйэлементы.
Можносделатьтак:
In[4]:mac,ip,lease,entry_type,vlan,intf=line.split()
Нотогдадальшеможетбытьнепонятнопочемупеременныеleaseиentry_typeнеиспользуютсядальше.Еслипонятнейиспользоватьимена,толучшеназватьпеременныеименамивроде-ignored.
Аналогичныйприемможетиспользоваться,когдапеременнаяцикланенужна:
In[5]:[0for_inrange(10)]
Out[5]:[0,0,0,0,0,0,0,0,0,0]
Подчеркиваниевинтерпретаторе
Винтерпретатореpythonиipythonподчеркиваниеиспользуетсядляполучениярезультатапоследнеговыражения
Подчеркиваниевименах
630
In[6]:[0for_inrange(10)]
Out[6]:[0,0,0,0,0,0,0,0,0,0]
In[7]:_
Out[7]:[0,0,0,0,0,0,0,0,0,0]
In[8]:a=_
In[9]:a
Out[9]:[0,0,0,0,0,0,0,0,0,0]
Одноподчеркивание
Одноподчеркиваниепередименем
Одноподчеркиваниепередименемуказывает,чтоимяиспользуетсякаквнутреннее.
Например,еслиодноподчеркиваниеуказановименифункцииилиметода,этоозначает,тоэтотобъектявляетсявнутреннейособенностьюреализацииинестоитегоиспользоватьнапрямую.
Но,крометого,приимпортевидаfrommoduleimport*небудутимпортироватьсяобъекты,которыеначинаютсясподчеркивания.
Например,вфайлеexample.pyтакиепеременныеифункции:
db_name='dhcp_snooping.db'
_path='/home/nata/pyneng/'
deffunc1(arg):
printarg
def_func2(arg):
printarg
Еслиимпортироватьвсеобъектыизмодуля,тоте,которыеначинаютсясподчеркиваниянебудутимпортированы:
Подчеркиваниевименах
631
In[7]:fromexampleimport*
In[8]:db_name
Out[8]:'dhcp_snooping.db'
In[9]:_path
...
NameError:name'_path'isnotdefined
In[10]:func1(1)
1
In[11]:_func2(1)
...
NameError:name'_func2'isnotdefined
Одноподчеркиваниепослеимени
Одноподчеркиваниепослеименииспользуетсявтомслучае,когдаимяобъектаилипараметрапересекаетсясвстроеннымиименами.
Пример:
In[12]:line='00:09:BB:3D:D6:5810.1.10.286250dhcp-snooping10FastEthernet
0/1'
In[13]:mac,ip,lease,type_,vlan,intf=line.split()
Дваподчеркивания
Дваподчеркиванияпередименем
Дваподчеркиванияпередименемметода,используютсянепростокакдоговоренность.Такиеименатрасформируютсявформат"имякласса+имяметода".Этопозволяетсоздаватьуникальныеметодыиатрибутыклассов.
Такоепреобразованиевыполняетсятольковтомслучае,есливконцеменеедвухподчеркиванийилинетподчеркиваний.
Подчеркиваниевименах
632
In[14]:classSwitch(object):
...:__quantity=0
...:def__configure(self):
...:pass
...:
In[15]:dir(Switch)
Out[15]:
['_Switch__configure','_Switch__quantity',...]
Хотяметодысоздавалисьбезприставки_Switch,онабыладобавлена.
Еслисоздатьподкласс,тометод__configureнеперепишетметодродительскогоклассаSwitch:
In[16]:classCiscoSwitch(Switch):
...:__quantity=0
...:def__configure(self):
...:pass
...:
In[17]:dir(CiscoSwitch)
Out[17]:
['_CiscoSwitch__configure','_CiscoSwitch__quantity','_Switch__configure','_Switch__
quantity',...]
Дваподчеркиванияпередипослеимени
Такимобразомобозначаютсяспециальныепеременныеиметоды.
Например,вмодулеPythonестьтакиеспециальныепеременные:
__name__-этапеременнаяравнастроке__main__,когдаскриптзапускаетсянапрямую.Иравенименимодуля,когдаимпортируется__file__-этапеременнаяравнаименискрипта,которыйбылзапущеннапрямую.Иравнаполномупутикмодулю,когдаонимпортируется
Переменная__name__,чащевсего,используетсячтобыуказать,чтоопределеннаячастькодадолжнавыполнятьсятолькокогдамодульвыполняетсянапрямую:
Подчеркиваниевименах
633
defmultiply(a,b):
returna*b
if__name__=="__main__":
printmultiply(3,5)
Апеременная__file__можетбытьполезнавопределениитекущегопутикфайлускрипта:
importos
print'__file__',__file__
printos.path.abspath(__file__)
Выводбудеттаким:
__file__example2.py
/Users/natasha/Desktop/current/pyneng_things/example2.py
Крометого,такимобразомвPythonобозначаютсяспециальныеметоды.ЭтиметодывызываютсяприиспользованиифункцийиоператоровPythonипозволяютреализоватьопределенныйфункционал.
Какправило,такиеметодыненужновызыватьнапрямую.Но,например,присозданиисвоегокласса,можетпонадобитьсяописатьтакойметод,чтобыобъектподдерживалкакие-тооперациивPython.
Например,длятогочтобыможнобылополучитьдлинуобъекта,ондолженподдерживатьметод__len__.
Ещёодинспециальныйметод__str__вызывается,когдаиспользуетсяоператорprintиливызываетсяфункцияstr().Еслинеобходимо,чтобыприэтомотображениебыловопределенномвиде,надосоздатьэтотметодвклассе:
Подчеркиваниевименах
634
In[10]:classSwitch(object):
...:
...:defset_name(self,name):
...:self.name=name
...:
...:def__configure(self):
...:pass
...:
...:def__str__(self):
...:return'Switch{}'.format(self.name)
...:
In[11]:sw1=Switch()
In[12]:sw1.set_name('sw1')
In[13]:printsw1
Switchsw1
In[14]:str(sw1)
Out[14]:'Switchsw1'
ТакихспециальныхметодоввPythonоченьмного.Несколькополезныхссылок,гдеможнопочитатьпроконкретныйметод:
документацияDiveIntoPython3-хотятутпримерыдляPython3,большинствометодовработаютаналогично.Вэтойкнигеотличнорасписаныэтыметоды.
Подчеркиваниевименах
635
ДополнительныересурсыКакправило,информациютяжелоусвоитьспервогораза.Особенно,новуюинформацию.
Еслиделатьпрактическиезаданияипометки,входеизучения,тоусвоитсянамногобольшеинформации,чем,еслипросточитатькурс.Но,скореевсего,вкаком-товиде,надобудетчитатьотойжеинформациинесколькораз.
ВэтомразделесобраныссылкинаразличныересурсыпоPython,связанныессетямиинет.Этирусурсыпозволятвамповторитьинформацию,почитать(посмотреть,послушать)тожесамое,нодругимисловами.Крометого,данынесколькоресурсовдляпродолженияобучения.
ПодкастыобавтоматизациисPythonХорошоподходятдлявдохновлениятемойавтоматизации
PacketPushersShow176–IntroToPython&AutomationForNetworkEngineersPacketPushersShow198–KirkByersOnNetworkAutomationWithPython&AnsiblePacketPushersShow270:Design&Build9:AutomationWithPythonAndNetmikoPQShow99:Netmiko&NAPALMForNetworkAutomationNetworkautomationtoolswithJasonEdelmanonSofwareGoneWildPacketPushersShow332:Don’tBelieveTheProgrammingHype-отличныйподкастобавтоматизации,программировании,скриптингеикаквсёэтоотноситсяксетевыминженерам.PacketPushersShow333:Automation&OrchestrationInNetworking
Блоги(python+network)KirkByersJasonEdelmanMattOswaltMichaelKashinHenryÖlsnerMatWood
Полезныессылки
636
Платныеибесплатныекурсы(python+network)
Яэтикурсынепроходила,поэтомуонисобранывпроизвольномпорядке,просточтобыбылопрощеоценить,чтоестьнасегодняшнийденьивыбратьто,чтобольшенравится.
Бесплатныекурсы:
БесплатныйemailкурсотKirkByersNetworkProgrammabilityandNetworkAutomationusingGNS3andPythonКурсынаCiscoDevNet
Платныекурсы
PythonforNetworkEngineers(курсотKirkByers)КурсыотNetworktoCodePythonProgrammingforNetworkEngineers(INE)NetworkAutomationwithAnsible(INE)PythonProgrammingForNetworkEngineers(Udemy)PythonNetworkProgramming-Part1:Build7PythonApps(GNS3Academy)NetworkAutomationusingPythonforEngineers(ehackingacademy)Networkautomationwebinars(IvanPepelnjak)
Полезныессылки
637
МатериалыпотемамкурсаТутсобраныссылкипотемамкурса.Большаячастьизнихпересекаютсястемамикурса,ноестьидополнения.Влюбомслучае,дажете,которыепересекаютсяполезнопочитать.
Ссылкинастатьиидругиересурсы
ОсновыPython
КнигипоPython:
AByteofPythonэтажекниганарусском
Python101LearnPythontheHardWay
КурсыпоосновамPython:
ПрограммированиенаPython
Регулярныевыражения
Статьи:
ParseCiscoIOSconfigurationsusingRegExUsingPythontogenerateCiscoconfigs
Сайтдляпроверкирегулярныхвыражений:
regex101
Базыданных
БолееподробноеописаниевозможностейSQLite:
SQLitetutorial
Telnet,SSH
Статьи:
Материалыпотемамкурса
638
NetmikoLibraryAutomateSSHconnectionswithnetmikoNetworkAutomationUsingPython:BGPConfiguration
Jinja2
Статьи:
NetworkConfigurationTemplatesUsingJinja2.MattOswaltPythonAndJinja2Tutorial.JeremySchulmanConfigurationGeneratorwithPythonandJinja2CustomfiltersforaJinja2basedConfigGenerator
TextFSM
Статьи:
ProgrammaticAccesstoCLIDeviceswithTextFSM.JasonEdelman(26.02.2015)-основыTextFSMиидеиоразвитии,которыелегливосновумодуляntc-ansibleParseCLIoutputswithTextFSM.HenryÖlsner(24.08.2015)-примериспользованияTextFSMдляразборабольшогофайласвыводомshinventory.ПодробнееобъясняетсясинтаксисTextFSMCreatingTemplatesforTextFSMandntc_show_command.JasonEdelman(27.08.2015)-подробнеерассматриваетсясинтаксисTextFSMипоказаныпримерыиспользованиямодуляntc-ansible(обратитевнимание,чтосинтаксисмодуляуженемногоизменился)TextFSMandStructuredData.KirkByers(22.10.2015)-вводнаястатьяоTextFSM.Тутнеописываетсясинтаксис,нодаетсяобщеепредставлениеотом,чтотакоеTextFSMипримерегоиспользования
Проекты,которыеиспользуютTextFSM:
Модульntc-ansible
ШаблоныTextFSM(измодуляntc-ansible):
ntc-templates
Ansible
УAnsibleоченьхорошаядокументация:
http://docs.ansible.com/ansible/
Материалыпотемамкурса
639
ОтличныевидеоотAnsible:
AUTOMATINGYOURNETWORKРепозиторийспримерамиизвебинара
Ansibleбезпривязкиксетевомуоборудованию
Оченьхорошаясериявидео,странскриптомихорошимиссылками:
https://sysadmincasts.com/episodes/43-19-minutes-with-ansible-part-1-4
ПримерыиспользованияAnsible:
https://github.com/ansible/ansible-examples
ПримерыPlaybookсдемонстрациейразличныхвозможностей
https://github.com/ansible/ansible-examples/tree/master/language_features
Ansiblefornetworkdevices
Обращайтевниманиенавремянаписаниястатьи.ВAnsibleсущественноизменилисьмодулидляработыссетевымоборудованием.Ивстатьяхмогутбытьещёстарыепримеры.
NetworkConfigTemplatingusingAnsible(KirkByers):
https://pynet.twb-tech.com/blog/ansible/ansible-cfg-template.htmlhttps://pynet.twb-tech.com/blog/ansible/ansible-cfg-template-p2.htmlhttps://pynet.twb-tech.com/blog/ansible/ansible-cfg-template-p3.html
Статьи:
http://jedelman.com/home/ansible-for-networking/http://jedelman.com/home/network-automation-with-ansible-dynamically-configuring-interface-descriptions/http://www.packetgeek.net/2015/08/using-ansible-to-push-cisco-ios-configurations/
Оченьхорошаясериястатей.Постепенноповышаетсяуровеньсложности:
http://networkop.github.io/blog/2015/06/24/ansible-intro/http://networkop.github.io/blog/2015/07/03/parser-modules/http://networkop.github.io/blog/2015/07/10/test-verification/http://networkop.github.io/blog/2015/07/17/tdd-quickstart/http://networkop.github.io/blog/2015/08/14/automating-legacy-networks/http://networkop.github.io/blog/2015/08/26/automating-network-build-p1/
Материалыпотемамкурса
640
http://networkop.github.io/blog/2015/09/03/automating-bgp-config/http://networkop.github.io/blog/2015/11/13/automating-flexvpn-config/
СсылкинадокументациюДокументациямодулей,которыеиспользовалисьвкурсе:
reYAMLCSVJSONsqlite3TelnetlibPexpectParamikoNetmikothreadingmultiprocessingJinja2TextFSMAnsible
Материалыпотемамкурса
641
Продолжениеобучения
Pythonбезпривязкиксетевомуоборудованию
ЕсливаминтересноразвиватьсвоизнанияпоPythonвцелом,ниженесколькополезныхссылок.
TheHitchhiker’sGuidetoPython!BestPythonResources-подборкассылокнахорошиересурсыпоPythonAutomatetheBoringStuffwithPythonProblemSolvingwithAlgorithmsandDataStructuresusingPython-отличнаякнигапоструктурамданныхиалгоритмам.Многопримеровидомашнихзаданий.
нарусском
Онлайнкурсы:
MITx-6.00.1xIntroductiontoComputerScienceandProgrammingUsingPython
Видеокурсы:
PythonотComputerScienceCenter
Сайтысзадачами:
CodeEvalHackerRank
Python+network
Лучшевсегопослекурсапопытатьсяприменитьполученныезнаниявработе.Этотвариантпозволитлучшезапомнитьто,чтовыужевыучили.Крометого,такимобразомзнаниярасширитьбудетпроще,таккакувасбудетпрактическаязадача,которуюнадорешить.
Еслиговоритьоразвитиидальшеприменимоксетямисетевомуоборудованию,тотутвариантовмного.Это,втомчисле,иразвитиезнанийпоPythonвцелом,нотакжеиболееспецифическиевещи,такиекакдоступксетевомуоборудованиючерезAPI,идр.Нижессылкинанекоторыепроекты,которыевасмогутзаинтересовать.
Проекты:
ScapyCiscoConfParse
Продолжениеобучения
642
NAPALMNOCProjectRequestsNetworkXTwistedAutoNetKit
Продолжениеобучения
643
ОтзывычитателейислушателейкурсаНаxgu.ruнаходиласьинформацияизпервыходинадцатиразделов.Сейчассxgu.ruвсёудаленоиперенесенонаGitBook.
Ильяпроверсиюкурсанаxgu.ru
БлагодарякурсуPythonдлясетевыхинженеровотНаташиСамойленко,язахотелсменитьквалификациюнадевелопера,ужеуспешнорешилрядрутинныхрабочихзадач,постояннонадоедавшихсвоимоднообразием.Всеначиналосьспростойстатьинаxgu.ru,нопотомэтосталочемтобольшим.
ПростотаиграциозностьописанияавтоматизациипроцессовНаташиСамойленкопозволиламнеоткрытьдверьвранеенедоступныймодный"DevOps".Всвязисэтим,помиморазвитиясвоихпрофессиональныхнавыков,ятакжеполучилзначительныйбонуснарынкетрудаввидедополнительныхзнаний.МнекакчеловекукоторыйизучалнемногоDelphiвуниверситете,даитонедостаточноглубоко,былодовольноинтересноиувлекательноразбиратьсясновойдляменястязей.Подачаматериалакрайне"легка"длявосприятия,инаглядна.Хорошиеиполезныевежедневнойработепримеры.
СпасибоНаташезаотличныйкурс
АлексейКирилловпроонлайнкурс
Обэтомкурсеяузналсовершеннослучайно.Наташапредложиламоемунепосредственномуначальникупрочитатьданныйкурсдляподчиненныхинженеров.Переднашимотделомкакразстоялаактуальнаязадачатестированияоборудования.Посленепродолжительногосогласованиямыприступиликобучению.
Длябольшинстваизнасэтобылопервоезнакомствосpython.Ноблагодаряотличнойподачематериала,атакжезаданиямсразнымуровнемсложности,обучениепроходиловесьмаинтересноипродуктивно.Ксожалению,невсетемынашлиприменениевнашейработе,ноглавнаяцельбыладостигнута-мыначалисоздаватьсистемуавтоматизированноготестирования.Причемэтизнанияпригодилисьнетолькодляоднойконретнойзадачи,нотакжепозволилирешитьмножестворутинныхзадач.Аизнекоторыхскриптоввырослиотдельныепроекты.
Отзывы
644
Делозамалым-интересом.Подход,предлагаемыйНаташейпомогаетнелезтьвдебрипрограммирования,адаетинструментдляавтоматизации(актонехочетиметьбольшесвободноговремени:)),которыйлегоквпониманиичеловеку,которыйдоэтогоработалтолькоссетями.Доэтогокурсаяпыталсяизучатьpythonпопопулярнымкнигамвинтернете,нокаждыйразэтобыстрозаканчивалосьиз-заскучностиинепониманиякакямогуэтоприменить.Вкурсежепрактическинакаждуютемуестьзадачи,покоторымвывидитепрактическоеприменениетогоилииногообъектаязыка.
Отзывы
645