Программирование для сетевых инженеров: работа с конфигурацией — SENETSY

Программирование для сетевых инженеров: работа с конфигурацией

23.10.2019 Москва
JUNIPER SUMMIT
Регистрация

Курс обучения Juniper Networks
В ПОДАРОК

Правила акции

Статьи и заметки сотрудников
7 Июня 2018
Автор: Андрей Андреев

Программирование для сетевых инженеров: работа с конфигурацией

Даже в хорошей, с точки зрения дизайна, сети время от времени приходится проводить работы по актуализации конфигураций тех или иных сущностей. Среди наиболее веских и ожидаемых причин подобной активности можно отметить миграции для согласования физической и логической плоскостей, развитие сети в рамках процесса технологической эволюции, гармонизация архитектур присоединяемых сегментов и решение проблем роста. На самом деле, жизненный цикл сети, почти всегда представляет из себя изменения с тем или иным уровнем рисков и панируемого влияния на сервис, в оценке которого нельзя не учитывать человеческий фактор. Хотя, будет вполне уместным обобщить это описание на большинство областей человеческой деятельности, функционирование коммуникационных сетей обладает некоторыми особенностями, которые заслуживают понимания, или хотя бы внимания, — элементы коммуникационных сетей находятся в плотной связи, тесно взаимодействуют и оказывают не только прямое, но и косвенное воздействие друг на друга. Поэтому, грамотная стратегия проводимых работ станет только продуктивнее будучи подкреплённой механизмами, которые снижают, насколько это возможно, вероятность появления человеческих ошибок. В очередной статье цикла «Зачем сетевым инженерам программирование» расскажу о вариантах применения автоматизации в одной задаче подобного рода.

Во многих «букварях» по сетевой архитектуре термины large-scale networks и hierarchical networks используются почти как синонимы, применительно к дизайну IGP домена обозначен простой и, на первый взгляд, понятный дизайн — дробление домена на области. При этом, порог насыщения — вещь сугубо индивидуальная, в каких-то случаях в одной IGP области может нормально сосуществовать тысяча современных маршрутизаторов, в ином случае и трехсот достаточно чтобы существенно снизить эксплуатационные характеристики. По большей части этот порог определяется наложенными на IGP протоколами, поэтому в условиях некоторой вариативности и неопределенности границ и масштабов будущей сети, «зубры» индустрии находят более гибким путь построения плоского домена с последующей миграцией его сформировавшихся частей в отдельные области. Иными словами, если вы не можете «сегодня смотреть в завтрашний день», стройте плоскую сеть и дробите ее по факту появления первых признаков деградации времени сходимости или излишней сложности эксплуатации, потому-как отсутствие иерархии все-таки лучше, чем неправильно выбранная в условиях неопределенности структура [1, 2, 3].

Довольно общих описаний и абстрактных примеров, предположим, что мы имеем дело с разросшейся областью OSPFv2 домена, топология физических волокон/каналов которого уже позволяет разглядеть в аморфной структуре границы будущих областей. Я не сторонник менять что-то по принципу «все или ничего», может так случится, что из-за непредвиденных обстоятельств пространство возможностей станет слишком узким, а ожидаемые риски слишком большими. Смена OSPFv2 области является деструктивной операцией, поэтому самая прямолинейная схема процесса миграции, которая заключается в последовательном переводе маршрутизаторов в новую область по пути от будущей границе вниз, удобна далеко во всех топологиях. К счастью, некоторое время назад в рекомендациях к развитию OSPF была обозначена возможность построения связности в рамках нескольких областей [4]. Пользуясь этой возможностью, мы можем разбить миграцию на три этапа:

  1. Построение дополнительной (secondary) связности в рамках новой области
  2. Перевод новой области в основной режим, а старой в дополнительный
  3. Удаление связности в рамках старой области

На первом этапе топология новой области постепенно расширяется, на втором, топология старой области сужается. При соблюдении конгруэнтности топологий старой и новой областей, мы получим согласованную маршрутизацию между переведенными и не переведенными узлами, а это, в свою очередь, делает возможным деление этапов на независимые под-этапы, на каждом из которых работа может производится с некоторой частью узлов. К выбору узлов для работ в рамках под-этапа целесообразно подходить с точки зрения фактической утилизации каналов в конкретной сети, таким образом, чтобы не допускать перезагрузку на границе старой и новой областей. Несмотря на то, что статья посвящена программированию, думаю, не будет лишним сказать пару слов о поведении наложенных на IGP протоколов в контексте смены областей. LDP ведет себя довольно предсказуемо, плавность перевода достигается обеспечением IP связности между transport-address таргетированных и интерфейсных сессий. В силу того, что BGP очень гибкий протокол, стоит обратить пристальное внимание на эту плоскость. Хорошо, если на переводимых узлах BGP не привносит в выбор пути особенных вольностей в виде несогласованного между узлами изменения Local Preferences или других критериев, обуславливающих локальный routing decision. Иными словами, поведение BGP тоже является предсказуемым, в том случае если внутри области выбор маршрута не противоречил правилу follow IGP path. Есть тонкий момент, связанный с работой RSVP в период времени сосуществования двух областей. Почву для размышлений подбрасывает факт наполнения TED таблицы из двух источников с разной видимостью, например, вы вполне можете лишится механизмов FRR до полного окончания работ. Это может случится если маршрутизаторы с большей видимостью, которые находятся в двух областях, посчитают ERO через маршрутизатор с меньшей видимостью, т.е. через переведенный сегмент сети без топологической информации о другой области. Подобное нельзя назвать катастрофой, так как путь в итоге конечно будет установлен, но и забывать об этом не стоит. Особенного искусства требует совмещение работ по внедрению иерархий IGP с работами такого же рода в BGP плоскости, например, миграцией full-mesh в отражатели маршрутов. Внедрение иерархий в разных плоскостях лучше проводить последовательно, то же самое, по моему мнению, относится ко всем вещам, которые прячут маршрутную информацию, суммаризацию проще построить на стабильном фундаменте иерархического IGP, нежели совмещать эти этапы.

Итак, общий план у нас есть, и, если вы еще не передумали, давайте поразмыслим над программной реализацией на Pyez, примерно такой как на этом рисунке.

Каждый красивый овал, может представлять из себя отдельную программную сущность, т.е. такую вещь, которая выполняет работу и готовит данные для следующего этапа. Под исходными данными подразумевается информация из сети, например, чтобы подготовить конфигурацию новой OSPF области, необходимо получить имена активных интерфейсов, принадлежащей старой области, и их веса, а значит нам пригодятся умения работы с Operation таблицами и представлениями. С этими понятиями можно познакомится в предыдущей статье цикла «Программирование для сетевых инженеров: первый кейс». На первом этапе генератор сохраняет в файлы команды необходимые для активации новой области, на втором — для удаления старой и перевода новой в основной режим, на третьем — команды по удалению старой области. Забегая вперед скажу, что для данной статьи я намеренно не пользуюсь визуализаторами шаблонов, наподобие Jinja2. Чтобы не отвлекаться от целевой темы код написан максимально просто. Если вот кому-то интересно документация по ссылке «Jinja2 Documentation».

Код генерации конфигураций этапов

import sys
import yaml
from jnpr.junos.factory.factory_loader import FactoryLoader
from jnpr.junos import Device

def getConnection(p_host, p_user):
           acc = {'lab': 'lab123'}

           try:
                      print '             DEBUG --- getting ssh connection to ' + p_host

                      l_dev = Device(host=p_host, user=p_user, password=acc[p_user] auto_probe=2,
gather_facts=True, port=22)
                      l_dev.open()
                      if (l_dev.connected):
                                   l_dev.timeout = 900
                                   print '             DEBUG --- ssh cionnection to host ' + p_host +
' established'
                                   print '             DEBUG --- connection ' + p_host + ' named ' +
l_dev.facts['hostname'] + ' established '
                                   return l_dev
                      else:
                                   raise Exception(' DEBUG --- ssh connection to '
+ p_host + ' was not established')
           except Exception as ex:
                                   print '             DEBUG --- ERROR --- getConnection : connection to ' +
p_host + ' was not established. ex:' + str(ex)
                                   return

def close_dev(d):
           try:
                                   d.close()
           except Exception as ex:
                                   print '             DEBUG --- ERROR --- close_dev : connection to cant be
closed. ex:' + str(ex)


yml = '''
---
OSPFInterface:
    rpc: get-ospf-interface-information

    args:
      area: '0.0.0.200'
      detail: True
    item: ospf-interface
    view: OSPFInterfaceView

OSPFInterfaceView:
    fields:
      name: interface-name
      type: interface-type
      cost: interface-cost
'''


globals().upd ate(FactoryLoader().load(yaml.load(yml)))

node_list = ['10.83.20.68', '10.83.20.69', '10.83.20.70', '10.83.20.71']
abr_list = ['10.83.20.66', '10.83.20.67']

for node in node_list + abr_list:
      ddev = getConnection(node, 'lab')
      ospf_interfaces = OSPFInterface(ddev).get()

         with open('st2-' + node + '.txt', "w") as f_st1:
                    f_st1.write('delete protocols ospf area 0.0.0.200' + '\n')
                    f_st1.write('delete protocols ospf area 0.0.0.250' + '\n')
                    for ospf_interface in ospf_interfaces:
                        f_st1.write('set protocols ospf area 0.0.0.250 interface ' + ospf_interfa
ce.name + ' interface-type p2p' + '\n')
                        f_st1.write('set protocols ospf area 0.0.0.200 interface ' + ospf_interfa
ce.name + ' secondary' + '\n')
                        f_st1.write('set protocols ospf area 0.0.0.200 interface ' + ospf_interfa
ce.name + ' interface-type p2p' + '\n')
                        if (ospf_interface.cost != '0'):
                               f_st1.write('set protocols ospf area 0.0.0.250 interface ' + ospf_int
erface.name + ' metric ' + ospf_interface.cost + '\n')
                               f_st1.write('set protocols ospf area 0.0.0.200 interface ' + ospf_int
erface.name + ' metric ' + ospf_interface.cost + '\n')

         with open('st3-' + node + '.txt', "w") as f_st1:
                    f_st1.write('delete protocols ospf area 0.0.0.200' + '\n')

Для наглядности я собрал в вот такую сеть на vMX.

vMX это полноценный программный маршрутизатор от Juniper Networks, во многом повторяющий поведение своего старшего, железного брата MX. Не то чтобы это был продукт, который нуждается в представлении, просто такие вещи полезно держать под рукой для упражнений в программировании или лабораторных испытаний. Триал, можно скачать тут «vMX Trial Download» инструкцию по установке по этой ссылке «Preparing the System to Install vMX».

Маршрутизаторы bb, abr1 и abr2 образуют нулевую область, маршрутизаторы r1, r2, r3 и r4 — должны быть перемещены из 200-й области в 250-ю.

lab@bb> show configuration protocols

bgp {
           group int {
                     type internal;
                     local-address 10.0.0.65;
                     neighbor 10.0.0.66;
                     neighbor 10.0.0.67;
                     neighbor 10.0.0.68;
                     neighbor 10.0.0.69;
                     neighbor 10.0.0.70;
                     neighbor 10.0.0.71;
           }
}
ospf {
           area 0.0.0.0 {
                     interface lo0.0 {
                                 passive;
                     }
                     interface ge-0/0/3.0 {
                                 interface-type p2p;
                     }
                     interface ge-0/0/2.0 {
                                 interface-type p2p;
                     }
           }
}



lab@abr1> show configuration protocols

bgp {
           group int {
                     type internal;
                     local-address 10.0.0.66;
                     neighbor 10.0.0.65;
                     neighbor 10.0.0.67;
                     neighbor 10.0.0.68;
                     neighbor 10.0.0.69;
                     neighbor 10.0.0.70;
                     neighbor 10.0.0.71;
           }
}
ospf {
           area 0.0.0.0 {
                     interface lo0.0 {
                                 interface-type p2p;
                     }
                     interface ge-0/0/3.0 {
                                 interface-type p2p;
                     }
                     interface ge-0/0/0.0 {
                                 interface-type p2p;
                     }
           }
           area 0.0.0.200 {
                     interface ge-0/0/1.0 {
                                 interface-type p2p;
                     }
           }
}


lab@abr2> show configuration protocols

bgp {
           group int {
                     type internal;
                     local-address 10.0.0.67;
                     neighbor 10.0.0.65;
                     neighbor 10.0.0.66;
                     neighbor 10.0.0.68;
                     neighbor 10.0.0.69;
                     neighbor 10.0.0.70;
                     neighbor 10.0.0.71;
           }
}
ospf {
           area 0.0.0.0 {
                     interface lo0.0 {
                                 interface-type p2p;
                     }
                     interface ge-0/0/0.0 {
                                 interface-type p2p;
                     }
                     interface ge-0/0/2.0 {
                                 interface-type p2p;
                     }
           }
           area 0.0.0.200 {
                     interface ge-0/0/1.0 {
                                 interface-type p2p;
                     }
           }
}



lab@r1> show configuration protocols

bgp {
           group int {
                     type internal;
                     local-address 10.0.0.68;
                     neighbor 10.0.0.65;
                     neighbor 10.0.0.66;
                     neighbor 10.0.0.67;
                     neighbor 10.0.0.69;
                     neighbor 10.0.0.70;
                     neighbor 10.0.0.71;
           }
}
ospf {
           area 0.0.0.200 {
                     interface ge-0/0/1.0 {
                                 interface-type p2p;
                     }
                     interface ge-0/0/2.0 {
                                 interface-type p2p;
                     }
                     interface ge-0/0/3.0 {
                                 interface-type p2p;
                     }
                     interface lo0.0 {
                                 interface-type p2p;
                     }
           }
}


lab@r2> show configuration protocols

bgp {
           group int {
                     type internal;
                     local-address 10.0.0.69;
                     neighbor 10.0.0.65;
                     neighbor 10.0.0.66;
                     neighbor 10.0.0.67;
                     neighbor 10.0.0.68;
                     neighbor 10.0.0.70;
                     neighbor 10.0.0.71;
           }
}
ospf {
           area 0.0.0.200 {
                     interface ge-0/0/0.0 {
                                 interface-type p2p;
                     }
                     interface ge-0/0/3.0 {
                                 interface-type p2p;
                     }
                     nterface lo0.0 {
                                 interface-type p2p;
                     }
           }
}


lab@r3> show configuration protocols

bgp {
           group int {
                     type internal;
                     local-address 10.0.0.70;
                     neighbor 10.0.0.65;
                     neighbor 10.0.0.66;
                     neighbor 10.0.0.67;
                     neighbor 10.0.0.68;
                     neighbor 10.0.0.69;
                     neighbor 10.0.0.71;
           }
}
ospf {
           area 0.0.0.200 {
                     interface ge-0/0/0.0 {
                                 interface-type p2p;
                     }
                     interface ge-0/0/3.0 {
                                 interface-type p2p;
                     }
                     nterface lo0.0 {
                                 interface-type p2p;
                     }
           }
}


lab@r4> show configuration protocols

bgp {
           group int {
                     type internal;
                     local-address 10.0.0.71;
                     neighbor 10.0.0.65;
                     neighbor 10.0.0.66;
                     neighbor 10.0.0.67;
                     neighbor 10.0.0.68;
                     neighbor 10.0.0.69;
                     neighbor 10.0.0.70;
           }
}
ospf {
           area 0.0.0.200 {
                     interface ge-0/0/1.0 {
                                 interface-type p2p;
                     }
                     interface ge-0/0/2.0 {
                                 interface-type p2p;
                     }
                     interface ge-0/0/3.0 {
                                 interface-type p2p;
                     }
                     nterface lo0.0 {
                                 interface-type p2p;
                     }
           }
}


Применительно к маршрутизатору r2, будет сгенерирован следующий набор команд.

Для первого этапа

set protocols ospf area 0.0.0.250 interface ge-0/0/1.0 secondary
set protocols ospf area 0.0.0.250 interface ge-0/0/1.0 interface-type p2p
set protocols ospf area 0.0.0.250 interface ge-0/0/1.0 metric 1
set protocols ospf area 0.0.0.250 interface ge-0/0/2.0 secondary
set protocols ospf area 0.0.0.250 interface ge-0/0/2.0 interface-type p2p
set protocols ospf area 0.0.0.250 interface ge-0/0/2.0 metric 1
set protocols ospf area 0.0.0.250 interface ge-0/0/3.0 secondary
set protocols ospf area 0.0.0.250 interface ge-0/0/3.0 interface-type p2p
set protocols ospf area 0.0.0.250 interface ge-0/0/3.0 metric 1


Для второго этапа

delete protocols ospf area 0.0.0.200
delete protocols ospf area 0.0.0.250
set protocols ospf area 0.0.0.250 interface ge-0/0/1.0 interface-type p2p
set protocols ospf area 0.0.0.200 interface ge-0/0/1.0 secondary
set protocols ospf area 0.0.0.200 interface ge-0/0/1.0 interface-type p2p
set protocols ospf area 0.0.0.250 interface ge-0/0/1.0 metric 1
set protocols ospf area 0.0.0.200 interface ge-0/0/1.0 metric 1
set protocols ospf area 0.0.0.250 interface ge-0/0/2.0 interface-type p2p
set protocols ospf area 0.0.0.200 interface ge-0/0/2.0 secondary
set protocols ospf area 0.0.0.200 interface ge-0/0/2.0 interface-type p2p
set protocols ospf area 0.0.0.250 interface ge-0/0/2.0 metric 1
set protocols ospf area 0.0.0.200 interface ge-0/0/2.0 metric 1
set protocols ospf area 0.0.0.250 interface ge-0/0/3.0 interface-type p2p
set protocols ospf area 0.0.0.200 interface ge-0/0/3.0 secondary
set protocols ospf area 0.0.0.200 interface ge-0/0/3.0 interface-type p2p
set protocols ospf area 0.0.0.250 interface ge-0/0/3.0 metric 1
set protocols ospf area 0.0.0.200 interface ge-0/0/3.0 metric 1
set protocols ospf area 0.0.0.250 interface lo0.0 interface-type p2p
set protocols ospf area 0.0.0.200 interface lo0.0 secondary
set protocols ospf area 0.0.0.200 interface lo0.0 interface-type p2p


Для третьего этапа

delete protocols ospf area 0.0.0.200

Как я уже писал элементы сети обладают внутренним состоянием, вокруг и внутри них что-то постоянно происходит независимо от того хотим мы этого или нет. Появление таких событий как отказ трансивера, отключение питания на узле или обрыв оптического канала нужно воспринимать как должное и учитывать при проведении работ. Я предлагаю обратить внимание на этот факт в контексте выбора проверок успешности этапов миграции. Как бы парадоксально это не звучало, в данном случае нам не обязательно проверять появилось ли OSPF соседство на том или ином интерфейсе, так как причины его отсутствия могут находится совсем в другой плоскости. Кроме перечисленного выше в исходные данные всегда может попасть ошибка или неточность, например, например, в виде всеми забытого интерфейса, который подлежит удалению, а в данный момент смотрит в «пустоту». В процессе формирования проверок полезно подняться на уровень выше, чтобы посмотреть на сеть с точки зрения оказываемых услуг и наложенных протоколов, так как в конечном счете состояние только этих вещей позволяет принять обоснованное решение об успешности того или иного этапа. Мало кто из заказчиков соглашается на публичные статьи по результатам выполненных работ, еще и поэтому я собрал виртуальную среду для демонстрации. В рамках этой среды я ограничусь проверкой состояния BGP сессий, которые построены по схеме каждый-с-каждым между маршрутизаторами, это и будет нашими наложенными сервисами, и услугами. Если, после работ выполняются условия для их работоспособности в виде IP связности каждый-с-каждым, значит есть и основания полагать что работы проведены успешно. В реальных проектах проверки обычно ограничиваются тем объемом услуг и наложенных протоколов, которые обозначается как business critical в данной сети. К программированию это не относится, тем не менее я не могу не заострить внимание на этом вопросе, так как выбор критерия успешности — это залог спокойного сна после проведенных работ.

Код проверки BGP сессий

Спойлер


Результаты его работы

Содержание спойлера 1

До текущего момента мы беседовали по большей части о каких-то сетевых вещах, но я еще ни словом ни обмолвился о программном коде, который вроде как должен быть в центре внимания в статье с таким названием. Во-первых, мне кажется, что мысли намного важнее кода, формализовав сущности в голове инженер сможет превратить их в инструмент и изложить в виде кода. Во-вторых, мне не хочется повторятся, все что может показаться не очевидным в этих примерах, а также минимально необходимые знания для начала работы с Python и Pyez, содержатся в предыдущей статье цикла. В-третьих, код подготовки конфигурации осуществляет свою работу, что называется в офлайне и не оказывает мгновенного воздействия на сеть, поэтому вряд ли может вызвать какой-то интерес. Несколько иначе обстоят дела с кодом имплементации этапа исполнения, разработка этих процедур ставит перед нами следующие вопросы:

  • Как проверить что весь объем команд адекватно восприняты интерпретатором?
  • Как проверить что вносимые в конфигурацию изменения были применены?
  • Как осуществить транзакционную схему внесения конфигурации?
  • Как сделать откат конфигурации в случае потери управления?
  • Как почистить за собой если в процессе передачи команд интерпретатор сообщил об ошибке?

Чем больше внимания будет уделено этим вопросам, тем реже на этапе исполнения будут служатся неожиданности. Представьте каких хлопот может принести синтаксическая ошибка описания политики маршрутизации, когда классификаторы этой политики только частично одобрены интерпретатором. Если не проконтролировать построчно процесс слияния этой политики с текущей конфигураций можно одним нажатием клавиши перевести груду оборудования в неадекватное состояние. Поэтому, я придерживаюсь примерно такой блок схемы этапа исполнения.

Вооружившись документацией по Pyez был составлен следующий код универсального загрузчика конфигурации, который вы можете использовать в своей повседневной работе, так же как я использую его в своей.

conf_change.py

Содержание спойлера

И результаты его работы по трем этапам

Скрытый текст

Содержание спойлера

  1. The Complete IS-IS Routing Protocol — Hannes Gredler, Walter Goralski
  2. OSPF; Anatomy of an Internet Routing Protocol — John T. Moy
  3. Network Mergers and Migrations: Junos Design and Implementation — Gonzalo Gomez Herrero, Jan Anton Bernal Van Der Ven
  4. OSPF Multi-Area Adjacency —tools.ietf.org/html/rfc5185
Источник: Хабрахабр, Андрей Андреев
Наверх
Остались вопросы? Нужна бесплатная консультация?
Свяжитесь с нами ​любым ​удобным для вас способом и ​мы предложим​ ​оптимальное решение вашей задачи
Если вы обращаетесь по вопросам, связанным с уже имеющимся у вас оборудованием или программным продуктом, пожалуйста пришлите его серийный номер
Написать нам