Si alguno hemos tenido oportunidad de trabajar con ACI, la interfaz gráfica es muy potente, pero como todas las GUIs, está muy inflada, es pesada y engorrosa. En parte también por la monstruosa complejidad, capacidad de configuración y flexibilidad del producto.

Para muchos de nosotros, la CLI, o línea de comandos siempre ha sido la herramienta directa, rápida y cómoda que nos proporciona toda la funcionalidad que necesitamos (en ocasiones incluso más). Aún con esto, si el entorno es grande, con muchos tenant, EPGs, bridge domains, etc, incluso a la línea de comandos le cuesta moverse con fluidez.

Entonces nos agarramos a la solución híbrida, que además nos ahorra hacer cli-scrapping y parsing de las salidas que recibimos de ella. Se trata de usar la API que nos proporciona el producto, junto a las herramientas de toda la vida en línea de comandos.

Por un lado entonces tendremos la API de ACI, y por otro la potencia descomunal del comando cURL.

Por supuesto para utilizarla necesitaremos autenticarnos contra ACI, de esa autenticación surgirá una cookie de sesión, que nos permitirá realizar operaciones y consultas posteriores sin tener que reenviar las credenciales en cada consulta.

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

Esta cookie es la que mandaremos para todas las demás consultas y operaciones, mientras sea válida.

Listar todos los tenant

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

Lo que nos devuelve el listado en formato JSON que podremos filtrar a nuestro antojo.

Endpoints disponibles para la API

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

Por tanto, cualquiera de estos, sustituido en <endpoint> dentro del formato de URL siguiente, nos devolverá un listado sin filtrar de los objetos que hayamos elegido

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

Un ejemplo de salida con un listado (cortada y saneada), por ejemplo de Application Profiles sería:

{
  "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"
        }
      }
    },
    {
    ...
  ...
...

La salida es un JSON string. Para conseguir la versión bonita, he usado el comando jq.

Filtros para endpoints

Las consultas anteriores pueden filtrarse, de forma que no sea necesario obtener el listado completo para filtrar o seleccionar a posteriori, sino que la consulta ya vuelva lo más limpia posible. Algún filtro que he usado:

  • ?query-target-filter=eq(fvAp.name, "<application profile name>"). En este caso devolverá una coincidencia exacta por el nombre en <application profile name>.
  • ?query-target-filter=wcard(fvAp.dn, "<filter criteria>"). En este caso devolverá todas las coincidencias que en el resultado obtenido su dn contenga la cadena en <filter criteria>.

También se pueden combinar para crear consultas más elaboradas, así:

  • ?query-target-filter=and(eq(fvAp.name, "<application profile name>"),eq(fvAp.dn, "<filter criteria>"). Hará que las dos condiciones eq deban cumplirse simultáneamente.
  • ?query-target-filter=or(eq(fvAp.name, "<application profile name>"),eq(fvAp.dn, "<filter criteria>"). En este caso cualquiera de las dos condiciones es válida para la respuesta.