Some instructions on how to get started with interfacing Python with Cisco APIC REST API.
I found this blog post by Wim Wauters particularly useful, and I have used much of his material in this post:
https://blog.wimwauters.com/networkprogrammability/2020-03-19-aci_python_requests/
Initial setup
If you have not yet done so, download and install Python.
https://www.python.org/downloads/
Example 1: login to the APIC Sandbox controller
Details of how to set up the REST API required to log in to the APIC controller can be found on this blog post, including how to obtain the login username/password credentials required (described in the Initial Setup section).
For convenience, I reproduce the REST API here (the password is subject to change, so login to the sandbox URL to get the most up-to-date credentials)
POST https://sandboxapicdc.cisco.com/api/aaaLogin.json
{ "aaaUser":{ "attributes":{ "name":"admin", "pwd":"!v3G@!4@Y" } } }
login.py
import requests import json def get_token(): url = "https://sandboxapicdc.cisco.com/api/aaaLogin.json" payload = { "aaaUser": { "attributes": { "name":"admin", "pwd":"!v3G@!4@Y" } } } headers = { "Content-Type" : "application/json" } requests.packages.urllib3.disable_warnings() response = requests.post(url,data=json.dumps(payload), headers=headers, verify=False).json() token = response['imdata'][0]['aaaLogin']['attributes']['token'] return token def main(): token = get_token() print("The token is: " + token) if __name__ == "__main__": main()
To run the python code example, open up a command prompt once you have successfully installed Python, navigate the location of the python file and run the file:
If you are new to installing Python you may get an error complaining about ‘requests’ module not being installed.
If this is the case just run the following command at the command prompt:
pip install requests
When successful, the Python program will print the token that is returned in the JSON body of the response.
We use this token for any subsequent REST calls that require it.
Example 2: Retrieve all ACI tenants
The REST API to retrieve the sandbox APIC tenants is reproduced here
GET https://sandboxapicdc.cisco.com/api/class/fvTenant.json
The following code retrieves all the tenants configured on our APIC sandbox.
tenants.py
import requests import json from login import get_token def get_tenants(): token = get_token() url = "https://sandboxapicdc.cisco.com/api/class/fvTenant.json" headers = { "Cookie" : f"APIC-Cookie={token}", } requests.packages.urllib3.disable_warnings() response = requests.get(url, headers=headers, verify=False) return response if __name__ == "__main__": response = get_tenants().json() tenants = response['imdata'] for tenant in tenants: print(f"Tenant name: {tenant['fvTenant']['attributes']['name']}")
Specifically the print statement prints all references to the fvTenant > attributes > name objects within the json response, thereby giving us the list of tenants by name:
Example 3: Creating an ACI tenant
To create a Tenant, use the existing REST API documentation:
POST https://sandboxapicdc.cisco.com/api/mo/uni.json
{ "fvTenant" : { "attributes" : { "name":"LABEVERYDAY_Tenant", "descr":"Newly created tenant", "nameAlias":"Andy" } } }
createTenant.py
In this code we need to retrieve the token so we use the existing get_token() function. Note that we imported that function in the third line of the script. According to the documentation, we need to pass that token in a Cookie called APIC-Cookie.
import requests import json from login import get_token tenant_name = "Tenant_Python" def create_tenant(): token = get_token() url = "https://sandboxapicdc.cisco.com/api/mo/uni.json" payload = { "fvTenant": { "attributes": { "name": tenant_name } } } headers = { "Cookie" : f"APIC-Cookie={token}", } requests.packages.urllib3.disable_warnings() response = requests.post(url,data=json.dumps(payload), headers=headers, verify=False) if (response.status_code == 200): print("Successfully created tenant") else: print("Issue with creating tenant") def get_tenant(): return tenant_name if __name__ == "__main__": create_tenant()
And on running this in command line see that the tenant is successfully added:
So that when we list the set of tenant again using tenants.py the new tenant gets added a shown:
Example 4: Deleting an ACI tenant
And here is some python script to remove a tenant.
deleteTenant.py
import requests import json from login import get_token def delete_tenant(): token = get_token() url = "https://sandboxapicdc.cisco.com/api/mo/uni.json" payload = { "fvTenant": { "attributes": { "name": "Tenant_Python", "status": "deleted" } } } headers = { "Cookie" : f"APIC-Cookie={token}", } requests.packages.urllib3.disable_warnings() response = requests.post(url,data=json.dumps(payload), headers=headers, verify=False) if (response.status_code == 200): print("Successfully deleted tenant") else: print("Issue with deleting tenant") if __name__ == "__main__": delete_tenant()
And when running the python script the tenant is deleted:
******************************************************************************************************************
Example 4: Cisco ACI: Authenticate, create a new domain and a new user
securityDomain.py
import requests import json APIC_HOST = "https://sandboxapicdc.cisco.com" APIC_USERNAME = "admin" APIC_PASSWORD = "!v3G@!4@Y" def post_request(apic, cookies, uri, payload): url = apic + uri print("\n-----------------------------------------------------------------") print("\nExecuting API Call: POST") print("\nURL: {}".format(url)) print("\nBODY: {}".format(payload)) req = requests.post(url, cookies=cookies, data=payload, verify=False) print("\nSTATUS CODE: {}".format(req.status_code)) print("\nRESPONSE: {}".format(req.text)) return req def get_cookies(apic): uri = "/api/aaaLogin.json" credentials = { "aaaUser": {"attributes": {"name": APIC_USERNAME, "pwd": APIC_PASSWORD}} } authenticate = post_request( apic=apic, cookies={}, uri=uri, payload=json.dumps(credentials) ) if not authenticate.ok: print("\n[ERROR] Authentication failed! APIC responded with:") print(json.dumps(json.loads(authenticate.text), indent=4)) exit() print("\n[OK] Authentication successful!") return authenticate.cookies def main(): cookies = get_cookies(APIC_HOST) # Create new security domain secdom = { "aaaDomain": { # "attributes": {"name": "SECDOM-PYTHON", "descr": "Python Managed Tenants"} } } path = "/api/mo/uni/userext/domain-SECDOM-PYTHON.json" rsp = post_request(APIC_HOST, cookies, path, json.dumps(secdom)) # Create new user for security domain user = { "aaaUser": { "attributes": {"name": "python", "pwd": "somePassword"}, "children": [ { "aaaUserDomain": { "attributes": {"name": "all"}, "children": [ { "aaaUserRole": { "attributes": { "name": "read-all", "privType": "readPriv", } } } ], } }, { "aaaUserDomain": { "attributes": {"name": "common"}, "children": [ { "aaaUserRole": { "attributes": { "name": "read-all", "privType": "readPriv", } } } ], } }, { "aaaUserDomain": { "attributes": {"name": "SECDOM-PYTHON"}, "children": [ { "aaaUserRole": { "attributes": { "name": "tenant-ext-admin", "privType": "writePriv", } } } ], } }, ], } } path = "/api/mo/uni/userext/user-python.json" rsp = post_request(APIC_HOST, cookies, path, json.dumps(user)) if __name__ == "__main__": main()
Output:
—————————————————————–
Executing API Call: POST
URL: https://sandboxapicdc.cisco.com/api/aaaLogin.json
BODY: {"aaaUser": {"attributes": {"name": "admin", "pwd": "!v3G@!4@Y"}}}
C:\Python\lib\site-packages\urllib3\connectionpool.py:1043: InsecureRequestWarning: Unverified HTTPS request is being made to host ‘sandboxapicdc.cisco.com’. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
warnings.warn(
STATUS CODE: 200
RESPONSE: {"totalCount":"1","imdata":[{"aaaLogin":{"attributes":{"token":"eyJhbGciOiJSUzI1NiIsImtpZCI6InZ5bXo5YWR1ZnFndnE1aXN1ZnQ4bDZzYnpwbXQ4NWV5IiwidHlwIjoiand0In0.eyJyYmFjIjpbeyJkb21haW4iOiJhbGwiLCJyb2xlc1IiOjAsInJvbGVzVyI6MX1dLCJpc3MiOiJBQ0kgQVBJQyIsInVzZXJuYW1lIjoiYWRtaW4iLCJ1c2VyaWQiOjE1Mzc0LCJ1c2VyZmxhZ3MiOjAsImlhdCI6MTY0OTM0MTk0NywiZXhwIjoxNjQ5MzQyNTQ3LCJzZXNzaW9uaWQiOiJVYnRmaW5jTlJqT2xwVlE0SXJXMjNnPT0ifQ.HybGBljEXdLxWIUp9qLaZo6_QQipE07PsuBUhDFVimtbp3YSZMRvHpHA_WhkeazS91zrO-nF8XgMaYta4PYflh3vxuwQgNVYMC_qnBK5w6kFHZ7MJD2qH-9sXKXxYKum3zAfuP4OqcxhOUj2DmFp4rCEQhCPQv5JJFwhsWWgwDeRryWaqp2pjkpK_UEq12UccBFHCcv69-fANJHJNdqNwkiujxy4VirUIqqWsjSZ7crs63Ph23XoCMMKc3na7Pfw7QPwn2I5x_d54M0tO7wxs3OYhDuANm_EVNhDu8UPKIFyaVwYj8CWFOVYdt7455jURXdfTLM2_y-Kf4f6PhIVEg","siteFingerprint":"vymz9adufqgvq5isuft8l6sbzpmt85ey","refreshTimeoutSeconds":"600","maximumLifetimeSeconds":"86400","guiIdleTimeoutSeconds":"1200","restTimeoutSeconds":"90","creationTime":"1649341947","firstLoginTime":"1649341947","userName":"admin","remoteUser":"false","unixUserId":"15374","sessionId":"UbtfincNRjOlpVQ4IrW23g==","lastName":"","firstName":"","changePassword":"no","version":"5.2(1g)","buildTime":"Wed Jul 28 23:09:38 UTC 2021","node":"topology/pod-1/node-1"},"children":[{"aaaUserDomain":{"attributes":{"name":"all","rolesR":"admin","rolesW":"admin"},"children":[{"aaaReadRoles":{"attributes":{}}},{"aaaWriteRoles":{"attributes":{},"children":[{"role":{"attributes":{"name":"admin"}}}]}}]}},{"DnDomainMapEntry":{"attributes":{"dn":"uni/tn-mgmt","readPrivileges":"admin","writePrivileges":"admin"}}},{"DnDomainMapEntry":{"attributes":{"dn":"uni/tn-infra","readPrivileges":"admin","writePrivileges":"admin"}}},{"DnDomainMapEntry":{"attributes":{"dn":"uni/tn-common","readPrivileges":"admin","writePrivileges":"admin"}}}]}}]}
[OK] Authentication successful!
—————————————————————–
Executing API Call: POST
URL: https://sandboxapicdc.cisco.com/api/mo/uni/userext/domain-SECDOM-PYTHON.json
BODY: {"aaaDomain": {}}
C:\Python\lib\site-packages\urllib3\connectionpool.py:1043: InsecureRequestWarning: Unverified HTTPS request is being made to host ‘sandboxapicdc.cisco.com’. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
warnings.warn(
STATUS CODE: 200
RESPONSE: {"totalCount":"0","imdata":[]}
—————————————————————–
Executing API Call: POST
URL: https://sandboxapicdc.cisco.com/api/mo/uni/userext/user-python.json
BODY: {"aaaUser": {"attributes": {"name": "python", "pwd": "somePassword"}, "children": [{"aaaUserDomain": {"attributes": {"name": "all"}, "children": [{"aaaUserRole": {"attributes": {"name": "read-all", "privType": "readPriv"}}}]}}, {"aaaUserDomain": {"attributes": {"name": "common"}, "children": [{"aaaUserRole": {"attributes": {"name": "read-all", "privType": "readPriv"}}}]}}, {"aaaUserDomain": {"attributes": {"name": "SECDOM-PYTHON"}, "children": [{"aaaUserRole": {"attributes": {"name": "tenant-ext-admin", "privType": "writePriv"}}}]}}]}}
C:\Python\lib\site-packages\urllib3\connectionpool.py:1043: InsecureRequestWarning: Unverified HTTPS request is being made to host ‘sandboxapicdc.cisco.com’. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
warnings.warn(
STATUS CODE: 200
RESPONSE: {"totalCount":"0","imdata":[]}
Navigate to the APIC sandbox to verify that the security domain has been created:
And also that the user “python” has been created: