This is a memo when setting the ACL of Cisco IOS-XE using RESTCONF among the transport protocols (NETCONF, RESTCONF, gRPC) used in model-driven programmability. There are Postman, Python, Ansible, etc. as operation tools, but this time I used Python and performed CRUD (Create, Get, Update, Delete) for the named extended ACL.
RESTCONF is an HTTP (S) based protocol that provides a RESTFul interface. In addition to XML, JSON format is also supported as the data format (NETCONF is SSH-based and only XML format).
RESTCONF / NETCONF CRUD operations
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") |
IOS originally supports REST APIs, but while REST APIs are vendor-specific implementations, RESTCONF is a protocol standardized by RFC 8040 and is a separate thing.
An overview of model-driven programmability, including RESTCONF, is summarized in the following sessions at DevNet Learning Labs. Introduction to Model Driven Programmability
[This article](https://qiita.com/tech_kitara/items/4c201a83f1f9286d002b#2-%E7%94%A8%E6%84%8F%E3%81%97%E3%81%9F%E7%92 As with% B0% E5% A2% 83), we used the Cisco dCloud environment. The target device used was "CSR1000v with IOS XE 16.08", and the Python version of the client was "3.6.8".
First, add one line of ACL name TEST
from the state where there is no ACL setting.
A request is made to the URL https: // [IP address]: [port number] / restconf / data / Cisco-IOS-XE-native: native / ip / access-list
using the PUT
method. The data in Body is defined in JSON format.
By the way, I couldn't create it with the POST
method.
create_acl.py
#!/usr/bin/python
import requests
import json
# disable warnings from SSL/TLS certificates
requests.packages.urllib3.disable_warnings()
# credentials for CSR1000v
HOST = '[IP address]'
PORT = 443 #Depends on the environment
USER = '[User name]'
PASS = '[password]'
def main():
# url string to issue request
url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip/access-list".format(h=HOST, p=PORT)
# RESTCONF media types for REST API headers
headers = {'Content-Type': 'application/yang-data+json',
'Accept': 'application/yang-data+json'}
# RESTCONF doby for new ACL
body_data = {
"Cisco-IOS-XE-native:access-list": {
"Cisco-IOS-XE-acl:extended": [
{
"name": "TEST",
"access-list-seq-rule": [
{
"sequence": "30",
"ace-rule": {
"action": "permit",
"protocol": "ip",
"ipv4-address": "192.168.4.0",
"mask": "0.0.0.255",
"dest-ipv4-address": "192.168.100.0",
"dest-mask": "0.0.0.255"
}
}
]
}
]
}
}
# this statement performs a PUT on the specified url
response = requests.put(url, auth=(USER, PASS),
headers=headers, data=json.dumps(body_data), verify=False)
# print the json that is returned
print(response)
if __name__ == '__main__':
main()
Status Code 204 (No Content) is returned.
$ python create_acl.py
<Response [204]>
Looking at the Config, the ACL was created as expected.
csr1#sh run | begin TEST
ip access-list extended TEST
permit ip 192.168.4.0 0.0.0.255 192.168.100.0 0.0.0.255
Next, try to get the information of the created ACL with RESTCONF.
Except for the main ()
function, it is the same as in 3., so the following is an excerpt.
First, for URL https: // [IP address]: [port number] / restconf / data / Cisco-IOS-XE-native: native / ip
, get IP related setting information with the GET
method. I tried it.
get_ip_info.py
def main():
# url string to issue request
url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip".format(h=HOST, p=PORT)
# RESTCONF media types for REST API headers
headers = {'Content-Type': 'application/yang-data+json',
'Accept': 'application/yang-data+json'}
# this statement performs a GET on the specified url
response = requests.get(url, auth=(USER, PASS),
headers=headers, verify=False)
# print the json that is returned
print(response.text)
I was able to confirm the settings with " access-list ": {"Cisco-IOS-XE-acl: extended ": [~]}
on the way.
By the way, before ACL setting, it was not created including this " access-list "
key. Therefore, when creating 3., even if you suddenly PUT " Cisco-IOS-XE-acl: extended ": [~]
in the lower hierarchy, an error occurred.
$ python get_ip_info.py
{
"Cisco-IOS-XE-native:ip": {
"domain": {
"name": "demo.dcloud.cisco.com"
},
"forward-protocol": {
"protocol": "nd"
},
"route": {
"ip-route-interface-forwarding-list": [
{
"prefix": "0.0.0.0",
"mask": "0.0.0.0",
"fwd-list": [
{
"fwd": "GigabitEthernet1",
"interface-next-hop": [
{
"ip-address": "198.18.128.1"
}
]
}
]
}
]
},
"ssh": {
"rsa": {
"keypair-name": "ssh-key"
},
"version": 2
},
"access-list": {
"Cisco-IOS-XE-acl:extended": [
{
"name": "TEST",
"access-list-seq-rule": [
{
"sequence": "30",
"ace-rule": {
"action": "permit",
"protocol": "ip",
"ipv4-address": "192.168.4.0",
"mask": "0.0.0.255",
"dest-ipv4-address": "192.168.100.0",
"dest-mask": "0.0.0.255"
}
}
]
}
]
},
"Cisco-IOS-XE-http:http": {
"authentication": {
"local": [null]
},
"server": true,
"secure-server": true
}
}
}
This is a deeper hierarchy URL https: // [IP address]: [port number] / restconf / data / Cisco-IOS-XE-native: native / ip / access-list / extended
, extended ACL only Is an example of pinpointing.
get_acl.py
def main():
# url string to issue request
url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip/access-list/extended".format(h=HOST, p=PORT)
# RESTCONF media types for REST API headers
headers = {'Content-Type': 'application/yang-data+json',
'Accept': 'application/yang-data+json'}
# this statement performs a GET on the specified url
response = requests.get(url, auth=(USER, PASS),
headers=headers, verify=False)
# print the json that is returned
print(response.text)
$ python get_acl.py
{
"Cisco-IOS-XE-acl:extended": [
{
"name": "TEST",
"access-list-seq-rule": [
{
"sequence": "30",
"ace-rule": {
"action": "permit",
"protocol": "ip",
"ipv4-address": "192.168.4.0",
"mask": "0.0.0.255",
"dest-ipv4-address": "192.168.100.0",
"dest-mask": "0.0.0.255"
}
}
]
}
]
}
Here is an example of adding an entry (ACE) and adding another ACL to an existing ACL.
Try adding an entry to the second line of the existing ACL name TEST
. This time, 50
is specified for the sequence number 30
. By the way, if you do not specify the sequence number, you could not set it due to an error.
Using PATCH
, which is a method for merging, the existing settings are left as they are, and the contents in the Body are added.
merge_ace.py
def main():
# url string to issue request
url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip/access-list".format(h=HOST, p=PORT)
# RESTCONF media types for REST API headers
headers = {'Content-Type': 'application/yang-data+json',
'Accept': 'application/yang-data+json'}
# RESTCONF doby for new ACL
body_data = {
"Cisco-IOS-XE-native:access-list": {
"Cisco-IOS-XE-acl:extended": [
{
"name": "TEST",
"access-list-seq-rule": [
{
"sequence": "50",
"ace-rule": {
"action": "permit",
"protocol": "ip",
"ipv4-address": "192.168.5.0",
"mask": "0.0.0.255",
"dest-ipv4-address": "192.168.100.0",
"dest-mask": "0.0.0.255"
}
}
]
}
]
}
}
# this statement performs a PUT on the specified url
response = requests.patch(url, auth=(USER, PASS),
headers=headers, data=json.dumps(body_data), verify=False)
# print the json that is returned
print(response)
Status Code 204 (No Content) is returned.
$ python merge_ace.py
<Response [204]>
Looking at Config, it was added to the second line as expected.
csr1#sh run | begin TEST
ip access-list extended TEST
permit ip 192.168.4.0 0.0.0.255 192.168.100.0 0.0.0.255
permit ip 192.168.5.0 0.0.0.255 192.168.100.0 0.0.0.255
Now let's add another line of ACL name TEST2
.
merge_another_acl.py
def main():
# url string to issue request
url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip/access-list".format(h=HOST, p=PORT)
# RESTCONF media types for REST API headers
headers = {'Content-Type': 'application/yang-data+json',
'Accept': 'application/yang-data+json'}
# RESTCONF doby for new ACL
body_data = {
"Cisco-IOS-XE-native:access-list": {
"Cisco-IOS-XE-acl:extended": [
{
"name": "TEST2",
"access-list-seq-rule": [
{
"sequence": "70",
"ace-rule": {
"action": "permit",
"protocol": "ip",
"ipv4-address": "192.168.7.0",
"mask": "0.0.0.255",
"dest-ipv4-address": "192.168.100.0",
"dest-mask": "0.0.0.255"
}
}
]
}
]
}
}
# this statement performs a PUT on the specified url
response = requests.patch(url, auth=(USER, PASS),
headers=headers, data=json.dumps(body_data), verify=False)
# print the json that is returned
print(response)
$ python merge_another_acl.py
<Response [204]>
You can see that the ACL name TEST2
has been added.
csr1#sh run | begin TEST
ip access-list extended TEST
permit ip 192.168.4.0 0.0.0.255 192.168.100.0 0.0.0.255
permit ip 192.168.5.0 0.0.0.255 192.168.100.0 0.0.0.255
ip access-list extended TEST2
permit ip 192.168.7.0 0.0.0.255 192.168.100.0 0.0.0.255
This time, it is an example of overwriting an existing ACL and creating another ACL name TEST3
. I think this is a case where normal ACL work is basically not performed (if you do it unintentionally, a big accident will occur). However, other than ACL settings, it can be defined as a declarative type based on the parameter sheet, such as "The setting of ... should be XX", so I think there is an advantage that it can be set without the hassle of deleting existing settings. I will.
The method uses PUT
as when creating a new one.
replace_acl.py
def main():
# url string to issue request
url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip/access-list".format(h=HOST, p=PORT)
# RESTCONF media types for REST API headers
headers = {'Content-Type': 'application/yang-data+json',
'Accept': 'application/yang-data+json'}
# RESTCONF doby for new ACL
body_data = {
"Cisco-IOS-XE-native:access-list": {
"Cisco-IOS-XE-acl:extended": [
{
"name": "TEST3",
"access-list-seq-rule": [
{
"sequence": "90",
"ace-rule": {
"action": "permit",
"protocol": "ip",
"ipv4-address": "192.168.9.0",
"mask": "0.0.0.255",
"dest-ipv4-address": "192.168.100.0",
"dest-mask": "0.0.0.255"
}
}
]
}
]
}
}
# this statement performs a PUT on the specified url
response = requests.put(url, auth=(USER, PASS),
headers=headers, data=json.dumps(body_data), verify=False)
# print the json that is returned
print(response)
$ python replace_acl.py
<Response [204]>
It was successfully overwritten.
csr1#sh run | begin TEST
ip access-list extended TEST3
permit ip 192.168.9.0 0.0.0.255 192.168.100.0 0.0.0.255
By the way, according to the person who wrote the following article, in the case of NX-OS, there are cases where the PUT
method does not meet the specifications of RFC8040 and is merged like the PATCH
method.
Exploring IOS-XE and NX-OS based RESTCONF Implementations with YANG and Openconfig
Finally, try deleting the created ACL.
Execute "3. ACL creation" and "5. ACL merge" in advance to overwrite TEST3
and leaveTEST
and TEST2
set.
csr1#sh run | begin TEST
ip access-list extended TEST
permit ip 192.168.4.0 0.0.0.255 192.168.100.0 0.0.0.255
permit ip 192.168.5.0 0.0.0.255 192.168.100.0 0.0.0.255
ip access-list extended TEST2
permit ip 192.168.7.0 0.0.0.255 192.168.100.0 0.0.0.255
Make a request to URL https: // [IP address]: [port number] / restconf / data / Cisco-IOS-XE-native: native / ip / access-list / extended = TEST2
with the DELETE
method. I will do it.
You can delete TEST2
on a per ACL basis by specifying / extended = TEST2
at the end of the URL.
(I didn't know how to delete each entry.)
delete_acl.py
def main():
# url string to issue request
url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip/access-list/extended=TEST2".format(h=HOST, p=PORT)
# RESTCONF media types for REST API headers
headers = {'Content-Type': 'application/yang-data+json',
'Accept': 'application/yang-data+json'}
# this statement performs a DELETE on the specified url
response = requests.delete(url, auth=(USER, PASS),
headers=headers, verify=False)
# print the json that is returned
print(response)
$ python delete_acl.py
<Response [204]>
TEST2
has been deleted.
csr1#sh run | begin TEST
ip access-list extended TEST
permit ip 192.168.4.0 0.0.0.255 192.168.100.0 0.0.0.255
permit ip 192.168.5.0 0.0.0.255 192.168.100.0 0.0.0.255
There weren't many sample codes on the internet yet, so I tried it. In the previous article, ACL Config was generated by combining requirement material and complicated Jinja2 template, but with RESTCONF, parameter information such as requirement material can be set by converting it to JSON format, so it is automated. I think it goes well with.
This time it was a Python example, but I would like to summarize an example using Ansible's restconf_get
and restconf_config
modules and how to check and create a model (URL) for each setting item.
Recommended Posts