Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

У цій статті я розповім, як налаштувати OpenDaylight для роботи з мережевим обладнанням, а також покажу, як за допомогою Листоноша та простих RESTCONF запитів цим обладнанням можна керувати. Працювати із залізом ми не будемо, а натомість розгорнемо невеликі віртуальні лабораторії з одним-єдиним роутером за допомогою Vrnetlab поверх Ubuntu LTS 20.04.

Детальну настройку я покажу спочатку на прикладі роутера Juniper vMX 20.1R1.11, а потім ми порівняємо її з налаштуванням Cisco xRV9000 7.0.2.

Зміст

  • необхідні знання
  • Частина 1: коротко обговорюємо OpenDaylight (далі за текстом ODL), Листоноша и Vrnetlab і навіщо вони нам потрібні
  • Частина 2: опис віртуальної лабораторії
  • Частина 3: налаштовуємо OpenDaylight
  • Частина 4: налаштовуємо Vrnetlab
  • Частина 5: за допомогою Листоноша підключаємо віртуальний роутер (Juniper vMX) до ODL
  • Частина 6: отримуємо та змінюємо конфігурацію роутера за допомогою Листоноша и ODL
  • Частина 7: додаємо Cisco xRV9000
  • Висновок
  • PS
  • Список літератури

необхідні знання

Для того, щоб стаття не перетворилася на простирадло, деякі технічні подробиці я опустив (з посиланнями на літературу, де про них можна почитати).

У зв'язку з чим пропоную вам теми, які добре б (але майже не обов'язково) знати перед прочитанням:

Частина 1: небагато теорії

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

  • Відкрита SDN платформа для керування та автоматизації всіляких мереж, що підтримується Linux Foundation
  • Java inside
  • Заснований на Model-Driven Service Abstraction Level (MD-SAL)
  • Використовує YANG моделі для автоматичного створення RESTCONF API мережних пристроїв

Основний модуль управління мережею. Саме через нього ми спілкуватимемося з підключеними пристроями. Керується через власний API.

Детальніше про OpenDaylight можна прочитати тут.

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

  • Інструмент для тестування API
  • Простий та зручний для використання інтерфейс

У нашому випадку він нам цікавий як засіб для надсилання REST запитів на API OpenDaylight'а. Можна, звичайно, і вручну запити відправляти, але в Postman все виглядає дуже наочно і для наших цілей підходить якнайкраще.

Для бажаючих покопатися: по ньому написано багато навчальних матеріалів (наприклад).

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

  • Інструмент для розгортання віртуальних роутерів у Docker'і
  • Підтримує: Cisco XRv, Juniper vMX, Arista vEOS, Nokia VSR та ін.
  • Open Source

Дуже цікавий, але маловідомий інструмент. У нашому випадку з його допомогою ми запустимо Juniper vMX та Cisco xRV9000 на звичайній Ubuntu 20.04 LTS.

Прочитати докладніше про нього можна на сторінці проекту.

Частина 2: лабораторна робота

В рамках цього туторіалу ми налаштовуватимемо наступну систему:

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Як це працює

  • Juniper vMX піднімається в Docker контейнері (засобами Vrnetlab) і функціонує як звичайнісінький віртуальний роутер.
  • ODL підключений до роутера і дозволяє керувати ним.
  • Листоноша запущено на окремій машині і через нього ми відправляємо команди ODL: на підключення/видалення роутера, зміна конфігурації тощо.

Коментар до пристрою системи

Juniper vMX и ODL вимагають чимало ресурсів для своєї стабільної роботи. Один тільки vMX вимагає 6 Gb оперативної пам'яті та 4 ядра. Тому було прийнято рішення винести всіх "важкоатлетів" на окрему машину (Heulett Packard Enterprise MicroServer ProLiant Gen8, Ubuntu 20.04 LTS). Роутер, звичайно, на ній не "літає", але для невеликих експериментів продуктивності вистачає.

Частина 3: налаштовуємо OpenDaylight

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Актуальна версія ODL на момент написання статті - Magnesium SR1

1) Встановлюємо Java OpenJDK 11 (за більш детальною установкою сюди)

ubuntu:~$ sudo apt install default-jdk

2) Знаходимо і завантажуємо свіже складання ODL звідси
3) Розархівуємо завантажений архів
4) Переходимо до отриманої директорії
5) Запускаємо ./bin/karaf

На цьому кроці ODL маємо запуститися і ми опинимося в консолі (Для доступу ззовні використовується порт 8181, чим ми скористаємося далі).

Далі встановлюємо ODL Features, призначені для роботи з протоколами NETCONF и RESTCONF. Для цього в консолі ODL виконуємо:

opendaylight-user@root> feature:install odl-netconf-topology odl-restconf-all

На цьому найпростіше налаштування ODL завершено. (Докладніше можна прочитати тут).

Частина 4: налаштовуємо Vrnetlab

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

підготовка системи

перед установкою Vrnetlab необхідно поставити необхідні пакети. Такі як Docker, мерзотник, sshpass:

ubuntu:~$ sudo apt update
ubuntu:~$ sudo apt -y install python3-bs4 sshpass make
ubuntu:~$ sudo apt -y install git
ubuntu:~$ sudo apt install -y 
    apt-transport-https ca-certificates 
    curl gnupg-agent software-properties-common
ubuntu:~$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
ubuntu:~$ sudo add-apt-repository 
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu 
   $(lsb_release -cs) 
   stable"
ubuntu:~$ sudo apt update
ubuntu:~$ sudo apt install -y docker-ce docker-ce-cli containerd.io

Установка Vrnetlab

Для установки Vrnetlab клонуємо відповідний репозиторій з github:

ubuntu:~$ cd ~
ubuntu:~$ git clone https://github.com/plajjan/vrnetlab.git

Переходимо до директорії vrnetlab:

ubuntu:~$ cd ~/vrnetlab

Тут можна побачити всі скрипти, які необхідні для запуску. Зверніть увагу, що для кожного типу роутера зроблено відповідну директорію:

ubuntu:~/vrnetlab$ ls
CODE_OF_CONDUCT.md  config-engine-lite        openwrt           vr-bgp
CONTRIBUTING.md     csr                       routeros          vr-xcon
LICENSE             git-lfs-repo.sh           sros              vrnetlab.sh
Makefile            makefile-install.include  topology-machine  vrp
README.md           makefile-sanity.include   veos              vsr1000
ci-builder-image    makefile.include          vmx               xrv
common              nxos                      vqfx              xrv9k

Створюємо image роутера

Кожен роутер, який підтримується Vrnetlabмає свою унікальну процедуру налаштування. В разі Juniper vMX нам достатньо закинути .tgz архів з роутером (завантажити його можна з офіційного сайту) в директорію vmx та виконати команду make:

ubuntu:~$ cd ~/vrnetlab/vmx
ubuntu:~$ # Копируем в эту директорию .tgz архив с роутером
ubuntu:~$ sudo make

Складання образу vMX займе близько 10-20 хвилин. Саме час сходити заварити каву!

Чому так довго, запитаєте ви?

Переклад відповіді автора на це запитання:

"Це пов'язано з тим, що при першому запуску VCP (Control Plane) зчитує файл конфігурації, який визначає, чи він працюватиме як VRR VCP в vMX. Раніше цей запуск виконувався під час запуску Docker, але це означало, що VCP завжди перезапускався один раз, перш ніж віртуальний маршрутизатор ставав доступним, що призводило до тривалого часу завантаження (близько 5 хвилин) Тепер перший запуск VCP виконується під час складання образу Docker, і оскільки збірка Docker не може бути запущена з параметром -privileged, це означає, що qemu працює без апаратного прискорення KVM і, таким чином, складання займає дуже багато часу.Під час цього процесу виводиться багато логів, так що, принаймні, ви зможете побачити, що відбувається.Я думаю, що тривале складання не таке страшне, тому що образ ми створюємо один раз, а запускаємо безліч.

Після можна буде побачити image нашого роутера в Docker:

ubuntu:~$ sudo docker image list
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
vrnetlab/vr-vmx     20.1R1.11           b1b2369b453c        3 weeks ago         4.43GB
debian              stretch             614bb74b620e        7 weeks ago         101MB

Запускаємо контейнер vr-vmx

Запускаємо командою:

ubuntu:~$ sudo docker run -d --privileged --name jun01 b1b2369b453c

Далі можемо переглянути інформацію про активні контейнери:

ubuntu:~$ sudo docker container list
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS                                                 NAMES
120f882c8712        b1b2369b453c        "/launch.py"        2 minutes ago       Up 2 minutes (unhealthy)   22/tcp, 830/tcp, 5000/tcp, 10000-10099/tcp, 161/udp   jun01

Підключаємось до роутера

IP-адресу мережного інтерфейсу роутера можна отримати наступною командою:

ubuntu:~$ sudo docker inspect --format '{{.NetworkSettings.IPAddress}}' jun01
172.17.0.2

За замовчуванням, Vrnetlab створює у роутера користувача vrnetlab/VR-netlab9.
Підключаємось за допомогою ssh:

ubuntu:~$ ssh [email protected]
The authenticity of host '172.17.0.2 (172.17.0.2)' can't be established.
ECDSA key fingerprint is SHA256:g9Sfg/k5qGBTOX96WiCWyoJJO9FxjzXYspRoDPv+C0Y.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '172.17.0.2' (ECDSA) to the list of known hosts.
Password:
--- JUNOS 20.1R1.11 Kernel 64-bit  JNPR-11.0-20200219.fb120e7_buil
vrnetlab> show version
Model: vmx
Junos: 20.1R1.11

На цьому налаштування роутера завершено.

Рекомендації щодо встановлення для роутерів різних вендорів можна знайти на github проекту у відповідних директоріях.

Частина 5: Postman – підключаємо роутер до OpenDaylight

Установка Postman

Для встановлення достатньо скачати програму звідси.

Підключення роутера до ODL

Створимо PUT запит:

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

  1. Рядок запиту:
    PUT http://10.132.1.202:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/jun01
  2. Тіло запиту (вкладка Body):
    <node xmlns="urn:TBD:params:xml:ns:yang:network-topology">
    <node-id>jun01</node-id>
    <host xmlns="urn:opendaylight:netconf-node-topology">172.17.0.2</host>
    <port xmlns="urn:opendaylight:netconf-node-topology">22</port>
    <username xmlns="urn:opendaylight:netconf-node-topology">vrnetlab</username>
    <password xmlns="urn:opendaylight:netconf-node-topology">VR-netlab9</password>
    <tcp-only xmlns="urn:opendaylight:netconf-node-topology">false</tcp-only>
    <schema-cache-directory xmlns="urn:opendaylight:netconf-node-topology">jun01_cache</schema-cache-directory>
    </node>
  3. На вкладці Authorization необхідно виставити параметр Basic Auth та логін/пароль: admin/admin. Це необхідно для доступу до ODL:
    Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab
  4. На вкладці Headers необхідно додати два заголовки:
    • Accept application/xml
    • Content-Type application/xml

Наш запит сформовано. Відправляємо. Якщо все було налаштовано правильно, то нам має повернутися статус "201 Created":

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Що робить цей запит?

Ми створюємо node всередині ODL з параметрами реального роутера, якого ми хочемо отримати доступ.

xmlns="urn:TBD:params:xml:ns:yang:network-topology"
xmlns="urn:opendaylight:netconf-node-topology"

Це внутрішні простори імен XML (Простір імен XML) для ODL відповідно до яких він створює node.

Далі, відповідно, ім'я роутера - це node-id, адреса роутера - господар і т.д.

Найцікавіший рядок — останній. Schema-cache-directory створює директорію, до якої викачуються всі файли YANG Schema підключеного роутера. Знайти їх можна в $ODL_ROOT/cache/jun01_cache.

Перевіряємо підключення роутера

Створимо GET запит:

  1. Рядок запиту:
    GET http://10.132.1.202:8181/restconf/operational/network-topology:network-topology/topology/topology-netconf/
  2. На вкладці Authorization необхідно виставити параметр Basic Auth та логін/пароль: admin/admin.

Відправляємо. Повинні отримати статус "200 OK" та список усіх підтримуваних пристроєм YANG Schema:

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Коментар: Щоб побачити останнє, в моєму випадку необхідно було почекати близько 10 хвилин після виконання. PUT, поки все YANG sсhema вивантажаться на ODL. До цього моменту при виконанні даного GET запиту буде виведено таке:

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Видаляємо роутер

Створимо DELETE запит:

  1. Рядок запиту:
    DELETE http://10.132.1.202:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/jun01
  2. На вкладці Authorization необхідно виставити параметр Basic Auth та логін/пароль: admin/admin.

Частина 6: Змінюємо конфігурацію роутера

Отримуємо конфігурацію

Створимо GET запит:

  1. Рядок запиту:
    GET http://10.132.1.202:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/jun01/yang-ext:mount/
  2. На вкладці Authorization необхідно виставити параметр Basic Auth та логін/пароль: admin/admin.

Відправляємо. Повинні отримати статус "200 OK" та конфігурацію роутера:

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Створюємо конфігурацію

Як приклад створимо наступну конфігурацію та змінимо її:

protocols {
    bgp {
        disable;
        shutdown;
    }
}

Створимо POST запит:

  1. Рядок запиту:
    POST http://10.132.1.202:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/jun01/yang-ext:mount/junos-conf-root:configuration/junos-conf-protocols:protocols
  2. Тіло запиту (вкладка Body):
    <bgp xmlns="http://yang.juniper.net/junos/conf/protocols">
    <disable/>
    <shutdown>
    </shutdown>
    </bgp>
  3. На вкладці Authorization необхідно виставити параметр Basic Auth та логін/пароль: admin/admin.
  4. На вкладці Headers необхідно додати два заголовки:
    • Accept application/xml
    • Content-Type application/xml

Після надсилання повинні отримати статус "204 No Content"

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

Створимо GET запит:

  1. Рядок запиту:
    GET http://10.132.1.202:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/jun01/yang-ext:mount/junos-conf-root:configuration/junos-conf-protocols:protocols
  2. На вкладці Authorization необхідно виставити параметр Basic Auth та логін/пароль: admin/admin.

Після виконання запиту побачимо таке:

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Змінюємо конфігурацію

Змінимо інформацію про протокол BGP. Після наших дій вона виглядатиме так:

protocols {
    bgp {
        disable;
    }
}

Створимо PUT запит:

  1. Рядок запиту:
    PUT http://10.132.1.202:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/jun01/yang-ext:mount/junos-conf-root:configuration/junos-conf-protocols:protocols
  2. Тіло запиту (вкладка Body):
    <protocols xmlns="http://yang.juniper.net/junos/conf/protocols">
    <bgp>
        <disable/>
    </bgp>
    </protocols>
  3. На вкладці Authorization необхідно виставити параметр Basic Auth та логін/пароль: admin/admin.
  4. На вкладці Headers необхідно додати два заголовки:
    • Accept application/xml
    • Content-Type application/xml

Використовуючи попередній GET запит, бачимо зміни:

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Видаляємо конфігурацію

Створимо DELETE запит:

  1. Рядок запиту:
    DELETE http://10.132.1.202:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/jun01/yang-ext:mount/junos-conf-root:configuration/junos-conf-protocols:protocols
  2. На вкладці Authorization необхідно виставити параметр Basic Auth та логін/пароль: admin/admin.

при виклику GET запиту з інформацією про протоколи побачимо таке:

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Доповнення:

Щоб змінити конфігурацію, не обов'язково надсилати тіло запиту у форматі XML. Це можна зробити і у форматі JSON.

Для цього, наприклад, у запиті PUT на зміну конфігурації замінимо тіло запиту на:

{
    "junos-conf-protocols:protocols": {
        "bgp": {
            "description" : "Changed in postman" 
        }
    }
}

Не забудьте змінити на вкладці Headers заголовки на:

  • Accept application/json
  • Content-Type application/json

Після відправки отримаємо наступний результат (Відповідь дивимося використовуючи GET запит):

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Частина 7: додаємо Cisco xRV9000

Що ми всі про Джуніпера, та про Джуніпера? Давайте про Cisco поговоримо!
У мене знайшовся xRV9000 версії 7.0.2 (звірюга, якому потрібні 8Gb RAM і 4 ядра. У вільному доступі не лежить, тому звертайтесь до Cisco) - його і запустимо.

Запуск контейнера

Процес створення контейнера Docker практично нічим не відрізняється від Juniper. Аналогічно, закидаємо .qcow2 файл з роутером у директорію, що відповідає його назві, (в даному випадку xrv9k) та виконуємо команду make docker-image.

За кілька хвилин бачимо, що образ створився:

ubuntu:~$ sudo docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
vrnetlab/vr-xrv9k   7.0.2               54debc7973fc        4 hours ago         1.7GB
vrnetlab/vr-vmx     20.1R1.11           b1b2369b453c        4 weeks ago         4.43GB
debian              stretch             614bb74b620e        7 weeks ago         101MB

Здійснюємо запуск контейнера:

ubuntu:~$ sudo docker run -d --privileged --name xrv01 54debc7973fc

Через деякий час дивимося, що контейнер запустився:

ubuntu:~$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                 PORTS                                                      NAMES
058c5ecddae3        54debc7973fc        "/launch.py"        4 hours ago         Up 4 hours (healthy)   22/tcp, 830/tcp, 5000-5003/tcp, 10000-10099/tcp, 161/udp   xrv01

Підключаємося по ssh:

ubuntu@ubuntu:~$ ssh [email protected]
Password:

RP/0/RP0/CPU0:ios#show version
Mon Jul  6 12:19:28.036 UTC
Cisco IOS XR Software, Version 7.0.2
Copyright (c) 2013-2020 by Cisco Systems, Inc.

Build Information:
 Built By     : ahoang
 Built On     : Fri Mar 13 22:27:54 PDT 2020
 Built Host   : iox-ucs-029
 Workspace    : /auto/srcarchive15/prod/7.0.2/xrv9k/ws
 Version      : 7.0.2
 Location     : /opt/cisco/XR/packages/
 Label        : 7.0.2

cisco IOS-XRv 9000 () processor
System uptime is 3 hours 22 minutes

Підключаємо роутер до OpenDaylight

Додавання відбувається абсолютно аналогічним з vMX чином. Потрібно лише назви поміняти.
PUT запит:
Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Через деякий час викликаємо GET запит, щоб перевірити, чи все підключилося:
Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Змінюємо конфігурацію

Налаштуємо наступну конфігурацію:

!
router ospf LAB
 mpls ldp auto-config
!

Створимо POST запит:

  1. Рядок запиту:
    POST http://10.132.1.202:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/xrv01/yang-ext:mount/Cisco-IOS-XR-ipv4-ospf-cfg:ospf
  2. Тіло запиту (вкладка Body):
    {
        "processes": {
            "process": [
                {
                    "process-name": "LAB",
                    "default-vrf": {
                        "process-scope": {
                            "ldp-auto-config": [
                                null
                            ]
                        }
                    }
                }
            ]
        }
    }
  3. На вкладці Authorization необхідно виставити параметр Basic Auth та логін/пароль: admin/admin.
  4. На вкладці Headers необхідно додати два заголовки:
    • Accept application/json
    • Content-Type application/json

Після його виконання мають набути статусу "204 No Content".

Перевіримо, що в нас вийшло.
Для цього створимо GET запит:

  1. Рядок запиту:
    GET http://10.132.1.202:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/xrv01/yang-ext:mount/Cisco-IOS-XR-ipv4-ospf-cfg:ospf
  2. На вкладці Authorization необхідно виставити параметр Basic Auth та логін/пароль: admin/admin.

Після виконання повинні побачити таке:

Автоматизація мережевих сервісів або як зібрати віртуальну лабораторію за допомогою OpenDaylight, Postman та Vrnetlab

Для видалення конфігурації використовуємо DELETE:

  1. Рядок запиту:
    DELETE http://10.132.1.202:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/xrv01/yang-ext:mount/Cisco-IOS-XR-ipv4-ospf-cfg:ospf
  2. На вкладці Authorization необхідно виставити параметр Basic Auth та логін/пароль: admin/admin.

Висновок

Усього, як ви могли помітити, процедури підключення Cisco і Juniper до OpenDaylight не відрізняються - це відкриває досить широкий простір для творчості. Починаючи від управління конфігураціями всіх компонентів мережі та закінчуючи створенням власних мережевих політик.
У цьому туторіалі я навів найпростіші приклади того, як можна взаємодіяти з мережевим обладнанням за допомогою OpenDaylight. Без сумніву, запити з наведених прикладів можна зробити набагато складніше і налаштовувати цілі сервіси одним кліком миші – все обмежено лише вашою фантазією*

Далі буде ...

PS

Якщо ви раптом все це вже знаєте або, навпаки, пройшли і вам запал у душу ODL, то рекомендую подивитися у бік розробки програм на контролері ODL. Почати можна звідси.

Успішних експериментів!

Список літератури

  1. Vrnetlab: Emulate networks using KVM and Docker / Brian Linkletter
  2. OpenDaylight Cookbook / Mathieu Lemay, Alexis de Talhouet, Et al
  3. Network Programmability with YANG / Benoît Claise, Loe Clarke, Jan Lindblad
  4. Learning XML, Second Edition / Erik T. Ray
  5. Effective DevOps / Jennifer Davis, Ryn Daniels

Джерело: habr.com

Додати коментар або відгук