Manipulating Cisco IOS-XE ACLs with RESTCONF (Python)

Introduction

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.

1. Overview of RESTCONF

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

2. Prepared environment

[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".

3. Create ACL

First, add one line of ACL name TEST from the state where there is no ACL setting.

3-1. Python code

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()

3-2. Execution result

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

4. Get ACL

Next, try to get the information of the created ACL with RESTCONF.

4-1. Python code (1) Get IP settings

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)

4-2. Execution result (1) IP setting acquisition

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
    }
  }
}

4-3. Python code (2) Get extended ACL settings

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)

4-4. Execution result (2) Acquisition of extended ACL settings

$ 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"
          }
        }
      ]
    }
  ]
}

5. ACL merge

Here is an example of adding an entry (ACE) and adding another ACL to an existing ACL.

5-1. Python code (1) Add entry

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)

5-2. Execution result (1) Addition of entry

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

5-3. Python code (2) Add another ACL

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)

5-4. Execution result (2) Addition of another ACL

$ 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

6. ACL replacement

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.

6-1. Python code

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)

6-2. Output result

$ 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

7. Delete ACL

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

7-1. Python code

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)

7-2. Execution result

$ 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

Finally

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

Manipulating Cisco IOS-XE ACLs with RESTCONF (Python)
Manipulating EAGLE .brd files with Python
Python and Bash on Cisco Catalyst IOS-XE
Device monitoring with On-box Python in IOS-XE
First YDK to try with Cisco IOS-XE
FizzBuzz with Python3
Scraping with Python
Statistics with python
Scraping with Python
Twilio with Python
Integrate with Python
Play with 2016-Python
AES256 with python
Tested with Python
python starts with ()
with syntax (Python)
Bingo with python
Zundokokiyoshi with python
Excel with Python
Microcomputer with Python
Cast with python
Serial communication with Python
Django 1.11 started with Python3.6
Primality test with Python
Python with eclipse + PyDev.
Socket communication with Python
Data analysis with python 2
Scraping with Python (preparation)
Try scraping with Python.
Learning Python with ChemTHEATER 03
"Object-oriented" learning with python
Run Python with VBA
Handling yaml with python
Solve AtCoder 167 with python
Serial communication with python
[Python] Use JSON with Python
Learning Python with ChemTHEATER 05-1
Learn Python with ChemTHEATER
Run prepDE.py with python3
1.1 Getting Started with Python
Collecting tweets with Python
Binarization with OpenCV / Python
3. 3. AI programming with Python
Kernel Method with Python
Non-blocking with Python + uWSGI
Scraping with Python + PhantomJS
Posting tweets with python
Drive WebDriver with python
Use mecab with Python3
[Python] Redirect with CGIHTTPServer
Voice analysis with python
Think yaml with python
Getting Started with Python
Use DynamoDB with Python
Zundko getter with python
Handle Excel with python
Ohm's Law with Python
Primality test with python
Run Blender with python
Solve Sudoku with Python
Python starting with Windows 7