After we’ve mastered our access to an ACI fabric via Postman – how about the same thing from a script?
Why a script? Firstly – this will give you a deeper understanding how this is working. And secondly – using a script gives you more control, especially if you are planning to automate things.
Just a simple example – you want to create in an automated way changes into the fabric – in the end unattended and in the background. There a so many tools to support you.
Workflow could be:
- create your demand as a database entry and trigger the build process
- details are being pushed out to a config file
- your delivery environment (e.g. GoCD ) detects this config file and
- launches your script (python, ansible, shell, whatever you prefer)
- changes will be implemented failsafe (in theory 🙂 as we all know – life can be guileful)
The procedure is always the same – send a post request to the API, and check have a look into the results.
Using Curl in a shell script
We’ll start with a simple curl command line – to login into the box.
The POST url we’ve to use is
https://192.168.140.40/api/aaaLogin.json
The payload is
{ "aaaUser" : { "attributes": {"name":"admin","pwd":"totalsecretpassword" } } }
We’ll put this together in a curl command line (–insecure is needed, as we don’t have a valid cert when accessing per IP):
curl -i --insecure -H "content-type:application/json" -XPOST https://192.168.140.40/api/aaaLogin.json -d '{ "aaaUser" : { "attributes": {"name":"admin","pwd":"totalsecretpassword" } } } '
And our APIC answers back (as we’ve seen in the postman article):
HTTP/1.1 200 OK
Server: Cisco APIC
Date: Thu, 02 Jul 2020 14:14:31 GMT
Content-Type: application/json
Content-Length: 1791
Connection: keep-alive
Set-Cookie: APIC-cookie=eyJhbGciOiJSUzI1NiIsImtpZCI6Ijh4MWp4bWN5aTRkamZqYWd2anVxbmxwdjNtZ2EzNDUyIiwidHlwIjoiand0In0.eyJyYmFjIjpbeyJkb21haW4iOiJhbGwiLCJyb2xlc1IiOjAsInJvbGVzVyI6MX1dLCJpc3MiOiJBQ0kgQVBJQyIsInVzZXJuYW1lIjoiYWRtaW4iLCJ1c2VyaWQiOjE1Mzc0LCJ1c2VyZmxhZ3MiOjAsImlhdCI6MTU5MzY5OTI3MSwiZXhwIjoxNTkzNjk5ODcxLCJzZXNzaW9uaWQiOiJsdVdtdUNBTFFWcStaQzJhckdNRFRBPT0ifQ.kWQcxvw-9cIMP1XAZsTvBs4rtkXWevC2ZC1ONcjsJefMpYWbP8PgiYW6DpW-QLgGDdc8TDL1xws1nG0jPqKneppRgklcI9BygTm3IywkyaWvIEYZMOs0uvWyT8GCFKCPyaqQwAE_m725PLECFIPk4RVfG0dyDi4qca08AtjzGaHhvhp3WsX55XrcBtxA_1c9ZgcHDjhK1NNbOm3HASHa5sZNcpMPogd0ya-vpUrePlw5yrP559gHy2gUhLybANcIkFBCptCUj8X7PETTJi9DEpcPm5mUZTSUw3bcQtHc_rKRA3lTGJWbrOuZTQRCreb7ygaES7SKXYp-3hWAem5FjQ; path=/; HttpOnly; HttpOnly; Secure
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, DevCookie, APIC-challenge, Request-Tag
Access-Control-Allow-Methods: POST,GET,OPTIONS,DELETE
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000; includeSubdomains
Cache-Control: no-cache="Set-Cookie, Set-Cookie2"
Client-Cert-Enabled: false
Access-Control-Allow-Origin: http://127.0.0.1:8000
Access-Control-Allow-Credentials: false
{"totalCount":"1","imdata":[{"aaaLogin":{"attributes":{"token":"eyJhbGciOiJSUzI1NiIsImtpZCI6Ijh4MWp4bWN5aTRkamZqYWd2anVxbmxwdjNtZ2EzNDUyIiwidHlwIjoiand0In0.eyJyYmFjIjpbeyJkb21haW4iOiJhbGwiLCJyb2xlc1IiOjAsInJvbGVzVyI6MX1dLCJpc3MiOiJBQ0kgQVBJQyIsInVzZXJuYW1lIjoiYWRtaW4iLCJ1c2VyaWQiOjE1Mzc0LCJ1c2VyZmxhZ3MiOjAsImlhdCI6MTU5MzY5OTI3MSwiZXhwIjoxNTkzNjk5ODcxLCJzZXNzaW9uaWQiOiJsdVdtdUNBTFFWcStaQzJhckdNRFRBPT0ifQ.kWQcxvw-9cIMP1XAZsTvBs4rtkXWevC2ZC1ONcjsJefMpYWbP8PgiYW6DpW-QLgGDdc8TDL1xws1nG0jPqKneppRgklcI9BygTm3IywkyaWvIEYZMOs0uvWyT8GCFKCPyaqQwAE_m725PLECFIPk4RVfG0dyDi4qca08AtjzGaHhvhp3WsX55XrcBtxA_1c9ZgcHDjhK1NNbOm3HASHa5sZNcpMPogd0ya-vpUrePlw5yrP559gHy2gUhLybANcIkFBCptCUj8X7PETTJi9DEpcPm5mUZTSUw3bcQtHc_rKRA3lTGJWbrOuZTQRCreb7ygaES7SKXYp-3hWAem5FjQ","siteFingerprint":"8x1jxmcyi4djfjagvjuqnlpv3mga3452","refreshTimeoutSeconds":"600","maximumLifetimeSeconds":"86400","guiIdleTimeoutSeconds":"1200","restTimeoutSeconds":"90","creationTime":"1593699271","firstLoginTime":"1593699271","userName":"admin","remoteUser":"false","unixUserId":"15374","sessionId":"luWmuCALQVq+ZC2arGMDTA==","lastName":"","firstName":"","changePassword":"no","version":"5.0(1k)","buildTime":"Wed May 13 23:24:01 PDT 2020","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-common","readPrivileges":"admin","writePrivileges":"admin"}}},{"DnDomainMapEntry":{"attributes":{"dn":"uni/tn-mgmt","readPrivileges":"admin","writePrivileges":"admin"}}},{"DnDomainMapEntry":{"attributes":{"dn":"uni/tn-infra","readPrivileges":"admin","writePrivileges":"admin"}}}]}}]}
As this works – we’ll put this into a bash script.
#!/bin/bash
#
# Script to login to APIC via curl
#
# Credentials
user=admin
pwd=donttell
# Curl Post
curl -i --insecure -H "content-type:application/json" -XPOST https://192.168.140.40/api/aaaLogin.json -d '{ "aaaUser" : { "attributes": {"name":"'$user'","pwd":"'$pwd'" } } } '
printf "\n\n Done\n\n"
# End of script
Works as designed – nice.
In the end – we’ll follow the same approach as we did with Postman.
The only difference – we’ve to store the session cookie we’ll get after the login (named APIC-cookie) and hand it over to the curl command to create the test tenant.
#!/bin/bash
#
# Script to create a tenant via curl
#
# Credentials
user=admin
pwd=sosecretsosecret
# Curl Post to get logged in
# Cookie is being stored in file named "Cookie"
curl -i --insecure -H "content-type:application/json" -XPOST https://192.168.140.40/api/aaaLogin.json -d '{ "aaaUser" : { "attributes": {"name":"'$user'","pwd":"'$pwd'" } } } ' -c Cookie
printf "\n\n Logged in\n\n"
printf "\n\n Create tenant\n\n"
# Curl Post to create the tenant
# Cookie being read in
curl -i --insecure -H "content-type:application/json" -XPOST https://192.168.140.40/api/node/mo/uni/tn-Beaker.json -d '{"fvTenant":{"attributes":{"dn":"uni/tn-Beaker","name":"Beaker","nameAlias":"TheLabGuy","descr":"A simple test tenant with the magic bash script","rn":"tn-Beaker","status":"created"},"children":[]}}' -b Cookie
printf "\n\n Done\n\n"
# End of script
Run the script and have a look on your APIC console.
The most obvious advantage of using a script instead of Postman – you don’t need to install a huge software package (OSX version has a 350 MB size), much leaner.
On the other hand – you’ve got much more options at hand by using Postman, so as always – you’ve to decide which approach fits best. In most cases – both – depends on your use case.
JSON PP (Pretty Printer)
If you modify the curl call a little bit, you are able to use tools like json_pp (man json_pp).
curl --insecure -H "content-type:application/json" -XPOST https://192.168.140.40/api/aaaLogin.json -d '{ "aaaUser" : { "attributes": {"name":"'$user'","pwd":"'$pwd'" } } } ' | json_pp
This will pipe the output into json_pp and the answer is much easier to read.
{
"totalCount" : "1",
"imdata" : [
{
"aaaLogin" : {
"children" : [
{
"aaaUserDomain" : {
"children" : [
{
"aaaReadRoles" : {
"attributes" : {}
}
},
{
"aaaWriteRoles" : {
"children" : [
{
"role" : {
"attributes" : {
"name" : "admin"
}
}
}
],
"attributes" : {}
}
}
],
"attributes" : {
"rolesW" : "admin",
"name" : "all",
"rolesR" : "admin"
}
}
},
{
"DnDomainMapEntry" : {
"attributes" : {
"writePrivileges" : "admin",
"readPrivileges" : "admin",
"dn" : "uni/tn-common"
}
}
},
{
"DnDomainMapEntry" : {
"attributes" : {
"writePrivileges" : "admin",
"readPrivileges" : "admin",
"dn" : "uni/tn-mgmt"
}
}
},
{
"DnDomainMapEntry" : {
"attributes" : {
"writePrivileges" : "admin",
"readPrivileges" : "admin",
"dn" : "uni/tn-infra"
}
}
}
],
"attributes" : {
"node" : "topology/pod-1/node-1",
"remoteUser" : "false",
"sessionId" : "TL+Yj5bNS1+5TI8rJBGkEQ==",
"siteFingerprint" : "8x1jxmcyi4djfjagvjuqnlpv3mga3452",
"lastName" : "",
"userName" : "admin",
"token" : "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijh4MWp4bWN5aTRkamZqYWd2anVxbmxwdjNtZ2EzNDUyIiwidHlwIjoiand0In0.eyJyYmFjIjpbeyJkb21haW4iOiJhbGwiLCJyb2xlc1IiOjAsInJvbGVzVyI6MX1dLCJpc3MiOiJBQ0kgQVBJQyIsInVzZXJuYW1lIjoiYWRtaW4iLCJ1c2VyaWQiOjE1Mzc0LCJ1c2VyZmxhZ3MiOjAsImlhdCI6MTU5MzcwNTE0OSwiZXhwIjoxNTkzNzA1NzQ5LCJzZXNzaW9uaWQiOiJUTCtZajViTlMxKzVUSThySkJHa0VRPT0ifQ.Fj4o4jwj2VT2KDmfdSQyrn2EkSSW8MRtjvdEUoHt4-_Bv4RKB_uVRsjaJuBo_e-TQm_dfAc07Hm46iUcqNLon7rbhyb9EqidXY855ZxbL9wJBUvLjKf4hclg3kkw_ctcGcgG6OpZNRX1oKd5NKwlDlffkqHmDB5dFWmjdTxbznyyqHwnuSzxvnRSzYeKU9Q8jVYMPfOnApcyCwyOfdwCLUktGkU0Yv1kpYWrm58gbIKiNt8O5IAHtFNTh58SA538soaHr2y-LFqaDeKrW0KubRyjPp670ocTk-Vc2L7Zh1Qz-jCcAsAyfpnaJvYza5y1FEOR_aiK1ub9AY9-Qrd4qg",
"firstLoginTime" : "1593705149",
"firstName" : "",
"restTimeoutSeconds" : "90",
"version" : "5.0(1k)",
"creationTime" : "1593705149",
"buildTime" : "Wed May 13 23:24:01 PDT 2020",
"changePassword" : "no",
"refreshTimeoutSeconds" : "600",
"maximumLifetimeSeconds" : "86400",
"unixUserId" : "15374",
"guiIdleTimeoutSeconds" : "1200"
}
}
}
]
}
Accessing ACI via a python script
As all roads lead to Rome – it is possible to use this approach with any tool you prefer. As a brief example – how to do this in python.
Python will manage this by using the json module, which has to be imported.
#!/usr/bin/env python
#
# Python-Example Script to configure ACI
#
# A. Fassl - 07/2020
# Load required modules
import json
import requests
import os
# Prepare
print requests.certs.where()
# The API URL of the apic - via letsencrypt-delivered https
base_url = 'https://acisim.progis.net:8022/api/'
# Access Credentials
name_and_pwd = {'aaaUser': {'attributes': {'name': 'admin', 'pwd': 'PwD2020xpx'}}}
json_credentials = json.dumps(name_and_pwd)
# login via the API
login_url = base_url + 'aaaLogin.json'
post_response = requests.post(login_url, data=json_credentials, verify=False)
# get token from login response structure
auth = json.loads(post_response.text)
print auth
login_attributes = auth['imdata'][0]['aaaLogin']['attributes']
auth_token = login_attributes['token']
# create cookie array from token
cookies = {}
cookies['APIC-Cookie'] = auth_token
print cookies
And the output delivers the expected results (I haven’t taken care of the SSL cert verification – so please ignore the warning):
# ./login.py
/etc/pki/tls/certs/ca-bundle.crt
/usr/lib/python2.7/site-packages/urllib3/connectionpool.py:769: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.org/en/latest/security.html
InsecureRequestWarning)
{u'imdata': [{u'aaaLogin': {u'attributes': {u'userName': u'admin', u'maximumLifetimeSeconds': u'86400', u'refreshTimeoutSeconds': u'600', u'firstName': u'', u'remoteUser': u'false', u'buildTime': u'Wed May 13 23:24:01 PDT 2020', u'creationTime': u'1594019696', u'sessionId': u'hhoET4O5S0er+ANqeI6FGw==', u'node': u'topology/pod-1/node-1', u'siteFingerprint': u'8x1jxmcyi4djfjagvjuqnlpv3mga3452', u'token': u'eyJhbGciOiJSUzI1NiIsImtpZCI6Ijh4MWp4bWN5aTRkamZqYWd2anVxbmxwdjNtZ2EzNDUyIiwidHlwIjoiand0In0.eyJyYmFjIjpbeyJkb21haW4iOiJhbGwiLCJyb2xlc1IiOjAsInJvbGVzVyI6MX1dLCJpc3MiOiJBQ0kgQVBJQyIsInVzZXJuYW1lIjoiYWRtaW4iLCJ1c2VyaWQiOjE1Mzc0LCJ1c2VyZmxhZ3MiOjAsImlhdCI6MTU5NDAxOTY5NiwiZXhwIjoxNTk0MDIwMjk2LCJzZXNzaW9uaWQiOiJoaG9FVDRPNVMwZXIrQU5xZUk2Rkd3PT0ifQ.irQ52pJMojphV8RrFQ231mwxsBx1_myQmb0kF3G7nIIGgDsU38uiUDydXa_N7dzLKVZb3eOwgZT6mhBDiyMS_iDjBmdimJMU6kRQ191eGeor4WSFX1UdcvHwH_X-BJMebGjWDM2c5tDampke7Ggf5Cr17bvM6NEtWfO_F82QvTq0Whe-FJlkxUXP8wdx1dSBgVVZXGX7c-u3WsaI6SaqhztlaW5mm0DfDYPd2u98xvHM4twJwLhgcyG8vbbu_y88d4O_9RcI-IV1mmXfWHamHzgMfU2vqetzgBWtlF8OUsy-Y-Sk9b2pa7hU23dMSvyXcNiuMDO7dZZwV3CXyMzKJQ', u'version': u'5.0(1k)', u'restTimeoutSeconds': u'90', u'changePassword': u'no', u'lastName': u'', u'firstLoginTime': u'1594019696', u'unixUserId': u'15374', u'guiIdleTimeoutSeconds': u'1200'}, u'children': [{u'aaaUserDomain': {u'attributes': {u'rolesW': u'admin', u'name': u'all', u'rolesR': u'admin'}, u'children': [{u'aaaReadRoles': {u'attributes': {}}}, {u'aaaWriteRoles': {u'attributes': {}, u'children': [{u'role': {u'attributes': {u'name': u'admin'}}}]}}]}}, {u'DnDomainMapEntry': {u'attributes': {u'dn': u'uni/tn-common', u'readPrivileges': u'admin', u'writePrivileges': u'admin'}}}, {u'DnDomainMapEntry': {u'attributes': {u'dn': u'uni/tn-mgmt', u'readPrivileges': u'admin', u'writePrivileges': u'admin'}}}, {u'DnDomainMapEntry': {u'attributes': {u'dn': u'uni/tn-infra', u'readPrivileges': u'admin', u'writePrivileges': u'admin'}}}]}}], u'totalCount': u'1'}
{'APIC-Cookie': u'eyJhbGciOiJSUzI1NiIsImtpZCI6Ijh4MWp4bWN5aTRkamZqYWd2anVxbmxwdjNtZ2EzNDUyIiwidHlwIjoiand0In0.eyJyYmFjIjpbeyJkb21haW4iOiJhbGwiLCJyb2xlc1IiOjAsInJvbGVzVyI6MX1dLCJpc3MiOiJBQ0kgQVBJQyIsInVzZXJuYW1lIjoiYWRtaW4iLCJ1c2VyaWQiOjE1Mzc0LCJ1c2VyZmxhZ3MiOjAsImlhdCI6MTU5NDAxOTY5NiwiZXhwIjoxNTk0MDIwMjk2LCJzZXNzaW9uaWQiOiJoaG9FVDRPNVMwZXIrQU5xZUk2Rkd3PT0ifQ.irQ52pJMojphV8RrFQ231mwxsBx1_myQmb0kF3G7nIIGgDsU38uiUDydXa_N7dzLKVZb3eOwgZT6mhBDiyMS_iDjBmdimJMU6kRQ191eGeor4WSFX1UdcvHwH_X-BJMebGjWDM2c5tDampke7Ggf5Cr17bvM6NEtWfO_F82QvTq0Whe-FJlkxUXP8wdx1dSBgVVZXGX7c-u3WsaI6SaqhztlaW5mm0DfDYPd2u98xvHM4twJwLhgcyG8vbbu_y88d4O_9RcI-IV1mmXfWHamHzgMfU2vqetzgBWtlF8OUsy-Y-Sk9b2pa7hU23dMSvyXcNiuMDO7dZZwV3CXyMzKJQ'}