Это часть серии статей, посвященных принципам сетевой автоматизации.

Декларативная и Императивная — это противоположные парадигмы программирования, которые описывают, описываете ли вы «как». В императивном программировании вы описываете «как», описывая отдельные шаги в декларативном программировании, вы описываете конечное состояние, не беспокоясь о «как». Скорее всего, без дополнительного контекста это мало что значит.

Ниже можно найти отличный пример из реального мира:

Декларативное программирование похоже на просьбу друга нарисовать пейзаж. Вам все равно, как они это рисуют, это их дело.

Императивное программирование похоже на то, что ваш друг слушает, как Боб Росс говорит им, как рисовать пейзаж. Хотя старый добрый Боб Росс не совсем командует, он дает им пошаговые инструкции для достижения желаемого результата.

Классический пример сети в сетевом пространстве — это switchport trunk allowed vlan 10,20,30vs, switchport trunk allowed vlan add 30и switchport trunk allowed vlan remove 15он хорошо задокументирован для создания хаоса.

Пример сетевой автоматизации

Если вам было поручено управлять набором VLAN, вы можете использовать идемпотентный дизайн, чтобы гарантировать, что VLAN существует. Это было бы полезно при развертывании новых VLAN, но как обеспечить, чтобы [10, 20, 30]существовали только VLAN, а не какие-либо другие? Используя традиционный клиентский интерфейс Cisco IOS, вы должны сначала понять, какие VLAN включены в конфигурацию, например, [10, 15, 20]и VLAN, которые не должны присутствовать, и понять те, которые необходимо добавить.

При императивном подходе вы должны определить, как вы добавляете и удаляете сети VLAN, и каждый разработчик должен решить, как это сделать. Это может привести к развертыванию различных механизмов с ожидаемыми разными результатами.

>>> def add_vlan(add_vlans):
...     for vlan in add_vlans:
...         print("vlan {}".format(vlan))
...         print(" name VLAN{}".format(vlan))
...
>>> def remove_vlan(removed_vlans):
...     for vlan in remove_vlans:
...         print("no vlan {}".format(vlan))
...
>>> expected_vlans = [30]
>>> remove_vlans = [15]
>>>
>>> add_vlan(expected_vlans)
vlan 30
 name VLAN30
>>> remove_vlan(remove_vlans)
no vlan 15
>>>

Однако при декларативном подходе вам просто нужно указать конечное состояние:

 

>>> def declarative_vlan(existing_vlans, expected_vlans):
...     persisted_vlans = list(set(expected_vlans).intersection(set(existing_vlans)))
...     # shown for completeness, no functional value here
...     add_vlans = list(set(expected_vlans).difference(set(existing_vlans)))
...     remove_vlans = list(set(existing_vlans).difference(set(expected_vlans)))
...     for vlan in add_vlans:
...         print("vlan {}".format(vlan))
...         print(" name VLAN{}".format(vlan))
...     for vlan in remove_vlans:
...         print("no vlan {}".format(vlan))
...
>>> existing_vlans = [10, 15, 20]
>>> expected_vlans = [10, 20, 30]
>>> declarative_vlan(existing_vlans, expected_vlans)
vlan 30
 name VLAN30
no vlan 15
>>>

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

Как вы можете видеть, «шаги» все еще были необходимы, например, функция должна была определить, какие виртуальные локальные сети добавить, а какие — удалить и создать эту конфигурацию. Однако впоследствии это делается один раз для всех случаев, и каждому разработчику не нужно придумывать собственное решение.

Сетевой ландшафт

В настоящее время полностью декларативными конфигурациями в Cisco IOS можно управлять с помощью метода замены конфигурации, который был подробно описан еще в 2007 году Иваном Пепельняком . Это действительно помогает перейти к окончательному состоянию и убедиться, что все конфигурации соответствуют вашим задумкам, как показано в примере с виртуальными локальными сетями. Однако для достижения этой цели требуется достаточно зрелый жизненный цикл автоматизации сети. Более реалистично, вам придется использовать каждую «функцию» по отдельности.

Junos предоставляет механизм для замены в строфе, который аналогичен использованию «функции», как описано ранее. Это бесценная функция, которая обеспечивает встроенную интеграцию с ОС поставщика. Аналогичная функциональность теперь существует для различных протоколов * CONF, документация поставщика предоставит представление о текущей поддержке.

Вкратце обсудим, как можно использовать декларативный подход на уровне функций в мире Cisco IOS, поскольку для правильного анализа потребуется охватить от одного до многих блогов.

  • Cisco IOS NETCONF / RESTCONF — эти протоколы на основе * CONF поддерживают замену функциональности
  • Иерархическая конфигурация — предоставляет примитивы для понимания того, как управлять конфигурациями, без каких-либо убеждений в том, как вы должны реализовать, и лучше описана здесь.
  • Yangify / Rosetta — новая библиотека, предназначенная для помощи в синтаксическом анализе и трансляции конфигурации в модели данных, и она воплощает декларативный подход.
  • Ansible Resource — недавно созданные модули ресурсов, которые предлагают «заменяющие функции».
  • Теория множеств. Используя теорию множеств, как показано в примере сетей VLAN, базовыми конфигурациями довольно легко управлять, используя тот же шаблон.

Вывод

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

Это не означает, что декларативный подход — единственный метод, который когда-либо должен быть развернут, например, вы можете захотеть иметь строгий контроль над тем, кто может удалять VLAN, и предоставление декларативного подхода не приведет к четкому разделению этих обязанностей. Кроме того, пользователю не всегда может быть ясно, что она что-то удалит, как в случае с switchport trunk allowed vlanкомандой.