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.
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.
<!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>
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>
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>
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_data
array.
var creds = {
url: 'https://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?
aci_endpoint_data
to iterate through the ACI data.
table_data
.
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: 'https://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?
classQuery()
function is called requesting the data of the object fvCEp
$.ajax()
function with the specified parameters.
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.
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.
buildTableData()
is passed into
a anonymous function with the data as table_data
.
endpoint-table
and calls DataTable()
passing in the data and setting the column names.
Return to your web app in your browser tab and hit reload to load the updated page. You should now see a table of endpoints from the ACI fabric which looks similar to: