Блог

  • Принципы автоматизации – декларативные / императивные

    Принципы автоматизации – декларативные / императивные

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

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

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

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

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

    Классический пример сети в сетевом пространстве – это 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командой.

  • Принципы автоматизации – DRY

    Принципы автоматизации – DRY

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

    DRY в компьютерных науках

    Фраза Do Not Repeat Yourself или DRY- довольно простое понятие. Если вы пишете фрагмент кода несколько раз, сделайте его модульным таким образом, чтобы не копировать код несколько раз. Определение DRY гласит:

    «Каждая часть знания должна иметь единственное, недвусмысленное и авторитетное представление в системе».

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

    В контексте контекста, Барт Симпсон, который снова и снова пишет одну и ту же фразу на доске, определенно не DRY.

    Пример

    В этом первом надуманном примере вы увидите дублирование кода при проверке данных VLAN.

    >>> def add_vlan(vlan_id):
    ...     if not isinstance(vlan_id, int):
    ...         raise TypeError("VLAN ID is not an integer")
    ...     elif not (vlan_id >=1 and vlan_id <= 4096):
    ...         raise ValueError("Invalid VLAN ID, which must be between 1-4096")
    ...     return "vlan {vlan_id}".format(vlan_id=vlan_id)
    ...
    >>> def configure_port_vlan(interface, vlan_id):
    ...     if not isinstance(vlan_id, int):
    ...         raise TypeError("VLAN ID is not an integer")
    ...     elif not (vlan_id >=1 and vlan_id <= 4096):
    ...         raise ValueError("Invalid VLAN ID, which must be between 1-4096")
    ...     return "interface {interface}\n switchport access vlan {vlan_id}".format(interface=interface, vlan_id=vlan_id)
    ...
    >>>

     

    DRY данные

    Вы заметите, что в официальном определении есть ссылка на «знание», которое, как указано в Pragmatic Programmer

    «Знания о системе намного шире, чем просто ее код. Это относится к схемам баз данных, планам тестирования, системе сборки и даже документации ».

    Проще говоря, DRY применим не только к коду. Один из примеров, понятный сетевым инженерам, — это управление данными для маски подсети. Я часто сталкиваюсь с решениями данных и связанных файлов Jinja, которые похожи на:

    subnets:
      - network: 10.1.1.0
        cidr: 24
        subnet_mask: 255.255.255.0
        wildcard_mask: 0.0.0.255
      - network: 10.1.2.0
        cidr: 24
        subnet_mask: 255.255.255.0
        wildcard_mask: 0.0.0.255
    {% if ansible_network_os = "ios" %}
      ip address {{ subnet['network'] | ipaddr('add', 1 ) }} {{ subnet['subnet_mask'] }}{# example result: 10.1.1.1 255.255.255.0 #}
    {% elif ansible_network_os = "nxos" %}
      ip address {{ subnet['network'] | ipaddr('add', 1 ) }}/{{ subnet['cidr'] }}{# example result: 10.1.1.1/24 #}
    {% endif %}

    Это решение, безусловно, имеет свои преимущества, однако, учитывая огромное количество управляемых подсетей, вполне вероятно, что в какой-то момент появится пользователь, который создаст cidrиз 25и subnet_maskкак 255.255.255.0. Однако подсеть и подстановочную маску можно определить из файла cidr, и многократное ведение данных является излишним и подверженным ошибкам. Единовременная стоимость создания перевода гарантирует, что вы не будете повторяться и не столкнетесь с проблемами, описанными здесь.

    Вместо этого вы можете управлять своими данными более DRY, например:

    subnets:
      - network: 10.1.1.0/24
      - network: 10.1.2.0/24
    {% if ansible_network_os = "ios" %}
      ip address {{ subnet['network'] | ipaddr('add', 1 ) }} {{ subnet['network'] | ipaddr('netmask') }}{# example result: 10.1.1.1 255.255.255.0 #}
    {% elif ansible_network_os = "nxos" %}
      ip address {{ subnet['network'] | ipaddr('add', 1 ) }}/{{ subnet['network'] | ipaddr('prefix') }}{# example result: 10.1.1.1/24 #}
    {% endif %}

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

    Намокать в DRY мире

    Если DRY не повторяет какой-либо элемент знаний, обратное будет «Записывать каждый раз» (WET). Зачем вам повторяться? Что ж, как всегда, это зависит от обстоятельств, и всегда необходимо учитывать конструктивные особенности.

    Например, обычная модель в конечном итоге, — это клиенты, которые создают один шаблон Jinja для IOS, NXOS и EOS. Поскольку эти три ОС имеют много общего, с точки зрения DRY вы можете быть склонны писать свои шаблоны в виде объединенных шаблонов.

    .
    ├── bgp.j2
    ├── ntp.j2
    └── vlan.j2
    
    0 directories, 3 files

    С примером файла Jinja, например

    {% for vlan in vlans %}
    vlan {{ vlan['id'] }}
     name {{ vlan['name'] }}
    {% endfor %}
    !

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

    {% if ansible_network_os == 'ios' %}
    router bgp {{ bgp['asn'] }}
     bgp router-id {{ bgp['id'] }}
     bgp log-neighbor-changes
    {% for neighbor in bgp['neighbors'] %}
     neighbor {{ neighbor['ip'] }} remote-as {{ neighbor['asn'] }}
     neighbor {{ neighbor['ip'] }} description {{ neighbor['description'] }}
    {% endfor %}
     address-family ipv4
    {% for net in bgp['networks'] %}
      network {{ net | ipaddr('network') }} mask {{ net | ipaddr('netmask') }}
    {% endfor %}
    {% for neighbor in bgp['neighbors'] %}
      neighbor {{ neighbor['ip'] }} activate
    {% endfor %}
     exit-address-family
    {# 
    --------------
    SWITCH TO NXOS
    --------------
    #}
    {% elif ansible_network_os == 'nxos' %}
    router bgp {{ bgp['asn'] }}
      router-id {{ bgp['id'] }}
      address-family ipv4 unicast
    {% for net in bgp['networks'] %}
        network {{ net }}
    {% endfor %}
    {% for neighbor in bgp['neighbors'] %}
      neighbor {{ neighbor['ip'] }} remote-as {{ neighbor['asn'] }}
        description {{ neighbor['description'] }}
        address-family ipv4 unicast
    {% endfor %}
    {% endif %}

    Как видите, все очень быстро усложняется. Несколько основных вариантов, каждый со своими плюсами и минусами.

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

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

    .
    ├── eos
    │   ├── bgp.j2
    │   ├── ntp.j2
    │   └── vlan.j2
    ├── ios
    │   ├── bgp.j2
    │   ├── ntp.j2
    │   └── vlan.j2
    └── nxos
        ├── bgp.j2
        ├── ntp.j2
        └── vlan.j2
    
    3 directories, 9 files

    Подобные ситуации будут возникать постоянно, и вам просто нужно будет использовать опыт / интуицию, чтобы понять влияние и принять оттуда лучшее решение.

    Вывод

    Управление кодом или данными в большинстве случаев должно выполняться СУХИМ способом, поскольку этот метод сокращает количество ошибок и способствует оптимизации вычислений. При этом есть некоторые конструктивные особенности, при которых может иметь смысл использовать подход WET.

  • Оптимизация ИТ-инфраструктуры: с чего начать и как избежать проблем в процессе

    Оптимизация ИТ-инфраструктуры: с чего начать и как избежать проблем в процессе

    В этой статье я рассказываю, какую пользу можно получить от оптимизации ИТ-инфраструктуры, а также то, как избежать распространенных трудностей в процессе. Мы неоднократно помогали клиентам с оптимизацией инфраструктуры, и все примеры, приведенные в статье – из собственного опыта.

    Для удобства я разделил текст на три части:

    • для чего нужна оптимизация инфраструктуры;
    • какие основные этапы оптимизации;
    • какие сложности могут возникнуть в процессе и как с ними справиться.

    Какие преимущества оптимизации инфраструктуры

    Оптимизация инфраструктуры начинается с анализа системы. В зависимости от того, где находится система: на вашем сервере (on-premise) или в облаке, преимущества оптимизации инфраструктуры отличаться.

    Также существует комбинированный подход (использование и сервера, и облака), который рассмотрю позже.

    Одним из преимуществ переноса инфраструктуры в облака является концепция общей ответственности  – за некоторые аспекты отвечает провайдер облака, а другие – клиент. Например, облака берут на себя вопросы эксплуатации, контроля и управления компонентами. Это охватывает всё от уровня виртуализации и операционной системы узла до уровня физической безопасности объектов, где работает сервис.

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

    Для некоторых компаний переход в облачную инфраструктуру – это замечательный путь оптимизации расходов. Например, один из наших клиентов международный стриминговий сервис – требовал решения, которое было бы дешевле содержать и выдерживало высокую нагрузку в 36000000 уникальных посетителей и более 10000000000 запросов в неделю. Мы выбрали оптимизацию путем создания Kubernetes кластера в облаке AWS. Наши разработчики создали 30 микросервисив, содержание которых обходится клиенту примерно в 2000 USD ежемесячно.

    Для сравнения, неоптимизированного архитектура, состоящая из EC2 OnDemand instances, обходилась нашем в клиенту больше чем в 38000 USD в месяц.

    Также у команды был такой случай: услуги клиента работали в EC2 instances через службу ECS. Однако после того, как мы проанализировали схемы использования услуг, периоды пиковой нагрузки и расходы на управление средой, предложили клиенту заменить экземпляры EC2 на Farate, безсерверное решение, которое не требует управления базовой инфраструктурой серверов. В результате решение помогло клиенту значительно сэкономить.

    Также, например, имплементировать новые аппликации в облаке часто гораздо быстрее и дешевле, чем on-premise. Разработчики создают прототип, тестируют его в облаке, и если сервис не оправдал надежд, его можно просто отключить, освободив тем самым ресурсы облака и деньги на содержание. В случае же on-premise пришлось бы сначала вложить деньги в физические серверы, которые затем могли не использоваться.

    Оптимизация on-premise инфраструктуры зачастую заключается в сокращении расходов за уменьшения количества машин или удешевлении машин. Нужно учитывать то, что, уменьшая расходы, вы уступаете чем-то другим. Например, из-за уменьшения количества машин в дата-центре ваш сайт или приложение может не выдержать пиковой нагрузки.

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

    Собственно, аудит и является первым этапом оптимизации инфраструктуры.

    Какие ключевые этапы оптимизации инфраструктуры?

    аудит ИТ инфраструктуры

    Этот этап помогает понять, насколько целесообразно оптимизировать инфраструктуру в вашем случае.

    Во время этого этапа DevOps-эксперты анализируют, как используется облако. Или администраторы анализируют, как работает сервер. Для этого обычно используют такие инструменты, как AWS Audit Manager от Amazon. Этот инструмент собирает метрики, которые затем можно проанализировать. Например, если анализ показал, что система никогда не использует более 6 Гб памяти с 16 доступных, то это повод для оптимизации путем уменьшения объема памяти.

    Планирование новой или оптимизированной инфраструктуры

    Поскольку есть много подходов к оптимизации инфраструктуры, планирование становится очень важным аспектом. Среди основных подходов к оптимизации – уменьшение количества машин, уменьшение памяти, объединение сервисов в кластеры и другие.

    В случае с Kubernetes кластером / Doker контейнером можно, например, создать систему из пяти машин и запустить на них 10 аппликаций. Такой подход помогает удешевить хостинг и не иметь визуальных проблем при выходе машины из строя. Так, если одна из машин выйдет из строя, система автоматически перенесет сервисы с неё на остальные машины. И наоборот, если машина возвращается в систему, то и сервисы на нее тоже возвращаются.

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

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

    Например, в системе бизнес-процесс, который запускается в начале месяца и работает один день. В остальные дни он не используется. В таком случае нецелесообразно хранить его on-premise и платить за него весь месяц. Лучше хранить его в облаке и платить только за несколько дней в месяц, когда он и используется.

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

    Имплементация плана оптимизации

    Если просто, имплементация – это перенос приложений с одних машин на другие. Как я уже писал, оптимизация on-premise инфраструктуры – это преимущественно уменьшение количества машин.

    Согласно классификации, есть 6 основных вариантов миграции:

    • Рехостинг / Re-host (lift and shift).
    • Реплатформинг / Re-platform (lift, tinker, and shift).
    • Модернизация / Modernize.
    • Переписывание / Rewrite.
    • Выбросить и купить готовое / Drop and shop.
    • Оставить как есть / Retain.

    Чтобы определить лучший метод именно для вашего проекта, сначала нужно провести тщательный анализ вашего приложения . Нужно оценить, возможно ли просто перенести аппликации, или программа требует совершенствования до / после миграции. Иногда невозможно перенести данные без совершенствования программного кода.

    Например, если система не поддержит многопоточность, сессионность или использования приложения во многих экземплярах, то мы навредим, запустив несколько экземпляров системы одновременно.

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

    Реплатформинг охватывает некоторые дальнейшие коррективы. Например, переход на полностью управляемые услуги. Этот подход, при правильной реализации, может существенно сократить расходы на инфраструктуру.

    Одна из крупнейших телеком-компаний Европы – Lebara, мигрировала свои данные в облако именно с помощью реплатформингу. В результате они смогли значительно улучшить масштабирования. Кроме того, переход на облако помог сократить расходы на инфраструктуру на  25-30%.

    При модернизации некоторые компоненты программы совершенствуются до или после миграции. Таким образом этот подход требует больше времени на изменения в коде.

    Переписывание – это сложный процесс преобразования приложений с адаптацией к облачной инфраструктуры. В зависимости от типа аппликации можно существенно сократить затраты на инфраструктуру.

    Drop and shop – здесь все просто, если на рынке есть приложения, которые вас удовлетворяют, то лучше купить их, чем создавать свои. Обычно это так же уменьшит цену инфраструктуры.

    Retain – это пассивный метод, поскольку миграция не нужна. Вы сохраняете программы как есть, где они есть.

    Какие сложности могут возникнуть в процессе оптимизации

    Сложности оптимизации я тоже условно делю на сложности on-premise и облака.

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

    Кроме того, сложности есть уникальность каждой инфраструктуры. Это касается и on-premise, и облачной инфраструктуры.

    К примеру, один клиентов, ранее работавший с другим разработчиком программного обеспечения, имел в системе бизнес-процесс, созданный еще 10 лет назад. Этот процесс посылал мейлы пользователям системы. Так как разработчики процесса уже ушли из проекта, команда не знала, на что может повлиять его оптимизация. В результате оптимизации сообщения пользователям перестали поступать. Соответственно через оптимизацию часть системы перестала работать. Чтобы такого не происходило, важно уделять внимание сохранению знаний на проектах.

    оптитмизация ИТ инфраструктурыСохранение знаний на проект в ИТ-аутсорсинга

    Итак, чтобы провести успешную оптимизацию инфраструктуры, необходимо:

    1. Осуществить детальный аудит системы.
    2. Найти способ оптимизации, который наилучшим образом соответствует потребностям вашего проекту.
    3. Имплементировать его.

    Компания ИТ для бизнеса предоставляет услуги по аудиту, планированию, оптимизации и реализации ИТ инфоаструктуры как в облаке так и на земле, обращайтесь office@itfb.com.ua

    Источник: Перевод статьи с DOU.

  • Принципы автоматизации – Идемпотент

    Принципы автоматизации – Идемпотент

    Термин idempotent берет свое начало в математике. В нем говорится, что «если приложение будет иметь тот же результат, если будет запущено один или несколько раз». Например, если вы умножите значение на 1 или 0 несколько раз, ответ будет получен через 1 или N раз, поэтому они считаются идемпотентными. Однако, если вы добавляете результат 1, 1 или N раз, он будет изменять значение каждый раз и, следовательно, не будет считаться идемпотентным.
    Самый распространенный пример «реального мира», о котором вы прочитаете, — это вызов лифта, как описано в этой цитате из википедии.
    Первоначальное нажатие кнопки переводит систему в состояние запроса, пока запрос не будет удовлетворен. Последующие активации кнопки между первоначальной активацией и удовлетворением запроса не имеют никакого эффекта, если только система не предназначена для настройки времени для удовлетворения запроса на основе количества активаций.
    Идемпотент в компьютерных науках
    Как именно это применяется в компьютерных науках? Функция считается идемпотентной, если она не приводит к изменению после одного или нескольких запусков. Возможность запускать функцию, зная, что никаких изменений не произойдет, если только они не потребуются, по сути делает их более безопасными.
    Представьте, что вы запускаете скрипт с десятью действиями, и скрипт не работает по независящей от вас причине, например, недоступность ресурса или сбой соединения на пятом шаге. Если десять функций не являются идемпотентными, сценарий должен будет запускаться с того места, где он потерпел неудачу. Чтобы даже добиться этого, вам нужно будет либо изменить сценарий, либо встроить в сценарий другую логику для создания отправной точки, что может быть или не быть практичным, например, если на третьем этапе будет собрана информация, которая вам понадобится позже, вы бы все равно нужно выполнить этот шаг, несмотря ни на что. Это ложится бременем на пользователя сценария и не дает оптимального опыта работы с пользователем. Однако, если все функции были идемпотентными, вы можете просто перезапустить скрипт.
    Примечание. Хотя, конечно, это не первая или единственная система, которая делает это, учитывая ее популярность, стоит отметить, что Ansible пытается применить эту технику ко всем созданным модулям.
    Пример
    Механизм создания идемпотентной функции состоит в том, чтобы сначала проверить состояние или значение, которое вы намереваетесь получить, и только если нет, то внесите изменения. Как вы можете видеть в этих надуманных примерах, первый пример не идемпотентен, а второй – нет.

    >>> neighbors = ["nyc-rt01"]
    >>>
    >>> def update_list(neighbors, neighbor):
    ...     neighbors.append(neighbor)
    ...     return neighbors
    ...
    >>> neighbors = update_list(neighbors, "nyc-rt02")
    >>> neighbors = update_list(neighbors, "nyc-rt02")
    >>> print(neighbors)
    ['nyc-rt01', 'nyc-rt02', 'nyc-rt02']
    >>>

    Примечание: результат включает nyc-rt02 несколько раз.

    >>> neighbors = ["nyc-rt01"]
    >>>
    >>> def update_list_idempotently(neighbors, neighbor):
    ...     if neighbor not in neighbors:
    ...         neighbors.append(neighbor)
    ...     return neighbors
    ...
    >>> neighbors = update_list_idempotently(neighbors, "nyc-rt02")
    >>> neighbors = update_list_idempotently(neighbors, "nyc-rt02")
    >>> print(neighbors)
    ['nyc-rt01', 'nyc-rt02']

    Примечание. Результат включает в себя nyc-rt02 один раз, даже если попытаться «добавить nyc-rt02» несколько раз.

    Бессилие в REST

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

    Пример использования в реальной жизни

    *_configМодули Ansible идемпотентны. Но что это значит, когда вы отправляете конфигурации через cli? В этом случае состояние, на которое ссылается модуль, заключается в том, существует ли уже конфигурация, а именно:как конфигурация отображается в «показательном прогоне». Повторюсь, это точное совпадение в прямом смысле слова, поэтому, хотя IOS примет «int gig0 / 1», который не будет соответствовать фрагменту конфигурации «interface GigabithEthernet0 / 1», который фактически отображается в конфигурации, то же самое верно. для несоответствия интервала. На самом деле нет никакой связи между тем, что примет cli, и тем, что отправит модуль. Таким образом, вы можете отправлять неверные команды, модуль просто вызовет ошибку, или у вас может быть несоответствие пробелов или синтаксиса, которые будут работать на cli, но не будут рассматриваться модулем как идемпотентная функция, поэтому модуль будет отправлять изменения конфигурации каждый раз.

    Используя специальную команду Ansible, вы можете увидеть результаты трехкратного выполнения этой команды.

    1. В первый раз применяется описание интерфейса config «int Gig1 / 0/24», и результат изменяется.
    2. Во второй раз модуль все еще пытается построить конфигурацию на «Gig1 / 0/24» и снова отправляет конфигурацию, так как не было точного совпадения.
    3. При третьем применении конфигурации к «интерфейсу GigabitEthernet1 / 0/24» обнаруживается совпадение в конфигурации, и никаких изменений не требуется, и, таким образом, модуль идемпотентен.

    Заключение

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

    Кроме того, всегда следует учитывать влияние построения идемпотентной функции. В приведенном надуманном примере гораздо эффективнее с вычислительной точки зрения создать набор из списка. Таким образом, появился способ получить желаемый конечный результат без вычислительных затрат. Это подчеркивает важность понимания вашего варианта использования и его влияния на ваше приложение.

  • Программируемость сети на основе модели с открытым исходным кодом

    Программируемость сети на основе модели с открытым исходным кодом

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

    В ближайшие несколько месяцев вам больше не придется воображать. Network to Code прилагает все усилия, чтобы это стало реальностью.

    Постановка проблемы

    Прежде чем мы посмотрим на решение, давайте установим уровень проблемы.

    Хотя многие современные сетевые операционные системы (NOS) в настоящее время предлагают API, многие из них уникальны для каждого поставщика или, что еще хуже, уникальны для каждой платформы. Конечно, существует еще больше устаревших платформ без поддержки API. Эти устройства предлагают SSH-доступ к интерфейсу командной строки (CLI) устройства. В обоих случаях использования данные, возвращаемые командой или вызовом API, зависят от поставщика.

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

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

    Решение

    На это легко ответить, что все поставщики и устройства должны поддерживать одни и те же модели и API (и мы с этим согласны!). Однако это будет некоторое время. Индустрия движется медленно. Несмотря на то, что в индустрии маршрутизаторов и коммутаторов наблюдается прогресс, следует рассмотреть различные типы устройств, такие как межсетевые экраны и балансировщики нагрузки. Здесь еще много работы (требуется дополнительная поддержка со стороны поставщиков и спрос со стороны покупателей).

    Сеть для программирования на основе модели API с открытым исходным кодом.

    Чтобы смягчить существующую проблему и продвинуть отрасль вперед, Network to Code будет открывать исходный код для четырех различных проектов, создавая независимый от поставщика и расширяемый API RESTCONF. Этот API сможет общаться с любым устройством с любой ОС, включая устаревшие устройства, поддерживающие только SSH.

    Четыре проекта, которые мы выпустим, в том порядке, в котором они будут выпущены:

    Янгифы

    Yangify – это движок, который фокусируется на синтаксическом анализе и переводе собственной конфигурации в данные YANG в кодировке JSON с использованием собственного Python, в отличие от предметно-ориентированного языка (DSL). Он берет встроенную конфигурацию из подобных команд show и создает структурированный и методичный подход для создания данных JSON, которые соответствуют моделям YANG. Он также переводит YANGify’d JSON в собственную конфигурацию (команды CLI или собственный JSON / XML). Для всех, кто уже занимался парсингом текста в своем путешествии, Yangify аналогичен TextFSM, хотя Yangify анализирует и переводит , а не только разбирает .

    Розетта

    Rosetta выполняет две основные функции. Во-первых, проект Rosetta будет хранить все синтаксические анализаторы и переводчики Yangify. Во-вторых, он предоставит интуитивно понятный интерфейс Python для использования всех парсеров и переводчиков. Rosetta – это аналог ntc-шаблонов с аналогией TextFSM.

    Модели NTC YANG

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

    Rosetta RESTCONF API

    Краеугольным камнем всего этого является легкий RESTCONF API, который объединяет все вышеупомянутые функции, но также интегрирует южное подключение к сетевым устройствам, что делает этот API полноценным мультивендорным. API будет взаимодействовать с сетевыми устройствами, анализируя и переводя собственные конфигурации в режиме реального времени. Rosetta RESTCONF API также будет поддерживать возможные конфигурации и возможность подключаться к другим системам с помощью Python API, настраиваемый и запускаемый при любом входящем вызове API. Для обеспечения возможности подключения устройства NAPALM будет использоваться в Rosett RESTCONF API.

    Ниже приводится визуальное представление того, как выглядит полный стек для Rosetta RESTCONF API, объединяя все, что будет с открытым исходным кодом, и NAPALM.

    API Rosetta RESTCONF

    Удачной автоматизации!

  • Как ускорить сайт

    Как ускорить сайт

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

    Пример. Имеем интернет магазин с более 100000 товаров, железо изначально запланировано под высокую нагрузку, но сайт в один момент стал тормозить или не отвечать совсем.

    Ищем проблему

    Изучаем настройки сервера, этот этап разделим на два под этапа: аудит бэкенд и фронтэнд частей. Выявили скорость загрузки страниц снижена на бэкенде, около 80 секунд на популярных страницах. Как результат можно получить снижение продаж.

    Определяем причину замедления: не оптимальные настройки БД и кэширование. В итоге провели несколько шагов и решили проблему.

    Решение

    1.Настройка БД

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

    2. Изменения типа данных на InnoDB

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

    У InnoDB значительно быстрее в за счет блокировки только строки, а не как в MyISAM всей таблицы. Когда запрос выполняется, все остальные транзакции ждут освобождения строки, а в случае с MyISAM таблицы. Логично, строка меньше таблицы, соответственно работы идет быстрее. Для InnoDB также можно оптимизировать параметры под конкретную БД.

    # InnoDB parameters
    innodb_file_per_table
    innodb_flush_log_at_trx_commit
    innodb_flush_method
    innodb_buffer_pool_size
    innodb_log_file_size
    innodb_buffer_pool_instances
    innodb_file_format
    innodb_locks_unsafe_for_binlog
    innodb_autoinc_lock_mode
    transaction-isolation
    innodb-data-file-path
    innodb_log_buffer_size
    innodb_io_capacity
    innodb_io_capacity_max
    innodb_checksum_algorithm
    innodb_read_io_threads
    innodb_write_io_threads

     

    скорость загрузки сайта

    Выполнив первые два шага, уменьшилось число одновременных запросов к БД, так как время их обработки стало меньше. Это привео к уменьшению использования оперативной памяти и БД перестала использовать swap.

    3. Настройка Nginx и кеширования

    Nginx один из самых популярных веб серверов, за счет своей простоты, надежности и скорости работы. Nginx используют Яндекс, Mail.ru, Вконтакте и другие компании. Nginx умеет работать с буферизацией (proxy_buffering) и кешированием proxy_cache). Мы также будем использовать эти возможности. В настройках сервера было установлено значение client_max_body_size как для стримингового сервиса, а не интернет магазина, изменение этого значения привело к уменьшению использования памяти.

    оптимизация производительности сайта

    4. Переход на PPH-FPM с Apache

    Nginx обрабатывает скрипты, а статику обрабатывает PHP-FPM. В такой связке работа быстрее чем Nginx+Apache. Apache медленее, ему необходимо каждый раз, считывать конф. файлы, используя для этого системные ресурсы.

    Nginx – быстрый веб сервер, но он нуждается в бэкенде, для этого мы установили PHP-FPM. Установив и настроев PHP-FPM на работу через сокеты unix socket, сайт стал работать еще быстрее.

    Результат

    Главная страница стала грузиться за 3 секунду, вместо 24, а внутренние до 8 сек.

    ускорение сайта

    Уменьшение потребления ресурсов

    Стабильная работа без зависаний

    Быстро работающий сайт, дал увеличение глубины просмотров, в следствии рост продаж.

    Если Вы хотите ускорить работу вашего сайта, обращайтесь наши специалисты помогут вам office@itfb.com.ua.

  • DNS-as-Code на базе dnscontrol

    DNS-as-Code на базе dnscontrol

    Важную роль в деятельности IT-специалистов занимает системное администрирование. Желание упростить все процессы до максимума оставляет лишь один выбор – автоматизация. В этом случае существенно помогают средства “Everything-as-Code”.

    Во время перерывов в написании плейбуков для Ansible, кода для Terraform, и прочих инфраструктурных инструментов для автоматизации, эксперты занимаются поиском новых направлений для автоматизирования, чтобы сократить нажатия кнопок и запуск скриптов по максимуму. 

    Часто в этом случае используется внешняя DNS-зона, которая хостится на nic.ru. Тогда она ее ограничивалась несколькими доменами, на базе которых размещалось более 3000 записей. Стоит отметить, что преимущественное большинство из этого количества было записями для стендов, предназначенных разработчикам. Их единственное отличие – порядковые номера или какой-нибудь суффикс. 

    Количество таких dev<N> достигало 150, в зависимости от временного промежутка. Весь этот процесс осложнял один факт – необходимость периодического изменения, добавления и удаления записей. К примеру, некоторые DEV-стенды должны быть переброшены на другой IP, или же для каждого из стендов необходимо создать персональный CNAME, либо что-то еще в этом роде. 

    Все эти действия выполнялись либо специалистами (поправка, добавление единичных записей), либо обвязками скриптов, для которых устанавливались сложные параметры, багги и большие мануалы. Можно сказать, что система далеко не из самых легких. 

    Один вечер стал решающим, поскольку во время работы специалисты задаются вопросом о том, почему же никто не сделал программирование всех этих записей, сгенерировав их с помощью какого-нибудь легкого алгоритма. Виртуальные данные хранятся на Terraform, работа конфигурации регулируется Ansible, Puppet, в любом случае должно существовать что-то для DNS.

    Затем можно проверить свою теорию, и воспользовался строкой поиска Google, вбив максимально точную фразу “DNS as Code”. Далее последовал поиск подходящих инструментов, которые помогли бы всем планам перейти от статуса фантазий к реальности. Прошло буквально 10 минут, прежде чем удастся наткнуться на несколько инструментов – Dnscontrol и Octodns. Они достаточно интересные, поэтому лучшим решением было незамедлительное изучение особенностей и поведения в работе. 

    Чтобы внести больше ясности, стоит рассмотреть их подробнее. 

    Octodns

    OctoDNS – обобщенное название инструмента, основа которого положена на подход «инфраструктура в виде кода». С его помощью осуществляется развертывание и управление DNS-зонами. Для выполнения действий используется ряд стандартных принципов программного обеспечения. Это касается контроля версий, тестирования и автоматического развертывания. Создателем OctoDNS является GitHub, его написали на Python.

    С этим вопросом разобраться не тяжело, но из него вытекает немало последующих. Как же действовать после изучения подходящего инструмента? С чего начинать? 

    Используя OctoDNS специалисты имеют возможность исключить различные сложности, с которыми связано ручное управление DNS, поскольку для хранения файлов зон используется исключительно структурированный формат (YAML).

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

    Посмотрим на его возможности:

    • наличие разработанных провайдеров, которые взаимодействуют со многими популярными облачными системами и регистраторами;
    • возможность синхронизации DNS-записи, привязав несколько провайдеров одновременно;
    • возможность встраивания в CI/CD.

    Описание DNS-записей в формате YAML выглядит приблизительно так: 

    • ~/octodns/config/config.yaml
    • ~/octodns/config/your-domain.yaml.

    Не особо углубляясь и используя беглое чтение, можно дать предварительную оценку возможностям инструмента. Он был бы лучше с наличием дополнительных возможностей, среди которых:

    • создание циклов и других алгоритмических конструкций для упрощения создания однотипных записей;
    • экспорт зоны в формате Bind.

    Из-за этих минусов обесцениваются возможности использования инструментов на базе моего кейса. Продолжаем искать. 

    Dnscontrol

    DNSControl – достаточно удобный инструмент, в построении которого лежит принцип инфраструктуры в формате кода. С его помощью можно развертывать и управлять зонами DNS. При этом используются стандартные принципы разработки ПО. Это касается контроля версий, тестирования и автоматизированного развертывания. Создателем инструмента DNSControl является Stack Exchange. Его написали на Go.

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

    DNSControl использует javascript в качестве основного языка ввода, чтобы обеспечить мощность и гибкость для настройки ваших доменов. Основная цель javascript – создать объект DNSConfig, который будет передаваться туда и обратно.

    Здесь я был немного удивлен. Но мы продолжаем изучать принцип работы инструмента. 

    Основные файлы конфигурации выглядят следующим образом:

    • dnscontrol.js.

    А вот шаблон файлов, в которых описана ваша зона:

    • my-zone.ru.js.

    Выглядит так себе, если честно. Если сравнивать с предыдущим вариантом, то YAML явно завоюет больше симпатий. Все еще непонятно, почему разработчики решили встроить JavaScrip для написания такого простенького кода. Повторно обратимся к документации и остановимся на интересной записи:

    «Продвинутые темы:

    Уловки кода: безопасно используйте макросы и циклы.»

    Воспользовавшись ссылкой переходим к примеру, который создавался с использованием переменных и циклов. Предварительно знакомимся с предупреждением: 

    «Язык dnsconfig.js – это JavaScript. С другой стороны, это означает, что вы можете использовать циклы, переменные и все, что захотите …

    Конечно, вы можете проделать много интересных трюков с if / thens, макросами и циклами. Да, вы понимаете код. Однако подумайте о своих коллегах, которые будут следующими, кто отредактирует файл. Вы настраиваете их на провал?»

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

    Если исключить страх, можно перейти к более сложным примерам, по типу: 

    • my-zone.ru.js.

    Многие привыкли использовать провайдера Bind. Он выполняет генерирование файлов зон в формате Bind. В дальнейшем они доступны для использования в любом направлении. 

    Для того, чтобы применить конфигурацию, воспользуемся командой dnscontrol push:

    • вывод.

    Изучив каталог zones, можно заметить появление файлов  зоны формата Bind:

    • myzone.ru.

    Далее, когда в исходных файлах появился ряд изменений, выполняется команда dnscontrol preview с перечнем планируемых изменений. Чтобы из применить придется повторно воспользоваться командой dnscontrol push. Это достаточно удобный вариант. 

    Итак, избегая все предосторожности, приступаем к написанию более сложного кода. Эксперименты заняли несколько часов моего времени, но результат того стоил. Получается разветвленная структура проекта, содержащая множество JS-файлов и даже персональные функции, которые были использованы при написании кода. 

    Безусловно, этот инструмент – именно то, что нужно. Займемся построением полного процесса DNS as Code.

    Этапы построения

    После того, как специалистам удалось определиться с выбором подходящего инструмента, приступаем к построению CI. Принципы Infrastructure as Code требуют применения практических навыков, которые используются во время работы над ПО. Среди них:

    • внедрение системы контроля версий;
    • код ревью;
    • CI/CD;
    • проведение тестирования;

    Новичкам будет сложновато разобраться со всеми этими требованиями, по возможности соберем их в пайплайн.

    Этот проект реализовывается с использованием Gitlab. Встроенный Container Registry и CI позволяет осуществить выстраивание всего необходимого пеплайна в одном месте. Для этого используем репрезиторий проекта, упростив себе задачу. 

    Предварительно займемся определением шагов, которые будут содержать наш пайплайн. Оптимальным вариантом будет такой состав:

    • validate – валидация кода;
    • prepare – подготовка всего необходимого, скачивание текущего состояния зон с сайта NIC.RU;
    • plan – построение плана изменений;
    • build – сборка новых файлов зон;
    • test – тестирование зон на DNS-сервере;
    • deploy – отправка проверенных зон в NIC.RU и их применение.

    Далее выбираем оптимальные docker-образы, которые будут использоваться на протяжении каждого шага. Можно остановиться на двух: 

    • stackexchange/dnscontrol – для всех шагов, кроме test;
    • internetsystemsconsortium/bind9 – для шага test.

    Придется пересобирать эти образы, поскольку понадобилась настройка корпоративных сертификатов и дополнительных утилит. 

    Несколько суток ушло на эксперименты с Gitlab CI, и в итоге получился пайплайн такого формата:

    • .gitlab-ci.yml

    Хочется уделить дополнительное внимание шагам plan и test.

    На первом из них plan после выводы команды началось перенаправление к файлу и последующее складывание его в артефакты. Этот артефакт был помечен опцией expose_as. Она свидетельствует  о том, что когда контрибьютор создаст Merge Request, ссылка на данный файл и джобу прикрепится в автоматическом порядке. Эта функция удобна для ревьюверов, которые кроме внесенных коррективов в коде смогут изучать и запланированные изменения в результирующей зоне. Выглядит это вот так:

    После нажатия на кнопку plan, которая находится снизу надписи Job, можно провалиться в вывод, чтобы ознакомиться с подробным планом: 

    На шаге test производится проверка зон на реальном bind-сервере. Далее осуществляется прогон тестов, которые проверяют, что необходимые записи резолвятся и возвращают правильный результат.

    Некоторые шаги требуют использования shell-скриптовс необходимой логикой. Процесс редактируется в зависимости от необходимостей.

    Shell-срипты для CI:

    • nic_auth.sh
    • nic_download.sh
    • nic_upload.sh
    • zones.conf.sh
    • bind_test.sh

    На этом, пожалуй, закончим данную тему. Надеемся, она была полезной для вас. 

     

  • Стратегии синтаксического анализа – парсеры Genie PyATS

    Стратегии синтаксического анализа – парсеры Genie PyATS

    Давайте продолжим и погрузимся глубже в то, как выглядит парсер show lldp neighbors, как он работает и как нам нужно изменить существующий playbook, чтобы использовать парсеры Genie.

    Genie Primer

    Количество парсеров резко увеличилось за последние несколько месяцев и начинает включать больше поставщиков, что приятно видеть.

    Genie использует классы Python для создания двух важных функций синтаксического анализа:

    • Класс схемы: этот класс определяет схему, которой должен придерживаться структурированный вывод.
    • Класс синтаксического анализатора: этот класс определяет фактические методы синтаксического анализа для конкретной команды.

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

    Еще одно ключевое отличие – это возможность использовать другие стратегии синтаксического анализа в Genie, такие, как TextFSM или Template Text Parser (TTP), но в этом посте мы рассмотрим RegEx.

    Давайте углубимся в наш конкретный парсер.

    """show_lldp.py
       supported commands:
         *  show lldp
         *  show lldp entry *
         *  show lldp entry [<WORD>]
         *  show lldp interface [<WORD>]
         *  show lldp neighbors
         *  show lldp neighbors detail
         *  show lldp traffic
    """
    import re
    
    from genie.metaparser import MetaParser
    from genie.metaparser.util.schemaengine import Schema, \
                                             Any, \
                                             Optional, \
                                             Or, \
                                             And, \
                                             Default, \
                                             Use
    
    # import parser utils
    from genie.libs.parser.utils.common import Common

    Мы видим, что этот парсер объявлен в show_lldp.pyмодуле и поддерживает несколько вариантов команды show lldp . Затем он импортирует re, который является встроенной библиотекой RegEx. Следующий импорт – MetaParserэто проверка того, что выходные данные парсеров с0щоответствуют определенной схеме. После MetaParserимпорта происходит связанный со схемой импорт, который помогает построить реальную схему, которую мы вскоре увидим. После этого импортируется класс Common, который предоставляет вспомогательные функции.

    Класс схемы дает нам представление о том, как будет выглядеть результат. Давайте посмотрим на первоначальное определение.

    class ShowLldpNeighborsSchema(MetaParser):
        """
        Schema for show lldp neighbors
        """
        schema = {
            'total_entries': int,
            'interfaces': {
                Any(): {
                    'port_id': {
                        Any(): {
                            'neighbors': {
                                Any(): {
                                    'hold_time': int,
                                    Optional('capabilities'): list,
                                }
                            }
                        }
                    }
                }
            }
        }

    Мы видим, что ShowLldpNeighborsSchemaэто будет подкласс MetaParserкласса, импортированного в начале файла. Внутри ShowLldpNeighborsSchemaкласса мы определяем наш schemaатрибут, используемый, чтобы убедиться, что наш вывод соответствует схеме, прежде чем возвращать его пользователю.

    Схема является словарем и ожидает total_entriesключ с целочисленным значением и interfacesключом, используемым для определения словаря. Каждый интерфейс будет ключом в interfacesсловаре, а данные, полученные из выходных данных, определены в нескольких других вложенных словарях. Каждая пара “ключ-значение” определяет ключ и typeзначение, которым он должен быть. Также существуют Optionalключи, которые не требуются для проверки схемы.

    Теперь, когда мы видим схему, мы можем смоделировать, каким будет наш потенциальный результат.

    {
        "total_entries": 1,
        "interfaces": {
            "GigabitEthernet1": {
                "port_id": {
                    "Gi1": {
                        "neighbors": {
                            "iosv-0": {
                                "capabilities": [
                                    "R"
                                ],
                                "hold_time": 120
                            }
                        }
                    }
                }
            }
        }
    }

    Перейдем к определенному классу парсера.

    class ShowLldpNeighbors(ShowLldpNeighborsSchema):
        """
        Parser for show lldp neighbors
        """
        CAPABILITY_CODES = {'R': 'router',
                            'B': 'mac_bridge',
                            'T': 'telephone',
                            'C': 'docsis_cable_device',
                            'W': 'wlan_access_point',
                            'P': 'repeater',
                            'S': 'station_only',
                            'O': 'other'}
    
        cli_command = ['show lldp neighbors']

    Мы видим, что ShowLldpNeighborsкласс наследует ShowLldpNeighborsSchemaкласс, который мы только что рассмотрели. Теперь есть сопоставление для коротких кодов возможностей формы, возвращаемых в выходных данных, когда соседи существуют, и для длинной формы, которую синтаксический анализатор хочет вернуть пользователю.

    Следующая определенная переменная – это cli_command. Он определяет команды, выполняемые синтаксическим анализатором, если выходные данные не предоставляются.

    Давайте посмотрим на cliметод, чтобы увидеть, что будет выполнено, когда пользователь укажет cliсинтаксический анализатор для show lldp neighborsкоманды.

    Каждый тип вывода будет указан как метод в классе парсера. Например, если устройство возвращает xml, будет xmlметод, который будет анализировать и возвращать структурированные данные, которые придерживаются той же схемы, что и cliвыходные данные.

    Давайте рассмотрим код по частям, чтобы показать, что происходит.

    def cli(self, output=None):
           if output is None:
               cmd = self.cli_command[0]
               out = self.device.execute(cmd)
           else:
               out = output
    
           parsed_output = {}

    Мы видим, что cliметод принимает необязательный аргумент с именем output, но по умолчанию None. Первая логика определяет, предоставил ли пользователь команду outputили нужно ли синтаксическому анализатору выполнить команду в отношении устройства. Используемое парсером соединение обеспечивается библиотекой PyATS. Это означает, что никакой другой библиотеки, такой как netmikoили napalmдля подключения к устройствам, не требуется.

    # Total entries displayed: 4
            p1 = re.compile(r'^Total\s+entries\s+displayed:\s+(?P<entry>\d+)$')
    
            # Device ID           Local Intf     Hold-time  Capability      Port ID
            # router               Gi1/0/52       117        R               Gi0/0/0
            # 10.10.191.107       Gi1/0/14       155        B,T             7038.eeff.572d
            # d89e.f3ff.58fe      Gi1/0/33       3070                       d89e.f3ff.58fe
            p2 = re.compile(r'(?P<device_id>\S+)\s+(?P<interfaces>\S+)'
                            r'\s+(?P<hold_time>\d+)\s+(?P<capabilities>[A-Z,]+)?'
                            r'\s+(?P<port_id>\S+)')

    parsed_outputСледующим шагом после создания переменной является определение выражений RegEx, используемых для поиска ценных данных в выходных данных устройства. Поскольку выходные данные представлены в виде таблицы, что означает, что они определены как таблица, все значения, которые нам нужны, будут в одной строке (строке) для каждого соседа.

    Анализатор использует, re.compileчтобы заранее указать выражение RegEx для последующего использования в коде. Обычно re.compileиспользуется, когда одно и то же выражение используется несколько раз.

    p1предоставит total_entriesв нашей схеме, используя возможность именованных групп захвата в reбиблиотеке. К счастью, Cisco предоставляет отличную документацию в коде, чтобы рассказать вам, что ожидает захватить каждое регулярное выражение. p2определяет регулярное выражение, используемое для сбора информации о соседях. Мы видим, что он использует в основном те, \S+которые захватывают любые непробельные символы, поскольку вывод является прямым. Но мы видим возможности названой группы захвата немного сложнее. Ожидается хотя бы одна заглавная буква или запятая, а затем ноль или одно из этого выражения RegEx. Это можно лучше объяснить на их примере, если мы посмотрим на столбец возможностей, он показывает, что он может фиксировать одну возможность, две возможности с запятой или нулевые возможности.

    Теперь давайте посмотрим на оставшийся код, чтобы увидеть, как он использует эти выражения RegEx.

    for line in out.splitlines():
                line = line.strip()
    
                # Total entries displayed: 4
                m = p1.match(line)
                if m:
                    parsed_output['total_entries'] = int(m.groupdict()['entry'])
                    continue
    
                # Device ID           Local Intf     Hold-time  Capability      Port ID
                # router               Gi1/0/52       117        R               Gi0/0/0
                # 10.10.191.107       Gi1/0/14       155        B,T             7038.eeff.572d
                # d89e.f3ff.58fe      Gi1/0/33       3070                       d89e.f3ff.58fe
                m = p2.match(line)
                if m:
                    group = m.groupdict()
    
                    intf = Common.convert_intf_name(group['interfaces'])
                    device_dict = parsed_output.setdefault('interfaces', {}). \
                                              setdefault(intf, {}). \
                                              setdefault('port_id', {}). \
                                              setdefault(group['port_id'], {}).\
                                              setdefault('neighbors', {}). \
                                              setdefault(group['device_id'], {})
    
                    device_dict['hold_time'] = int(group['hold_time'])
    
                    if group['capabilities']:
                        capabilities = list(map(lambda x: x.strip(), group['capabilities'].split(',')))
                        device_dict['capabilities'] = capabilities
    
    
                continue
    
            return parsed_output

    Мы видим, что синтаксический анализатор выполняет forцикл по выходным splitlinesданным, используя метод для предоставления списка каждой строки в выходных данных. Он удалит любые пробелы с обеих сторон строки.

    Синтаксический анализатор попытается сопоставить p1скомпилированный RegEx и, если он его захватит, добавит total_entriesв parsed_outputсловарь, а затем continueв следующую строку вывода.

    Если синтаксический анализатор ничего не захватил p1, он попытается сопоставить p2. Если совпадение произойдет, groupdict()метод вернет все названные группы захвата и их значения в виде словаря.

    Теперь мы видим, что парсер использует convert_intf_nameметод из Commonкласса, импортированного в верхней части файла.

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

    После того, device_dictкак указан, он добавляет к нему время удержания.

    Следующим шагом является разделение возможностей на список и добавление к device_dictпеременной. Затем синтаксический анализатор перейдет к следующей строке вывода.

    Как только все строки будут проанализированы, он вернет parsed_outputпользователю, если он пройдет проверку схемы.

    Я считаю, что это легче понять, чем что-то вроде TextFSM, поскольку он написан на Python, а Python – популярный язык среди инженеров по автоматизации сети.

    Давайте продолжим и еще раз рассмотрим топологию.

    Топология .. Опять же

    Ниже приведено изображение лабораторной топологии, которую мы используем для проверки соседей LLDP . Это простая топология с тремя маршрутизаторами Cisco IOS, соединенными вместе и с включенным протоколом LLDP.

    Ansible Setup .. Опять же

    Мы уже рассмотрели большую часть настройки Ansible, но мы объясним небольшие изменения, которые мы должны внести, чтобы использовать парсеры Genie в Ansible.

    Вот взгляд на host var, который мы определили как освежитель, поскольку здесь нет никаких изменений.

    ---
    approved_neighbors:
      - local_intf: "Gi0/0"
        neighbor: "iosv-1"
      - local_intf: "Gi0/1"
        neighbor: "iosv-2"

    Теперь посмотрим на изменения в pb.validate.neighbors.yml.

    ---
    - hosts: "ios"
      connection: "ansible.netcommon.network_cli"
      gather_facts: "no"
    
      tasks:
        - name: "PARSE LLDP INFO INTO STRUCTURED DATA"
          ansible.netcommon.cli_parse:
            command: "show lldp neighbors"
            parser:
              name: ansible.netcommon.pyats
            set_fact: "lldp_neighbors"
    
        - name: "MANIPULATE THE DATA TO BE IN THE SAME FORMAT AS TEXTFSM TO PREVENT CHANGING FINAL ASSERTION TASK"
          set_fact:
            lldp_neighbors: "{{ lldp_neighbors | convert_data }}"
    
        - name: "ASSERT THE CORRECT NEIGHBORS ARE SEEN"
          assert:
            that:
              - "lldp_neighbors | selectattr('local_interface', 'equalto', item['local_intf']) | map(attribute='neighbor') | first == item['neighbor']"
          loop: "{{ approved_neighbors }}"

    Используя ansible.netcommon.pyatsанализатор требует genieи pyatsдолжен быть установлен с помощью pip install genie pyats.

    Есть несколько вещей, которые нужно проанализировать с помощью учебника. Сначала мы изменили парсер на ansible.netcommon.pyats. Во-вторых, мы добавили еще одну задачу для управления данными, которые мы получаем от парсера, в формате, аналогичном второму сообщению в блоге в этой серии, поэтому нам не нужно изменять последнюю задачу. Я выполнил преобразование с помощью настраиваемого плагина фильтра из-за структуры данных и простоты обработки в Python. Вы увидите результат ниже, как только мы запустим нашу книгу.

    ❯ ansible-playbook pb.validate.neighbors.yml -k -vv
    ansible-playbook 2.10.3
      config file = /Users/myohman/Documents/local-dev/blog-posts/ansible.cfg
      configured module search path = ['/Users/myohman/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
      ansible python module location = /Users/myohman/.virtualenvs/3.8/main/lib/python3.8/site-packages/ansible
      executable location = /Users/myohman/.virtualenvs/3.8/main/bin/ansible-playbook
      python version = 3.8.6 (default, Nov 17 2020, 18:43:06) [Clang 12.0.0 (clang-1200.0.32.27)]
    Using /Users/myohman/Documents/local-dev/blog-posts/ansible.cfg as config file
    SSH password:
    redirecting (type: callback) ansible.builtin.yaml to community.general.yaml
    redirecting (type: callback) ansible.builtin.yaml to community.general.yaml
    Skipping callback 'default', as we already have a stdout callback.
    Skipping callback 'minimal', as we already have a stdout callback.
    Skipping callback 'oneline', as we already have a stdout callback.
    
    PLAYBOOK: pb.validate.neighbors.yml ***********************************************************************
    1 plays in pb.validate.neighbors.yml
    
    PLAY [ios] ************************************************************************************************
    META: ran handlers
    
    TASK [Parse LLDP info into structured data] ***************************************************************
    task path: /Users/myohman/Documents/local-dev/blog-posts/pb.validate.neighbors.yml:9
    ok: [iosv-2] => changed=false
      ansible_facts:
        lldp_neighbors:
          interfaces:
            GigabitEthernet0/0:
              port_id:
                Gi0/1:
                  neighbors:
                    iosv-0:
                      capabilities:
                      - R
                      hold_time: 120
            GigabitEthernet0/1:
              port_id:
                Gi0/1:
                  neighbors:
                    iosv-1:
                      capabilities:
                      - R
                      hold_time: 120
          total_entries: 2
      parsed:
        interfaces:
          GigabitEthernet0/0:
            port_id:
              Gi0/1:
                neighbors:
                  iosv-0:
                    capabilities:
                    - R
                    hold_time: 120
          GigabitEthernet0/1:
            port_id:
              Gi0/1:
                neighbors:
                  iosv-1:
                    capabilities:
                    - R
                    hold_time: 120
        total_entries: 2
      stdout: |-
        Capability codes:
            (R) Router, (B) Bridge, (T) Telephone, (C) DOCSIS Cable Device
            (W) WLAN Access Point, (P) Repeater, (S) Station, (O) Other
    
        Device ID           Local Intf     Hold-time  Capability      Port ID
        iosv-1              Gi0/1          120        R               Gi0/1
        iosv-0              Gi0/0          120        R               Gi0/1
    
        Total entries displayed: 2
      stdout_lines: <omitted>
    ok: [iosv-0] => changed=false
      ansible_facts:
        lldp_neighbors:
          interfaces:
            GigabitEthernet0/0:
              port_id:
                Gi0/0:
                  neighbors:
                    iosv-1:
                      capabilities:
                      - R
                      hold_time: 120
            GigabitEthernet0/1:
              port_id:
                Gi0/0:
                  neighbors:
                    iosv-2:
                      capabilities:
                      - R
                      hold_time: 120
          total_entries: 2
      parsed:
        interfaces:
          GigabitEthernet0/0:
            port_id:
              Gi0/0:
                neighbors:
                  iosv-1:
                    capabilities:
                    - R
                    hold_time: 120
          GigabitEthernet0/1:
            port_id:
              Gi0/0:
                neighbors:
                  iosv-2:
                    capabilities:
                    - R
                    hold_time: 120
        total_entries: 2
      stdout: |-
        Capability codes:
            (R) Router, (B) Bridge, (T) Telephone, (C) DOCSIS Cable Device
            (W) WLAN Access Point, (P) Repeater, (S) Station, (O) Other
    
        Device ID           Local Intf     Hold-time  Capability      Port ID
        iosv-2              Gi0/1          120        R               Gi0/0
        iosv-1              Gi0/0          120        R               Gi0/0
    
        Total entries displayed: 2
      stdout_lines: <omitted>
    ok: [iosv-1] => changed=false
      ansible_facts:
        lldp_neighbors:
          interfaces:
            GigabitEthernet0/0:
              port_id:
                Gi0/0:
                  neighbors:
                    iosv-0:
                      capabilities:
                      - R
                      hold_time: 120
            GigabitEthernet0/1:
              port_id:
                Gi0/1:
                  neighbors:
                    iosv-2:
                      capabilities:
                      - R
                      hold_time: 120
          total_entries: 2
      parsed:
        interfaces:
          GigabitEthernet0/0:
            port_id:
              Gi0/0:
                neighbors:
                  iosv-0:
                    capabilities:
                    - R
                    hold_time: 120
          GigabitEthernet0/1:
            port_id:
              Gi0/1:
                neighbors:
                  iosv-2:
                    capabilities:
                    - R
                    hold_time: 120
        total_entries: 2
      stdout: |-
        Capability codes:
            (R) Router, (B) Bridge, (T) Telephone, (C) DOCSIS Cable Device
            (W) WLAN Access Point, (P) Repeater, (S) Station, (O) Other
    
        Device ID           Local Intf     Hold-time  Capability      Port ID
        iosv-2              Gi0/1          120        R               Gi0/1
        iosv-0              Gi0/0          120        R               Gi0/0
    
        Total entries displayed: 2
      stdout_lines: <omitted>
    
    TASK [MANIPULATE THE DATA TO BE IN STANDARD FORMAT] *******************************************************
    task path: /Users/myohman/Documents/local-dev/blog-posts/pb.validate.neighbors.yml:16
    ok: [iosv-0] => changed=false
      ansible_facts:
        lldp_neighbors:
        - local_interface: Gi0/1
          neighbor: iosv-2
        - local_interface: Gi0/0
          neighbor: iosv-1
    ok: [iosv-1] => changed=false
      ansible_facts:
        lldp_neighbors:
        - local_interface: Gi0/1
          neighbor: iosv-2
        - local_interface: Gi0/0
          neighbor: iosv-0
    ok: [iosv-2] => changed=false
      ansible_facts:
        lldp_neighbors:
        - local_interface: Gi0/1
          neighbor: iosv-1
        - local_interface: Gi0/0
          neighbor: iosv-0
    
    TASK [Assert the correct neighbors are seen] **************************************************************
    task path: /Users/myohman/Documents/local-dev/blog-posts/pb.validate.neighbors.yml:20
    ok: [iosv-0] => (item={'local_intf': 'Gi0/0', 'neighbor': 'iosv-1'}) => changed=false
      ansible_loop_var: item
      item:
        local_intf: Gi0/0
        neighbor: iosv-1
      msg: All assertions passed
    ok: [iosv-2] => (item={'local_intf': 'Gi0/0', 'neighbor': 'iosv-0'}) => changed=false
      ansible_loop_var: item
      item:
        local_intf: Gi0/0
        neighbor: iosv-0
      msg: All assertions passed
    ok: [iosv-1] => (item={'local_intf': 'Gi0/0', 'neighbor': 'iosv-0'}) => changed=false
      ansible_loop_var: item
      item:
        local_intf: Gi0/0
        neighbor: iosv-0
      msg: All assertions passed
    ok: [iosv-0] => (item={'local_intf': 'Gi0/1', 'neighbor': 'iosv-2'}) => changed=false
      ansible_loop_var: item
      item:
        local_intf: Gi0/1
        neighbor: iosv-2
      msg: All assertions passed
    ok: [iosv-1] => (item={'local_intf': 'Gi0/1', 'neighbor': 'iosv-2'}) => changed=false
      ansible_loop_var: item
      item:
        local_intf: Gi0/1
        neighbor: iosv-2
      msg: All assertions passed
    ok: [iosv-2] => (item={'local_intf': 'Gi0/1', 'neighbor': 'iosv-1'}) => changed=false
      ansible_loop_var: item
      item:
        local_intf: Gi0/1
        neighbor: iosv-1
      msg: All assertions passed
    META: ran handlers
    META: ran handlers
    
    PLAY RECAP ************************************************************************************************
    iosv-0                     : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    iosv-1                     : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    iosv-2                     : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

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

    Если мы внимательно посмотрим на вывод первой задачи, мы увидим под parsedключом, а также установив fact ( lldp_neighbors), что у нас есть структурированные данные от запуска сырого вывода через Genie.

    Вторая задача показывает цикл для каждого хоста и itemего использование во время цикла. Если вы посмотрите на нашу книгу, мы используем local_intfи neighborдля наших утверждений из нашей approved_neighborsпеременной.

    Резюме

    Мы преобразовали данные с помощью настраиваемого плагина фильтра, но мы могли бы легко скорректировать факты и окончательные утверждения, чтобы они соответствовали выходным данным, полученным от Genie. Также важно показать возможность использования единой книги воспроизведения с использованием любого синтаксического анализатора для выполнения рабочих утверждений. Если бы мы были в производстве, мы могли бы заставить convert_dataнастраиваемый плагин фильтра переводить несколько различных форматов парсера в формат, независимый от парсера.

    Для краткости, вот наш treeрезультат, чтобы показать вам, как выглядит структура папок при использовании настраиваемого плагина фильтра.

    ❯ tree
    .
    ├── ansible.cfg
    ├── filter_plugins
    │   └── custom_filters.py
    ├── group_vars
    │   ├── all
    │   │   └── all.yml
    │   └── ios.yml
    ├── host_vars
    │   ├── iosv-0.yml
    │   ├── iosv-1.yml
    │   └── iosv-2.yml
    ├── inventory
    └── pb.validate.neighbors.yml

    Наконец, содержимое файла custom_filters.py.

    # -*- coding: utf-8 -*-
    
    from __future__ import absolute_import, division, print_function
    __metaclass__ = type
    
    
    import re
    
    def convert_genie_data(data):
    
        intfs = []
        for k,v in data['interfaces'].items():
            intf_name = "Gi" + re.search(r'\d/\d', k).group(0)
            intf_dict = {}
            intf_dict['local_interface'] = intf_name
            neighbor_intf = list(v['port_id'].keys())[0]
            intf_dict['neighbor'] = list(v['port_id'][neighbor_intf]['neighbors'].keys())[0]
            intfs.append(intf_dict)
    
        return intfs
    
    
    class FilterModule:
        def filters(self):
            filters = {
                'convert_data': convert_genie_data,
            }
            return filters

    Надеюсь, вам понравился этот пост в блоге, и вы немного больше понимаете о парсерах Genie и о том, как использовать их с помощью Ansible. 

     

  • Стратегии синтаксического анализа – шаблоны NTC с использованием TextFSM

    Стратегии синтаксического анализа – шаблоны NTC с использованием TextFSM

    В этом посте мы подробнее рассмотрим использование шаблонов NTC для синтаксического анализа неструктурированных данных в пригодные для использования структурированные данные. Шаблоны NTC используют TextFSM под капотом, чтобы иметь возможность анализировать данные, полученные от традиционных сетевых устройств, с помощью регулярных выражений (RegEx). Мы рассмотрим, как выглядит шаблон TextFSM, как он работает и как мы можем использовать шаблон в Ansible для выполнения утверждений топологии относительно топологии нашей лаборатории.

    TextFSM Primer

    TextFSM был создан Google для преобразования полуструктурированных данных с сетевых устройств в структурированные данные, к которым можно легко получить программный доступ. TextFSM – это язык, специфичный для домена (DSL), использующий под капотом RegEx для анализа данных. Это означает, что требуются некоторые знания RegEx

    Давайте взглянем на шаблон TextFSM, а затем разберем его, чтобы лучше понять, как работает TextFSM. Ниже представлен шаблон из NTC Templates, который мы будем использовать для этого поста.

    Value Required NEIGHBOR (\S{0,20})
    Value Required LOCAL_INTERFACE (\S+)
    Value CAPABILITIES (\S*)
    Value Required NEIGHBOR_INTERFACE (\S+)
    
    Start
      ^Device.*ID -> LLDP
      # Capture time-stamp if vty line has command time-stamping turned on
      ^Load\s+for\s+
      ^Time\s+source\s+is
    
    LLDP
      ^${NEIGHBOR}\s*${LOCAL_INTERFACE}\s+\d+\s+${CAPABILITIES}\s+${NEIGHBOR_INTERFACE} -> Record
      ^${NEIGHBOR}
      ^\s+${LOCAL_INTERFACE}\s+\d+\s+${CAPABILITIES}\s+${NEIGHBOR_INTERFACE} -> Record

    Мы рассмотрим этот шаблон более подробно ниже, но я хочу показать вам, как выглядят необработанные данные и как они выглядят после того, как они были проанализированы с помощью TextFSM.

    Capability codes:
        (R) Router, (B) Bridge, (T) Telephone, (C) DOCSIS Cable Device
        (W) WLAN Access Point, (P) Repeater, (S) Station, (O) Other
    
    Device ID           Local Intf     Hold-time  Capability      Port ID
    S2                  Fa0/13         120        B               Gi0/13
    Cisco-switch-1      Gi1/0/7        120                        Gi0/1
    Juniper-switch1     Gi2/0/1        120        B,R             666
    Juniper-switch1     Gi1/0/1        120        B,R             531
    
    Total entries displayed: 4

    Вот результат, возвращенный после анализа полуструктурированных данных с использованием шаблонов NTC.

    ---
    parsed_sample:
      - capabilities: "B"
        local_interface: "Fa0/13"
        neighbor: "S2"
        neighbor_interface: "Gi0/13"
      - capabilities: ""
        local_interface: "Gi1/0/7"
        neighbor: "Cisco-switch-1"
        neighbor_interface: "Gi0/1"
      - capabilities: "B,R"
        local_interface: "Gi2/0/1"
        neighbor: "Juniper-switch1"
        neighbor_interface: "666"
      - capabilities: "B,R"
        local_interface: "Gi1/0/1"
        neighbor: "Juniper-switch1"
        neighbor_interface: "531"

    Значения

    Как видно из полученных нами выходных данных, Valueв шаблоне используется как ключ (столбец) в каждом возвращаемом словаре (строке).

    Есть несколько ключевых слов, которые могут изменить способ обработки значения, например следующие:

    • Обязательно: запись (строка) сохраняется в таблице только в том случае, если это значение совпадает.
    • Заполнение: ранее сопоставленное значение сохраняется для последующих записей (если оно явно не очищено или не сопоставлено снова). Другими словами, последнее совпавшее значение копируется в новые строки, если оно не будет найдено снова.
    • Список: значение представляет собой список, добавляемый к каждому совпадению. Обычно совпадение перезаписывает любое предыдущее значение в этой строке.
    • Ключ: объявляет, что содержимое поля способствует уникальному идентификатору строки. Это можно использовать для связывания данных из нескольких шаблонов в единую структуру.
    • Заполнение: аналогично заполнению, но заполняется вверх, пока не будет найдена непустая запись. Несовместимо с Required.

    В конце строки мы укажем RegEx, которое будет соответствовать нашему полуструктурированному тексту для этого конкретного значения. В зависимости от данных, это может быть как общий \S+, если данные хорошо известны, так и сложный, если необходимо.

    \S+ соответствует любому непробельному символу, который требует, чтобы данные были либо контролируемыми, либо хорошо известными, как указано выше.

    Напомним, что мы только что обсуждали, здесь пробой Значение строки:.Value {KEYWORD} {VALUE_NAME} (RegEx)

    Состояния

    В государственных определениях заимствованы после Значения определений и отделяются от значений пустой строки. Строки с отступом после каждого состояния – это правила состояния, которые указаны в соответствии с определениями значений, указанными в начале шаблона. Состояния помогают разбить ваш шаблон на более легкие для чтения фрагменты, если полуструктурированные данные сложны. Верхнего предела количества состояний в шаблоне TextFSM нет, но всегда требуется Start.

    Государственные правила

    Правила определяют строки, которые мы хотим захватить, с определениями значений в начале шаблона. Каждая линия правила должна начинаться с символа карата ( ^). Правила необязательно должны заканчиваться ->действием правила, но оно может потребоваться в зависимости от данных. ->Обозначает действие правила и говорит TextFSM, что делать с данными, полученными до этого момента. Вскоре мы обсудим действия правила. Значения обозначаются ${VALUE_NAME}в правилах состояния, которые будут расширены с помощью RegEx из определения значения.

    Имейте в виду, что вся строка необязательно должна быть RegEx или values ​​( ${NEIGHBOR}), но также может быть обычным текстом для сопоставления. Незаметно TextFSM преобразует каждое правило в полную строку RegEx. Если мы посмотрим на первую строку под LLDPсостоянием, за кадром она будет выглядеть следующим образом:^(\S{0,20})\s*(\S+)\s+\d+\s+(\S*)\s+(\S+)

    Правило состояния необязательно должно соответствовать всей строке, которую мы видим в нашем шаблоне с расширением `. ^ Загрузить \ s + для \ s + , which will match any line that starts with Загрузить для `.

    Правило и линейные действия

    Действия правила могут применяться для каждой строки правила состояния, но они влияют на поведение, и размещение в пределах состояний должно быть тщательно продумано. Есть строковые действия, которые сообщают TextFSM, что нужно делать с текущей строкой во время ее обработки, а затем действия правил сообщают TextFSM, что делать с захваченными значениями. Согласно действию по умолчанию, любая строка, не содержащая символа ->, равна Next.NoRecord. Чтобы лучше понять это, давайте рассмотрим, какие варианты у нас есть, когда дело доходит до использования линии и действий правил, указав ->.

    Действия линии

    • Далее (по умолчанию) : закончить строку ввода, прочитать следующую строку и снова начать сопоставление с начала состояния. Это поведение по умолчанию, если действие строки не указано.
    • Продолжить: сохранить текущую строку и не возобновлять сопоставление с первого правила состояния. Продолжить обработку правил, как если бы совпадение не произошло (присвоение значений все еще происходит).

    Использование действия «Продолжить строку» не является распространенным вариантом использования при построении шаблона,  показывает вариант использования, когда вы хотите захватить несколько значений, находящихся в одной строке.

    Вот пример шаблона:

    Value List INTERFACES ([\w\./]+)
    
    ..omitted for brevity
    
    VLANS
      ^\d+ -> Continue.Record
      ^${VLAN_ID}\s+${NAME}\s+${STATUS}\s*$$
      ^${VLAN_ID}\s+${NAME}\s+${STATUS}\s+${INTERFACES},* -> Continue
      ^\d+\s+(?:\S+\s+){3}${INTERFACES},* -> Continue
      ^\d+\s+(?:\S+\s+){4}${INTERFACES},* -> Continue
      ^\d+\s+(?:\S+\s+){5}${INTERFACES},* -> Continue
      ^\d+\s+(?:\S+\s+){6}${INTERFACES},* -> Continue
      ^\d+\s+(?:\S+\s+){7}${INTERFACES},* -> Continue

    Вот пример частично структурированных данных, которые будут проанализированы:

    50   VLan50                           active    Fa0/1, Fa0/2, Fa0/3, Fa0/4, Fa0/5, Fa0/6, Fa0/7, Fa0/8, Fa0/9
                                                    Fa0/10, Fa0/11, Fa0/12

    Используя Continue для каждой строки, мы можем сохранить захваченное значение, а также строку, которую оно обрабатывает в данный момент, а затем перейти к следующему правилу состояния в State, чтобы захватить дополнительные значения в строке.

    Это означает, что наши структурированные данные будут выглядеть следующим образом:

    ---
    parsed_sample:
      - vlan_id: "50"
        name: "VLan50"
        status: "active"
        interfaces:
          - "Fa0/1"
          - "Fa0/2"
          - "Fa0/3"
          - "Fa0/4"
          - "Fa0/5"
          - "Fa0/6"
          - "Fa0/7"
          - "Fa0/8"
          - "Fa0/9"
          - "Fa0/10"
          - "Fa0/11"
          - "Fa0/12"

    Правило Действия

    • NoRecord (по умолчанию) : ничего не делать. Это поведение по умолчанию, если действие записи не указано.
    • Запись: Запишите собранные значения до строки в возвращаемых данных. Значения без заполнения очищаются. Примечание. Никакая запись не выводится, если есть неназначенные «Обязательные» значения.
    • Очистить: очистить значения без заполнения.
    • Clearall : очистить все значения.
    • Состояние: переход в другое состояние.
    • Ошибка: это встроенное состояние, которое отбрасывает все захваченные значения и возвращает исключение.

    Мы используем действие правила ошибки, чтобы помочь устранить неполадки в наших шаблонах и убедиться, что в наших шаблонах учитываются правильные данные. Вот как мы его используем: `^. -> Ошибка`, которая выдаст исключение для строки, не соответствующей какому-либо определенному правилу состояния.

    `-> Continue.State` не может предотвращать зацикливание в TextFSM.

    Если мы посмотрим на шаблон под LLDPсостоянием, мы увидим в нем две -> Recordопции. Это позволяет нам захватывать соответствующие значения, но путем синтаксического анализа немного отличающегося вывода.

    Мы также можем комбинировать линейное действие с действием правила. Синтаксис для этого LineAction.RuleAction.

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

    Топология

    Ниже приведено изображение топологии, которую мы будем использовать для проверки соседей LLDP в нашей лабораторной топологии. Это простая топология с тремя маршрутизаторами Cisco IOS, которые соединены вместе и имеют включенный LLDP.

    Установка Ansible

    К счастью, наша топология и последующая инвентаризация будут простыми. У нас есть маршрутизаторы в группе под названием, iosкоторая затем имеет соответствующий ios.ymlфайл в group_varsпапке, в которой есть переменные, относящиеся к подключению к этим устройствам, которые я вскоре покажу. Затем у нас есть {hostname}.ymlфайлы для каждого маршрутизатора, содержащие approved_neighborsпеременную, которую мы будем использовать для проверки соседей, которые мы видим из наших проанализированных LLDPданных. Ниже представлено дерево нашего каталога, которое содержит сборник пьес и инвентарь Ansible.

    ❯ tree
    .
    ├── ansible.cfg
    ├── group_vars
    │   └── ios.yml
    ├── host_vars
    │   ├── iosv-0.yml
    │   ├── iosv-1.yml
    │   └── iosv-2.yml
    ├── inventory
    └── pb.validate.neighbors.yml
    
    2 directories, 7 files

    Вот inventoryфайл, который связывает наши маршрутизаторы с iosгруппой.

    [ios]
    iosv-0 ansible_host=10.188.1.56
    iosv-1 ansible_host=10.188.1.54
    iosv-2 ansible_host=10.188.1.55

    Вот содержимое ios.ymlфайла. Это указывает пользователя, которого мы хотим подключиться к маршрутизаторам, а также ansible_network_osчтобы сообщить Ansible, к какому типу сетевого устройства мы будем подключаться.

    Если вы заметили, это отличается от синтаксиса Ansible <= 2.9 и использует путь к, cisco.ios.iosа не просто ios. Это связано с тем, что мы запускаем Ansible 2.10 и переходим на использование синтаксиса, который Ansible будет применять в будущем. Вы также заметите некоторые различия с использованием Ansible 2.10 в нашей инструкции.

    ---
    ansible_user: "cisco"
    ansible_network_os: "cisco.ios.ios"

    Вот взгляд на host var, который мы определили.

    ---
    approved_neighbors:
      - local_intf: "Gi0/0"
        neighbor: "iosv-1"
      - local_intf: "Gi0/1"

    Теперь посмотрим на pb.validate.neighbors.yml.

    ---
    - hosts: "ios"
      connection: "ansible.netcommon.network_cli"
      gather_facts: "no"
    
      tasks:
        - name: "PARSE LLDP INFO INTO STRUCTURED DATA"
          ansible.netcommon.cli_parse:
            command: "show lldp neighbors"
            parser:
              name: ansible.netcommon.ntc_templates
            set_fact: "lldp_neighbors"
    
        - name: "ASSERT THE CORRECT NEIGHBORS ARE SEEN"
          assert:
            that:
              - "lldp_neighbors | selectattr('local_interface', 'equalto', item['local_intf']) | map(attribute='neighbor') | first == item['neighbor']"
          loop: "{{ approved_neighbors }}"

    Плейбук начинается с определения наших хостов как iosгруппы в нашем inventoryфайле, которая состоит из наших трех маршрутизаторов IOS. В методе подключения используется синтаксис> = Ansible 2.10, network_cliи мы отключили сбор фактов.

    Давайте разберем две задачи, которые мы используем, чтобы подтвердить, что наши рабочие данные для соседей LLDP соответствуют определенной approved_neighborsпеременной, которая у нас есть для каждого хоста.

    Первая задача использует ansible.netcommon.cli_parseмодуль для запуска команды на устройстве, а затем анализирует данные с помощью нашего определенного ansible.netcommon.ntc_templatesсинтаксического анализатора. Этот вывод сохраняется в lldp_neighborsсоответствии с set_factдирективой задачи.

    Если вы хотите запустить тот же сценарий, убедитесь, что вы ntc-templatesустановили через pip install ntc-templates.

    Следующий цикл задачи будут над нашим approved_neighborsпеременным , а затем попытаться найти соответствие в наших проанализированных данных путем поиска записи , которая имеет значение для ключа , local_interfaceкоторый соответствует тому , что мы установили для local_intfв approved_neighborsи что neighborключ также соответствует нашей neighborценности. Наша playbook потерпит неудачу, если какой-либо из соседей не соответствует тому, что мы определили approved_neighbors.

    То, как развивается Ansible, и методологии, которые мы использовали в playbook, не ограничивают потенциал playbook одними только iosи мы фактически можем поменять iosопределение хостов для allлюбого количества групп и хостов, которые являются мультивендерами. Это связано с закулисной магией, которая ansible.netcommon.cli_parseвыполняется с ansible_network_osпеременной, которую мы установили в group vars. Он использует эту переменную, чтобы определить, какой nos_commandмодуль запустить для подключения к устройству и какой шаблон использовать для анализа возвращенных данных.

    Выходные данные Playbook

    Давайте продолжим, запустим playbook и посмотрим, какой результат мы получим.

    ❯ ansible-playbook -i inventory pb.validate.neighbors.yml -vv -k
    ansible-playbook 2.10.2
      config file = /Users/myohman/local-dev/blog-posts/ansible.cfg
      configured module search path = ['/Users/myohman/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
      ansible python module location = /Users/myohman/.virtualenvs/main-3.8/lib/python3.8/site-packages/ansible
      executable location = /Users/myohman/.virtualenvs/main-3.8/bin/ansible-playbook
      python version = 3.8.6 (default, Oct 16 2020, 21:27:09) [Clang 12.0.0 (clang-1200.0.32.2)]
    Using /Users/myohman/local-dev/blog-posts/ansible.cfg as config file
    SSH password:
    redirecting (type: callback) ansible.builtin.yaml to community.general.yaml
    redirecting (type: callback) ansible.builtin.yaml to community.general.yaml
    
    PLAYBOOK: pb.validate.neighbors.yml ***********************************************************************************
    1 plays in pb.validate.neighbors.yml
    
    PLAY [ios] ************************************************************************************************************
    META: ran handlers
    
    TASK [Parse LLDP info into structured data] ***************************************************************************
    task path: /Users/myohman/local-dev/blog-posts/pb.validate.neighbors.yml:10
    ok: [iosv-0] => changed=false
      ansible_facts:
        lldp_neighbors:
        - capabilities: R
          local_interface: Gi0/1
          neighbor: iosv-2
          neighbor_interface: Gi0/0
        - capabilities: R
          local_interface: Gi0/0
          neighbor: iosv-1
          neighbor_interface: Gi0/0
      parsed:
      - capabilities: R
        local_interface: Gi0/1
        neighbor: iosv-2
        neighbor_interface: Gi0/0
      - capabilities: R
        local_interface: Gi0/0
        neighbor: iosv-1
        neighbor_interface: Gi0/0
      stdout: |-
        Capability codes:
            (R) Router, (B) Bridge, (T) Telephone, (C) DOCSIS Cable Device
            (W) WLAN Access Point, (P) Repeater, (S) Station, (O) Other
    
        Device ID           Local Intf     Hold-time  Capability      Port ID
        iosv-2              Gi0/1          120        R               Gi0/0
        iosv-1              Gi0/0          120        R               Gi0/0
    
        Total entries displayed: 2
      stdout_lines: <omitted>
    ok: [iosv-1] => changed=false
      ansible_facts:
        lldp_neighbors:
        - capabilities: R
          local_interface: Gi0/1
          neighbor: iosv-2
          neighbor_interface: Gi0/1
        - capabilities: R
          local_interface: Gi0/0
          neighbor: iosv-0
          neighbor_interface: Gi0/0
      parsed:
      - capabilities: R
        local_interface: Gi0/1
        neighbor: iosv-2
        neighbor_interface: Gi0/1
      - capabilities: R
        local_interface: Gi0/0
        neighbor: iosv-0
        neighbor_interface: Gi0/0
      stdout: |-
        Capability codes:
            (R) Router, (B) Bridge, (T) Telephone, (C) DOCSIS Cable Device
            (W) WLAN Access Point, (P) Repeater, (S) Station, (O) Other
    
        Device ID           Local Intf     Hold-time  Capability      Port ID
        iosv-2              Gi0/1          120        R               Gi0/1
        iosv-0              Gi0/0          120        R               Gi0/0
    
        Total entries displayed: 2
      stdout_lines: <omitted>
    ok: [iosv-2] => changed=false
      ansible_facts:
        lldp_neighbors:
        - capabilities: R
          local_interface: Gi0/1
          neighbor: iosv-1
          neighbor_interface: Gi0/1
        - capabilities: R
          local_interface: Gi0/0
          neighbor: iosv-0
          neighbor_interface: Gi0/1
      parsed:
      - capabilities: R
        local_interface: Gi0/1
        neighbor: iosv-1
        neighbor_interface: Gi0/1
      - capabilities: R
        local_interface: Gi0/0
        neighbor: iosv-0
        neighbor_interface: Gi0/1
      stdout: |-
        Capability codes:
            (R) Router, (B) Bridge, (T) Telephone, (C) DOCSIS Cable Device
            (W) WLAN Access Point, (P) Repeater, (S) Station, (O) Other
    
        Device ID           Local Intf     Hold-time  Capability      Port ID
        iosv-1              Gi0/1          120        R               Gi0/1
        iosv-0              Gi0/0          120        R               Gi0/1
    
        Total entries displayed: 2
      stdout_lines: <omitted>
    
    TASK [Assert the correct neighbors are seen] **************************************************************************
    task path: /Users/myohman/local-dev/blog-posts/pb.validate.neighbors.yml:17
    ok: [iosv-0] => (item={'local_intf': 'Gi0/0', 'neighbor': 'iosv-1'}) => changed=false
      ansible_loop_var: item
      item:
        local_intf: Gi0/0
        neighbor: iosv-1
      msg: All assertions passed
    ok: [iosv-1] => (item={'local_intf': 'Gi0/0', 'neighbor': 'iosv-0'}) => changed=false
      ansible_loop_var: item
      item:
        local_intf: Gi0/0
        neighbor: iosv-0
      msg: All assertions passed
    ok: [iosv-0] => (item={'local_intf': 'Gi0/1', 'neighbor': 'iosv-2'}) => changed=false
      ansible_loop_var: item
      item:
        local_intf: Gi0/1
        neighbor: iosv-2
      msg: All assertions passed
    ok: [iosv-1] => (item={'local_intf': 'Gi0/1', 'neighbor': 'iosv-2'}) => changed=false
      ansible_loop_var: item
      item:
        local_intf: Gi0/1
        neighbor: iosv-2
      msg: All assertions passed
    ok: [iosv-2] => (item={'local_intf': 'Gi0/0', 'neighbor': 'iosv-0'}) => changed=false
      ansible_loop_var: item
      item:
        local_intf: Gi0/0
        neighbor: iosv-0
      msg: All assertions passed
    ok: [iosv-2] => (item={'local_intf': 'Gi0/1', 'neighbor': 'iosv-1'}) => changed=false
      ansible_loop_var: item
      item:
        local_intf: Gi0/1
        neighbor: iosv-1
      msg: All assertions passed
    META: ran handlers
    META: ran handlers
    
    PLAY RECAP ************************************************************************************************************
    iosv-0                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    iosv-1                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    iosv-2                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

    Если мы более внимательно посмотрим на вывод первой задачи, мы увидим под parsedключом, а также установив fact ( lldp_neighbors), что у нас есть структурированные данные, через которые проходит необработанный вывод NTC Templates.

    Вторая задача показывает цикл для каждого хоста и то, itemчто он использует во время цикла. Если вы посмотрите на нашу книгу, мы используем local_intfи neighborдля наших утверждений из нашей approved_neighborsпеременной.

    Резюме

    Надеюсь, вам понравился этот пост в блоге, и вы немного больше понимаете о TextFSM, шаблонах NTC и о том, насколько легко их использовать с Ansible. Простота использования не является уникальной для Ansible, поскольку этого также можно легко достичь с помощью Netmiko или необработанного Python, но с использованием Ansible из-за промышленного принятия Ansible.

  • Анонс Windows Server 2022 – превью

    Из сообщения Бернардо Калдаса, вице-президента по управлению программами, Core OS и периферийной инфраструктуре Microsoft:

    Сегодня мы объявляем, что появилась Windows Server 2022 превью версия, следующий выпуск Long-Term Servicing Channel (LTSC), будет общедоступен позднее, но в этом календарном году. 2022 версия основана на Windows Server 2019, нашем самом быстром внедрении Windows Server за всю историю. Этот выпуск включает расширенную многоуровневую безопасность, гибридные возможности с Azure и гибкую платформу для модернизации приложений с помощью контейнеров.

    На протяжении многих лет такие компании, как  Forever 21  и  Sentara Healthcare, используют Windows Server для своих промышленных серверов. Большое спасибо всем членам сообщества Windows Server, а также нашим клиентам и партнерам за вашу поддержку и отзывы, которые вдохновили нас на инновации, которые мы встроили в этот новый выпуск.

    Расширенная многоуровневая безопасность

    В связи с растущим числом угроз кибербезопасности и быстро нарастающим числом инцидентов, безопасность становится главным приоритетом для наших клиентов. Windows Server 2022 включает новые возможности безопасности, такие как secured-core сервер и secure connectivity.

    Сервер с защищенным ядром (secured-core) объединяет мощную защиту от угроз для обеспечения многоуровневой безопасности оборудования, микропрограмм и операционной системы. Он использует Trusted Platform Module 2.0 и System Guard для безопасной загрузки Windows Server и минимизации риска, связанного с уязвимостями микропрограмм. Сервер с защищенным ядром также включает функции безопасности на основе виртуализации (VBS), такие как Credential Guard и целостность кода, защищенного гипервизором (HVCI). Credential Guard,  обеспечивает превентивную защиту конфиденциальных активов, таких как учетные данные, а HVCI применяет аппаратную безопасность, чтобы предотвратить вмешательство продвинутых вредоносных программ в систему.

    Защищенное соединение (secure connectivity) добавляет дополнительный уровень безопасности во время передачи данных. Windows Server 2022 улучшает безопасность подключения за счет более быстрого и безопасного протокола передачи зашифрованного гипертекста (HTTPS) и безопасности транспортного уровня (TLS) 1.3, включенных по умолчанию. Клиенты также могут обеспечить дополнительную безопасность соединения с сервером с помощью стандартного шифрования AES-256, которое теперь поддерживается в протоколе SMB.

    Гибридные возможности с Azure

    Мы предлагаем новые возможности, которые позволяют клиентам использовать преимущества облачных инноваций в их локальных средах. Azure Arc  и  служба миграции хранилища – это две ключевые гибридные возможности, которые лучше всего работают с Windows Server 2022.

    Azure Arc позволяет клиентам, обеспечивать безопасность и управлять Windows Server локальной среды с edge, или в многооблачных средах из единой панели управления в Azure. С помощью Azure Arc клиенты могут легко использовать для этих серверов возможности управления Azure, такие как Azure Policy, Azure Monitor и Azure Defender. Более того, несколько простых щелчков мышью в Windows Admin Center могут включить подключение к Azure Arc. Кроме того, мы  улучшили Windows Admin Center v2103, добавив значительно улучшенное управление виртуальными машинами, более простую программу просмотра событий и многие другие обновления. Центр администрирования Windows также доступен на портале Azure.

    Windows Server 2022 улучшает беспрепятственное подключение локальных файловых серверов к файловым серверам в Azure. Обновления службы миграции хранилища позволяют клиентам переносить файловые серверы с NetApp FAS на серверы Windows. Использование службы миграции хранилища  для миграции данных на сервера позволяет клиентам поддерживать низкую задержку при одновременном сокращении объема локального хранилища.

    Гибкая платформа приложений

    Клиенты используют Windows Server для запуска крупномасштабных и распределенных приложений. Следовательно, мы уделяем неустанное внимание предоставлению возможностей платформы и инструментов, которые улучшают скорость разработки и поддержку критически важных для бизнеса промышленных сред, таких как SQL Server.

    В этом выпуске мы добавляем несколько улучшений платформы для  контейнеров Windows , включая совместимость приложений и возможности контейнера Windows с Kubernetes. Основное улучшение включает уменьшение размера образа контейнера Windows, что приводит к сокращению времени загрузки и повышению производительности. Кроме того, теперь вы можете запускать приложения, зависящие от Azure Active Directory, с групповыми учетными записями управляемых служб (gMSA) без присоединения домена к узлу контейнера. Кроме того, есть несколько других улучшений, которые упрощают работу с контейнером Windows с Kubernetes. Эти улучшения включают поддержку контейнеров хост-процесса для конфигурации узла, IPv6 и согласованную реализацию сетевой политики с Calico.

    Помимо улучшений платформы, у нас есть обновленный инструмент Windows Admin Center, который упрощает контейнеризацию приложений .NET. Когда приложение окажется в контейнере, вы можете разместить его в Реестре контейнеров Azure, чтобы затем развернуть его в других службах Azure, включая службу Azure Kubernetes.

    Windows Server 2022 поддерживает критически важные для бизнеса и крупномасштабные приложения, такие как SQL Server, которым требуется 48 ТБ памяти и 2048 логических ядер, работающих на 64 физических сокетах.

    Попробуйте новую превью версию Windows Server 2022

    Благодарим вас за то, что вы являетесь ценным клиентом Windows Server и своим энтузиазмом и отзывами помогли нам сформировать этот новый выпуск. 

    Не забывайте:

    • Клиенты Software Assurance могут воспользоваться правами на новую версию и смогут выполнить обновление до Windows Server 2022.
    • Попробуйте превью версию Windows Server 2022 LTSC

    Если Вам нужна поддержка Windows серверов или Azure обращайтесь office@itfb.com.ua