Знакомимся с 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 Function | Action |
---|---|
GET | Requests data from a destination |
POST | Submits data to a specific destination |
PUT | Replaces data in a specific destination |
PATCH | Appends data to a specific destination |
DELETE | Removes data from a specific destination |
HEAD | GET without a body |
OPTIONS | Discover which operations are supported by a data resource |
В сравнении с NETCONF будет так:
RESTCONF | NETCONF |
---|---|
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:

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

Ответ устройства:
<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:

Теперь смотрим:
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
Далее делаем всё как в лабе, у меня немного отличается:

Почему в лабе не указывается заголовок 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.
Element | Explanation |
---|---|
requests.get() | The method that actually makes the GET request |
url | The variable that holds the URL address string |
auth | The tuple variable created to hold the authentication information |
headers | A parameter that is assigned the headers variable |
verify | Verification 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.