We've covered two important functions: authenticating with the ACI fabric using login() and querying class data with classQuery(). Now, all we need is to display this data in the browser.

For this part of the tutorial, there are two parts. The first focuses on extracting data from the JSON object, and the second focuses on displaying the data in a table and showing how JavaScript libraries can be used to make the table interactive.

Step 1 - Prepare HTML page for ACI Endpoint App

As we move into the realm of HTML, the possibilities of building nice, interactive applications come to light. JavaScript provides many freely available libraries which can be used, one of which is Datatables. Another is Bootstrap, which is a commonly used styling library from the developers of Twitter. Bootstrap provides a CSS (Cascading Style Sheet) framework and grid layout for designing web pages. It alleviates common frustrations in web development, allowing for a visually pleasing and consistent interface.

To utilize these tools, they must be referenced in the HTML file to be loaded into the page. Thankfully, the required files are already present since they we include them for you.

You are going to edit the index.html file. You might already have this file open in your editor.

Add the two lines for reading the CSS files for Data Tables and Bootstrap.


<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
    <link rel="stylesheet" type="text/css" href="css/dataTables.bootstrap.min.css"/>
  </head>
  <body>

    <h1>Hello World!</h1>
    <script src="js/jquery-3.2.1.min.js"></script>
    <script src="js/index.js"></script>
  </body>
</html>

Step 2 - Add DataTables and Bootstrap JavaScript Files

Add script tags at the bottom of the HTML page for the minified DataTable and Bootstrap JavaScript code.


<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
    <link rel="stylesheet" type="text/css" href="css/dataTables.bootstrap.min.css"/>
  </head>
  <body>

    <h1>Hello World!</h1>
    <script src="js/jquery-3.2.1.min.js"></script>
    <script src="js/index.js"></script>
    <script src="js/jquery.dataTables.min.js"></script>
    <script src="js/dataTables.bootstrap.min.js"></script>
  </body>
</html>

Step 3 - Add HTML Table Structure

Datatables works by dynamically adding rows into the table on the page. You can think of the table structure below as a skeleton which is later updated by the JavaScript code. Bootstrap constructs will also be added to ensure the page renders the title and table in separate "rows". This avoids possibility of elements being in the wrong place.


<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
    <link rel="stylesheet" type="text/css" href="css/dataTables.bootstrap.min.css"/>
  </head>
  <body>
    <div class="container">
      <div class="row">
          <h1>Endpoint Viewer</h1>
      </div>
      <div class="row">
        <div class="table-responsive">
          <table id="endpoint-table" class="table table-striped table-hover">
          </table>
        </div>
      </div>
    </div>
    <script src="js/jquery-3.2.1.min.js"></script>
    <script src="js/index.js"></script>
    <script src="js/jquery.dataTables.min.js"></script>
    <script src="js/dataTables.bootstrap.min.js"></script>
  </body>
</html>

Step 4 - Building the Endpoint Table Dynamically

Given ACI endpoint data, a buildTableData() function will be created to dynamically build rows of table data. It will return table_data, which is a JavaScript array of tables rows, similar to a SQL DB return of rows. Each element (row) in this array will be an array of data values, making table_data a multi-dimensional array.

The buildTableData() function receives the ACI JSON response and iterates through the data using the jQuery library $.each() call. The $.each() function can be used to iterate over any collection, such as an object or array. In the case of an array, the callback function is passed an array index (i) and corresponding value (endpoint) each time through the loop.

Since each corresponding value (endpoint) is an object, in JavaScript we access the attributes of objects using dot notation. For example, .fvCEp.attributes.mac. Using this, we can create a row containing only the information we care about and push it to the end of the table_dataarray.


var creds = {
  url: 'http://10.0.226.41',
  name: 'aciproglab04',
  pwd: 'cisco.123',
  token: '',
  urlToken: ''
}

var endpoints = {};

function classQuery(classname) {
  return $.ajax({
    url: creds.url + '/api/node/class/' + classname + '.json',
    headers: {
      'DevCookie': creds.token,
      'APIC-challenge': creds.urlToken,
      'Content-Type': 'application/json'
    }
  });
};

function login() {
  return $.ajax({
    type: 'POST',
    url: creds.url + '/api/aaaLogin.json?gui-token-request=yes',
    data: JSON.stringify({
      aaaUser: {
        attributes: {
          name: creds.name,
          pwd: creds.pwd
        }
      }
    }),
  });
};

function buildTableData(aci_endpoint_data) {
  regex =  /tn-(\w*)\/ap-(\w*)\/epg-(\w*)\/cep-(\w*:\w*:\w*:\w*:\w*:\w*)/
  table_data = []
  $.each(aci_endpoint_data['imdata'], function (i, endpoint) {  // for each endpoint in the data
    var line_dn = regex.exec(endpoint.fvCEp.attributes.dn)
    if (line_dn) {
      tenant = line_dn[1]
      app_profile = line_dn[2]
      epg = line_dn[3]
      row = [ endpoint.fvCEp.attributes.mac, tenant, app_profile, epg ];  // build a row containing the endpoint's IP and MAC
      table_data.push(row);  // then add this entry to the end of the array
    }
  });
  return table_data;
}

$( document ).ready(function() {
  login().then(function (data) {
    var attrs = data['imdata'][0]['aaaLogin']['attributes'];
    creds.token = attrs['token'];
    creds.urlToken = attrs['urlToken'];

    classQuery('fvCEp').then(function (data) {
      endpoints = data['imdata'];
      console.log('Total # of endpoints: ' + endpoints.length);
    });
  });
});

What are you doing with the buildTableData code?

  1. First we define the regular expression. This is similar to what we did in the Python side to break in parts the ACI distinguished name.
  2. Then the we start a look with the aci_endpoint_data to iterate through the ACI data.
  3. The REGEX is processed and if it matches ( that the object has a Tenant, APP Profile and EPG ) then we extract the data from the regex and push it into the return data that will be added to table_data.
  4. Once it finishes looping over all the return ACI data, the function completes and returns the structured data that we need for the DataTables javascript code.

Step 5 - Calling buildTableData to update the DataTable

As mentioned previously, the DataTables library builds dynamic HTML tables from data provided. You will modify the logic to call buildTableData() after data has been received from classQuery(). Finally, the rows of table data returned from buildTableData() will be passed into a DataTable() function to be rendered on the page.

Make sure to modify the existing code, NOT simply adding new code.

var creds = {
  url: 'http://10.0.226.41',
  name: 'aciproglab04',
  pwd: 'cisco.123',
  token: '',
  urlToken: ''
}

var endpoints = {};

function classQuery(classname) {
  return $.ajax({
    url: creds.url + '/api/node/class/' + classname + '.json',
    headers: {
      'DevCookie': creds.token,
      'APIC-challenge': creds.urlToken,
      'Content-Type': 'application/json'
    }
  });
};

function login() {
  return $.ajax({
    type: 'POST',
    url: creds.url + '/api/aaaLogin.json?gui-token-request=yes',
    data: JSON.stringify({
      aaaUser: {
        attributes: {
          name: creds.name,
          pwd: creds.pwd
        }
      }
    }),
  });
};

function buildTableData(aci_endpoint_data) {
  regex =  /tn-(\w*)\/ap-(\w*)\/epg-(\w*)\/cep-(\w*:\w*:\w*:\w*:\w*:\w*)/
  table_data = []
  $.each(aci_endpoint_data['imdata'], function (i, endpoint) {  // for each endpoint in the data
    var line_dn = regex.exec(endpoint.fvCEp.attributes.dn)
    if (line_dn) {
      tenant = line_dn[1]
      app_profile = line_dn[2]
      epg = line_dn[3]
      row = [ endpoint.fvCEp.attributes.mac, tenant, app_profile, epg ];  // build a row containing the endpoint's IP and MAC
      table_data.push(row);  // then add this entry to the end of the array
    }
  });
  return table_data;
}

$( document ).ready(function() {
  login().then(function (data) {
    var attrs = data['imdata'][0]['aaaLogin']['attributes'];
    creds.token = attrs['token'];
    creds.urlToken = attrs['urlToken'];

    classQuery('fvCEp').then(buildTableData).then( function(table_data){
      $("#endpoint-table").DataTable({  // using jQuery to select the HTML element with ID='endpoint-table'
        data: table_data,               // and calling DataTable() to render the data
        columns: [                      // constructed from buildTableData()
          {title: "MAC Address"},
          {title: "Tenant"},
          {title: "App Profile"},
          {title: "EPG"}
        ]
      })
    });
  });
});

What is happening with the above code?

  1. classQuery() function is called requesting the data of the object fvCEp
  2. The function classQuery() builds the REST Request to the ACI fabric and the jquery code invokes the $.ajax() function with the specified parameters.
  3. The javascript code uses the then capability to wait for the response from the APIC with all the data. Once that code completes and returns the 200OK the code execution returns to this point.
  4. The data returned from classQuery() is passed into buildTableData() function which processes the return data from the APIC and converts it into a format that we can utilize to build the table.
  5. The data returned from buildTableData() is passed into a anonymous function with the data as table_data.
  6. In this anonymouse function, jQuery selects the element in the HTML page with id of endpoint-table and calls DataTable() passing in the data and setting the column names.
  7. The datatables library then builds the table dynamically that has many interactive capabilities because it is managed via javascript.

Return to your web app in your browser tab to see your Hello World app has been updated. You should now see a table of endpoints from the ACI fabric which looks similar to: