Знакомимся с RESTCONF

Знакомимся с RESTCONF — продолжаем изучать возможности скриптинга на оборудовании CISCO, на этот раз RESTCONF.

Предварительно нужно прочесть Знакомимся с NETCONF. Изложение идёт последовательно и если этого не сделать, то может быть непонятно.


RESTCONF

Самое первое тут: RESTCONF не замена NETCONF, хотя и посвежее (RFC8040, 2017 год), они живут параллельно. В чём отличие? Практически одинаковые они, работают с одной и той же YANG структурой, но разные модели доступа и запросов.

RESTCONF is an HTTP-based protocol that provides a programmatic interface for accessing data 
defined in YANG, using the datastore concepts defined in the Network Configuration Protocol 
(NETCONF).

NETCONF это SSH, а RESTCONF это HTTPS. Запросы NETCONF это NETCONF RPC, запросы RESTCONF это RESTful API. Оба умеют возвращать данные либо в виде XML, либо JSON. Поэтому для RESTCONF помимо Python, возможно что-то делать через программы типа Postman (скачиваем, ставим), которые могут RESTful. Что это значит "могут RESTful"?

Немного про RESTful APIs: так говорят про любой API, который использует REST (Representational State Transfer). Сам REST определяет способ обмена информацией.

REST is an architectural style for designing web service applications. An API can be considered 
“RESTful” if it has the following features:

Client-Server - The client handles the front end and the server handles the back end. Either can be 
replaced independently of the other.
Stateless - No client data is stored on the server between requests. The session state is stored 
on the client.
Cacheable - Clients can cache responses to improve performance.

Сама CISCO видит это так:

REST HTTP Functions

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

HTTP FunctionAction
GETRequests data from a destination
POSTSubmits data to a specific destination
PUTReplaces data in a specific destination
PATCHAppends data to a specific destination
DELETERemoves data from a specific destination
HEADGET without a body
OPTIONSDiscover which operations are supported by a data resource
HTTP Functions

В сравнении с NETCONF будет так:

RESTCONFNETCONF
GET <get><get-config>
POST <edit-config> (operation="create")
PUT <edit-config> (operation="create/replace")
PATCH <edit-config> (operation="merge")
DELETE<edit-config> (operation="delete")
Включение агента

Старые железки не знают RESTCONF. На устройстве должен быть агент, всё это IOS XE последних версий. Чем свежее версия, тем больше возможностей по настройке. Агент нужно включить:

R1(config)# restconf
R1(config)# ip http secure-server
R1(config)# ip http authentication local - если используется локальный пользователь

Затем проверить:

csr1000v-1# show platform software yang-management process
confd            : Running    
nesd             : Running    
syncfd           : Running    
ncsshd           : Running    
dmiauthd         : Running    
nginx            : Running    
ndbmand          : Running    
pubd             : Running  

Снова DevNet в помощь, ну а где ещё взять железку IOS XE, не в продакшене и чтобы можно было её мучать? Открываем лабу.


Настройка с помощью Postman

Отключаем в Postman проверку сертификатов:

  • File > Settings;
  • SSL certificate verification > OFF.
Запрос GET

Новый запрос GET, на закладке Authorization:

Знакомимся с RESTCONF

На закладке Headers, сначала XML, application/yang-data+xml:

Знакомимся с RESTCONF

Ответ устройства:

<restconf xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf">
    <data/>
    <operations/>
    <yang-library-version>2016-06-21</yang-library-version>
</restconf>

Теперь меняем XML на JSON, application/yang-data+json:

{
    "ietf-restconf:restconf": {
        "data": {},
        "operations": {},
        "yang-library-version": "2016-06-21"
    }
}

Если URL выбрать как в лабе:

https://10.10.20.48:443/restconf/data/Cisco-IOS-XE-native:native 

то в ответ вывалит всю конфигурацию. А если:

https://10.10.20.48/restconf/data/ietf-interfaces:interfaces

то инфу по интерфейсам. Добавив к ссылке интерфейсов /interface=GigabitEthernet1, получим вывод для Gi1:

{
    "ietf-interfaces:interface": {
        "name": "GigabitEthernet1",
        "description": "MANAGEMENT INTERFACE - DON'T TOUCH ME",
        "type": "iana-if-type:ethernetCsmacd",
        "enabled": true,
        "ietf-ip:ipv4": {
            "address": [
                {
                    "ip": "10.10.20.48",
                    "netmask": "255.255.255.0"
                }
            ]
        },
        "ietf-ip:ipv6": {}
    }
}
Запрос PUT

Меняем GET на PUT, URL https://10.10.20.48/restconf/data/ietf-interfaces:interfaces/interface=Loopback1, а на закладке Body:

{
 "ietf-interfaces:interface": {
 "name": "Loopback1",
 "description": "[Student Name]'s Loopback",
 "type": "iana-if-type:softwareLoopback",
 "enabled": true,
 "ietf-ip:ipv4": {
 "address": [
 {
 "ip": "10.1.1.1",
 "netmask": "255.255.255.0"
 }
 ]
 },
 "ietf-ip:ipv6": {}
 }
}

При этом нужно выставить переключатели raw, JSON и жмакаем Send. Результат запроса 201 Created:

Знакомимся с RESTCONF

Теперь смотрим:

csr1000v-1# show ip int br
Interface              IP-Address      OK? Method Status                Protocol
GigabitEthernet1       10.10.20.48     YES NVRAM  up                    up      
GigabitEthernet2       unassigned      YES NVRAM  administratively down down    
GigabitEthernet3       unassigned      YES NVRAM  administratively down down    
Loopback1              10.1.1.1        YES other  up                    up  

Не так вот прям просто, но вполне жизнеспособно. Кроме этого можно брать структуры, полученные с помощью GET, закидывать их в Body, менять в них требуемые параметры, менять GET на PUT и отправлять. Предлагаю попробовать таким способом изменить hostname на R1:

https://10.10.20.48:443/restconf/data/Cisco-IOS-XE-native:native/hostname

Настройка с помощью Python

Скрипты для Python можно брать прям из Postman. Если мы делаем какой-то запрос, то справа есть кнопа </>, нажимаем и вывалится соотвествующий этому запросу скрипт Python. Открываем промпт миниконды:

(base) C:\Users\Andy> pip3 install requests

Далее делаем всё как в лабе, у меня немного отличается:

Знакомимся с RESTCONF

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

  • Content-Type: Specify the type of data being sent from the client;
  • Accept: Specify the type of data being requested by the client.
ElementExplanation
requests.get()The method that actually makes the GET request
urlThe variable that holds the URL address string
authThe tuple variable created to hold the authentication information
headersA parameter that is assigned the headers variable
verifyVerification of the SSL certificate when the request is made

Проверку сертификата отключили, но не отключили жирный варнинг о недостоверном сертификате. Нужно подсунуть строку:

requests.packages.urllib3.disable_warnings()

Теперь по интерфейсам:

import requests
import json

requests.packages.urllib3.disable_warnings()

host = '10.10.20.48'
port = 443
username = 'developer'
password = 'C1sco12345'

def get_interfaces_json():
    url = "https://{h}:{p}/restconf/data/ietf-interfaces:interfaces".format(h = host, p = port)
    headers = {"Accept" : "application/yang-data+json", "Content-type" : "application/yang-data+json"}
    response = requests.get(url, auth = (username, password), headers = headers, verify = False)
    print(response.text)

get_interfaces_json()

И создадим интерфейс, как мы это делали с Postman:

import requests
import json

requests.packages.urllib3.disable_warnings()

host = '10.10.20.48'
port = 443
username = 'developer'
password = 'C1sco12345'

yangConfig = {
 "ietf-interfaces:interface": {
 "name": "Loopback2",
 "description": "Changed interface",
 "type":  "iana-if-type:softwareLoopback",
 "enabled": True,
 "ietf-ip:ipv4": {
 "address": [
     {
     "ip": "10.2.1.1",
     "netmask": "255.255.255.0"
     }
 ]
 },
 "ietf-ip:ipv6": {}
 }
}
 
def set_interfaces_json():
    url = "https://{h}:{p}/restconf/data/ietf-interfaces:interfaces/interface=Loopback2".format(h = host, p = port)
    headers = {"Content-Type" : "application/yang-data+json", "Accept" : "application/yang-data+json"}
    response = requests.put(url, data = json.dumps(yangConfig), auth = (username, password), headers = headers, verify = False)
    print(response.text)

set_interfaces_json()

Здесь мы отправляем структуру yangConfig, заголовок Content-Type нужен, так как по умолчанию ожидается XML. Команда не выводит никаких результатов кроме кода операции, после выполнения команды смотрим интерфейсы:

csr1000v-1# show ip int br
Interface              IP-Address      OK? Method Status                Protocol
GigabitEthernet1       10.10.20.48     YES NVRAM  up                    up      
GigabitEthernet2       unassigned      YES NVRAM  administratively down down    
GigabitEthernet3       unassigned      YES NVRAM  up                    up      
Loopback1              10.1.1.1        YES other  up                    up      
Loopback2              10.2.1.1        YES other  up                    up  

По итогам лабы обязательно нужно разобрать как строится URL RESTCONF:

https://{host}:{port}/restconf/data/<[YANG MODULE:]CONTAINER>/<LEAF/LIST>[?<OPTIONS>]

Заключение

NETCONF/RESTCONF: все эти запросы невозможно запомнить или набивать от руки. Можно дёргать из готовых скриптов, но тогда там должны быть очень подробные комментарии. Чтобы было понятно. Никто такие комментарии (конечно же) писать не будет.

Есть множество ресурсов со справочной информацией, например:

Но сёрфить все эти ресурсы, нужен вагон времени. Проблема не в количестве инфы, а в том как из этого многобразия выбрать необходимое за оптимальное время. Поэтому лучше всего "рыба" где-то в файле Notepad++ на рабочей шаре, а ещё лучше несколько страничек с описанием в базе знаний и совсем замечательно на гитхабе. Откуда эти запросы берутся по мере надобности. Несколько готовых функций с запросами легко подтянуть из лабы.

get_hostname()
get_interfaces_json()
save_running_config()
change_interface(user_selection)
get_allinterfaces()
get_allinterfaces_status()
set_interfaces_json()

Не забываем про ссылку на книжку в конце лабы. Спасибо DevNet за наше счастливое детство! 🙂 На этом всё пока, осталось написать статейку про модуль Netmiko для Python и про Ansible.

Leave a Comment

Scroll to Top