If any of us have had a chance to work with ACI, the graphical interface is very powerful, but like all GUIs, it is bloated, heavy and cumbersome. Partly also because of the monstrous complexity, configurability and flexibility of the product.

For many of us, the CLI, or command line, has always been the direct, fast and convenient tool that provides all the functionality we need (sometimes even more). Even with this, if the environment is large, with many tenants, EPGs, bridge domains, etc, even the command line has a hard time moving smoothly.

Then we grab to the hybrid solution, which also saves us to do cli-scrapping and parsing of the outputs that we receive from it. It is a matter of using the API provided by the product, together with the traditional command line tools.

On the one hand then we will have the API of ACI, and on the other hand the enormous power of the cURL command.

Of course to use it we will need to authenticate against ACI, from that authentication will emerge a session cookie, which will allow us to perform operations and subsequent queries without having to resend the credentials in each query.

curl -X POST -k https://<fqdn|ip>/api/aaaLogin.json -d '{ "aaaUser" : { "attributes" : { "name" : "admin", "pwd" : "password" } } }' -c /tmp/cookie.txt

This cookie is the one we will send for all other queries and operations, as long as it is valid.

List all tenants

curl -s -b /tmp/cookie.txt -X GET -k 'https://<fqdn|ip>/api/class/fvTenant.json'

This returns the list in JSON format that we can filter as we wish.

Endpoints available for the API

  • fvTenant. Tenant.
  • fvAp. Application Profile.
  • fvAEPg. Endpoint Group.
  • fvDB. Bridge Domain.
  • fvCEp. Endpoint.

Therefore, any of these, substituted in <endpoint> within the following URL format, will return an unfiltered listing of the objects we have chosen

curl -s -b /tmp/cookie.txt -X GET -k 'https://<fqdn|ip>/api/class/<endpoint>.json'

An example of output with a listing (cut and sanitized), for example from Application Profiles would be:

{
  "totalCount": "117",
  "imdata": [
    {
      "fvAp": {
        "attributes": {
          "annotation": "",
          "childAction": "",
          "descr": "",
          "dn": "uni/tn-XXXX/ap-XXXX-XXXX-XXXX",
          "extMngdBy": "",
          "lcOwn": "local",
          "modTs": "2021-12-21T16:41:36.292+02:00",
          "monPolDn": "uni/tn-common/monepg-default",
          "name": "XXXX-XXXX-XXXX",
          "nameAlias": "",
          "ownerKey": "",
          "ownerTag": "",
          "prio": "unspecified",
          "status": "",
          "uid": "13678"
        }
      }
    },
    {
      "fvAp": {
        "attributes": {
          "annotation": "",
          "childAction": "",
          "descr": "",
          "dn": "uni/tn-XXXX/ap-XXXX-XXXX",
          "extMngdBy": "",
          "lcOwn": "local",
          "modTs": "2021-12-21T16:42:06.655+02:00",
          "monPolDn": "uni/tn-common/monepg-default",
          "name": "XXXX-XXXX",
          "nameAlias": "",
          "ownerKey": "",
          "ownerTag": "",
          "prio": "unspecified",
          "status": "",
          "uid": "13678"
        }
      }
    },
    {
      "fvAp": {
        "attributes": {
          "annotation": "",
          "childAction": "",
          "descr": "",
          "dn": "uni/tn-XXXX/ap-XXXX-XXXX",
          "extMngdBy": "",
          "lcOwn": "local",
          "modTs": "2021-12-21T16:42:38.450+02:00",
          "monPolDn": "uni/tn-common/monepg-default",
          "name": "XXXX-XXXX",
          "nameAlias": "",
          "ownerKey": "",
          "ownerTag": "",
          "prio": "unspecified",
          "status": "",
          "uid": "13678"
        }
      }
    },
    {
    ...
  ...
...

The output is a JSON string. To get the nice version, I used the jq command.

Filters for endpoints

The above queries can be filtered, so that it is not necessary to get the complete listing to filter or select after the fact, but the query already comes back as clean as possible. Some filter I have used:

  • ?query-target-filter=eq(fvAp.name, "<application profile name>"). In this case it will return an exact match for the name in<application profile name>.
  • ?query-target-filter=wcard(fvAp.dn, "<filter criteria>"). In this case it will return all the matches that in the result obtained its dn contains the string in <filter criteria>.

They can also be combined to create more elaborate queries, as well:

  • ?query-target-filter=and(eq(fvAp.name, "<application profile name>"),eq(fvAp.dn, "<filter criteria>"). It will cause the two eq conditions to be satisfied simultaneously.
  • ?query-target-filter=or(eq(fvAp.name, "<application profile name>"),eq(fvAp.dn, "<filter criteria>"). In this case either of the two conditions is valid for the answer.