Network Statistics Sample SDN Application With OpenDaylight Code Walk Thru

Continuing my earlier blog about the sample SDN application that I have created called Network Statistics SDN Application, here I’ll walk you thru the code and how I developed it.

Technologies used in this project are HTML, Twitter Bootstrap CSS, JQuery, JavaScript and WAR Packaging. This project is available at my git for download.

First, create a Java Dynamic Web Project. Here in this project although I do not use server-side code, but based on my confort in this tech-stack, that is what I chose.

18

 

 

 

 

 

 

 

 

Second, I searched online for some Bootstrap CSS based theme that would suffice this project & selected Freelancer Free Theme which I downloaded and copied into the WebContent of the above created project.

I have created the UI like this, with this theme.

Third, I changed the html file as per my needs and create four sections in it.

  1. Section 1: Place where I take input for SDN Controller releated credentials.
  2. Section 2: Place where we display Network Devices.
  3. Section 3: Place where we display selected device’s Port Statistics.
  4. Section 4: Place where we display selected device’s Table Statistics.

Hence, the complete HTML for above sections looks something like this:

<html lang="en">
<head>

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">

<title>Network Stats SDN Application | SDN{Geeks}</title>

<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/freelancer.css" rel="stylesheet">
<link href="font-awesome-4.1.0/css/font-awesome.min.css"
    rel="stylesheet" type="text/css">
<link href="http://fonts.googleapis.com/css?family=Montserrat:400,700"
    rel="stylesheet" type="text/css">
<link
    href="http://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic"
    rel="stylesheet" type="text/css">
</head>

<body id="page-top" class="index">

    <nav class="navbar navbar-default navbar-fixed-top">
        <div class="container">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header page-scroll">
                <button type="button" class="navbar-toggle" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1">
                    <span class="sr-only">Toggle navigation</span> <span
                        class="icon-bar"></span> <span class="icon-bar"></span> <span
                        class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#page-top">Network Stats SDN
                    Application</a>
            </div>

            <div class="collapse navbar-collapse"
                id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav navbar-right">
                    <li class="hidden"><a href="#page-top"></a></li>
                    <li class="page-scroll"><a href="#nodes">Network Devices</a></li>
                    <li class="page-scroll"><a href="#port">Port Stats</a></li>
                    <li class="page-scroll"><a href="#table">Table Stats</a></li>
                </ul>
            </div>
        </div>
    </nav>

    <header>
        <div class="container">
            <div class="row">
                <div class="col-lg-10 col-lg-offset-1">
                    <p>
                        Network Statistics SDN Application runs on-top of a OpenDaylight
                        SDN Controller. <br />It tracks port, table and flow statistics
                        of all the nodes added to OpenDaylight. Run this application on
                        tomcat locally and supply the credentials below to see it in
                        action.
                    </p>
                    <p>This is a pure jQuery/Bootstrap and HTML based application
                        without any backend in itself.</p>
                </div>

                <div class="col-lg-12">
                    <br /> <br />
                    <div class="col-lg-3"></div>
                    <div class="col-lg-6">
                        <input type="text" class="form-control"
                            placeholder="SDN Controller URL" id="url"> <br /> <input
                            type="text" class="form-control"
                            placeholder="SDN Controller Username" id="username"> <br />
                        <input type="password" class="form-control"
                            placeholder="SDN Controller Password" id="password"> <br />
                        <br />
                        <div class="col-lg-8 col-lg-offset-2 text-center">
                            <a href="javascript:trackNetworkStatisticsClicked();"
                                class="btn btn-lg btn-outline"> <i class="fa fa-tachometer"></i>
                                Track Network Statistics
                            </a>
                        </div>

                    </div>
                    <div class="col-lg-3"></div>
                </div>
            </div>
        </div>
    </header>

    <section id="nodes">
        <div class="container">
            <div class="row">
                <div class="col-lg-12 text-center">
                    <h2>Network Devices</h2>
                    <hr />
                </div>
            </div>
            <div class="row">
                <div class="col-lg-12" align="center">
                    <p>This section shows all the device loaded into OpenDaylight.
                        I have created a thumbnail for these devices.</p>
                    <p>You can very well use any JS plotting frameworks to show the
                        network topology also</p>
                    <br /> <br />

                </div>
            </div>

            <div class="row" id="nodesButton">
                <div class="col-md-12" align="center">
                    <a href="#page-top" class="btn btn-success"> Please enter
                        OpenDaylight credentials first. </a> <br /> <br />
                </div>
            </div>

            <div class="row hidden" id="nodesDiv">
            </div>
        </div>
    </section>

    <section id="port" style="background: #C8D1DA;">
        <div class="container">
            <div class="row">
                <div class="col-lg-12 text-center">
                    <h2>Port Statistics</h2>
                    <hr />
                </div>
            </div>
            <div class="row">
                <div class="col-lg-12" align="center">
                    <p>This section shows the port statistics of the selected
                        device.</p>
                    <p>
                        You can intelligently show which ports are running maximum traffic
                        and are at risk etc. <br />There are numerous use cases that open
                        up with this data
                    </p>
                    <br /> <br />
                </div>
            </div>

            <div class="row" id="portButton">
                <div class="col-md-12" align="center">
                    <a href="#nodes" class="btn btn-success"> Please select a
                        device to view port statistics. </a> <br /> <br />
                </div>
            </div>

            <div class="row hidden" id="portDiv">
            </div>
        </div>
    </section>

    <section id="table">
        <div class="container">
            <div class="row">
                <div class="col-lg-12 text-center">
                    <h2>Table Statistics</h2>
                    <hr>
                </div>
            </div>
            <div class="row">
                <div class="col-lg-12" align="center">
                    <p>This section shows the OpenFlow table statistics of the
                        selected device.</p>
                    <p>This information is helpul in pre-configuring the switch and
                        knowing the switch capacity.</p>
                    <br /> <br />
                </div>
            </div>

            <div class="row" id="tableButton">
                <div class="col-md-12" align="center">
                    <a href="#nodes" class="btn btn-success"> Please select a
                        device to view table statistics. </a> <br /> <br />
                </div>
            </div>

            <div class="row hidden" id="tableDiv">
            </div>

        </div>
    </section>

    <footer class="text-center">
        <div class="footer-below">
            <div class="container">
                <div class="row">
                    <div class="col-lg-12">Network Statistics SDN Application by
                        Saurabh Agarwal</div>
                </div>
            </div>
        </div>
    </footer>

    <script src="js/jquery-1.11.0.js"></script>
    <script src="js/bootstrap.min.js"></script>
    <script
        src="http://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
    <script src="js/classie.js"></script>
    <script src="js/cbpAnimatedHeader.js"></script>
    <script src="js/jqBootstrapValidation.js"></script>
    <script src="js/contact_me.js"></script>
    <script src="js/freelancer.js"></script>
    
    <!-- This is where all the magic happen -->
    <script src="scripts/NetworkStats.js"></script>

</body>

</html>

Fourth, let’s move on to the javascript where the actual calls are made. I have created NetworkStats.js inside script folder and included in the above html.

<!-- This is where all the magic happen -->
<script src="scripts/NetworkStats.js"></script>

Fifth, for each action on the html page, I have written a corresponding javascript functions.

  1. Track Network Status” action – Here we take input from user for OpenDaylight credentials and fetch nodes via REST API.
  2. Port Stats” action – Here we take the node id and name of the device & fetch port statistics via REST API.
  3. Table Stats” action – Here we take the node id and name of the device & fetch port statistics via REST API.

Hence the complete javascript looks like this:

//Global Variables
var sdnControllerURL = null;
var sdnControllerUserName = null;
var sdnControllerPassword = null;
var base64EncodedPassword = null;

// Get the values supplied
// Store them in global valiables
// Populate Network Device section
// Show Network Devices
function trackNetworkStatisticsClicked() {

    sdnControllerURL = $("#url").val();
    sdnControllerUserName = $("#username").val();
    sdnControllerPassword = $("#password").val();
    base64EncodedPassword = "Basic "
            + btoa(sdnControllerUserName + ":" + sdnControllerPassword);

    console.log(sdnControllerURL);
    console.log(sdnControllerUserName);
    console.log(sdnControllerPassword);
    console.log(base64EncodedPassword);

    if (ifCredentialsNotNull(sdnControllerUserName, sdnControllerPassword)) {
        populateNetworkNodes();
    } else {
        alert("Trying error cases huhh..? Please enter createndials.");
    }
}

// Fetch network nodes from OpenDaylight
// Populate Network Device section
// Show Network Devices
function populateNetworkNodes() {
    var nodes = null;
    $
            .ajax({
                url : sdnControllerURL
                        + "/controller/nb/v2/switchmanager/default/nodes",
                type : "GET",
                async : false,
                contentType : "application/json",
                success : function(data, textStatus, jqXHR) {
                    console.log(data);
                    nodes = data;
                },
                error : function(jqXHR, textStatus, errorThrown) {
                    alert("Unable to fetch OpenDaylight Nodes.\nDid you supply the credentials correct?");
                },
                beforeSend : function(xhr) {
                    // Default Base64 Encoding for (admin/admin)
                    xhr
                            .setRequestHeader("Authorization",
                                    base64EncodedPassword);
                }
            });

    if (nodes != null && nodes != undefined) {
        // Construct divs
        $.each(nodes.nodeProperties, function(index, value) {
            var div = getNetworkDeviceDiv(value.properties.description.value,
                    value.node.id, value.properties.macAddress.value,
                    value.properties.timeStamp.value);
            $("#nodesDiv").append(div);
        });

        $("#nodesDiv").removeClass("hidden").addClass("visible");
        $("#nodesButton").removeClass("visible").addClass("hidden");

    }

}

// Method to create the network device div programatically
// No logic here, just plain factory generating divs on supplied inputs
function getNetworkDeviceDiv(name, id, mac, upTime) {
    var div = '<div class="col-sm-6 col-md-4"><div class="thumbnail"><br /> <br /> <img src="img/device.png"><div class="caption">';
    div += '<h4>' + name + '</h4>';
    div += '<ul class="list-group">';
    div += '<li class="list-group-item"><b>Id:</b> ' + id + '</li>';
    div += '<li class="list-group-item"><b>Name:</b> ' + name + '</li>';
    div += '<li class="list-group-item"><b>MAC:</b> ' + mac + '</li>';
    div += '<li class="list-group-item"><b>Connected Since:</b> ' + upTime
            + '</li>';
    div += '</ul><p>';
    div += '<a href="javascript:showSwitchPortStats(\''
            + name
            + '\',\''
            + id
            + '\')" class="btn btn-success" role="button">Port Stats</a> &nbsp;&nbsp;';
    div += '<a href="javascript:showSwitchTableStats(\'' + name + '\',\'' + id
            + '\')" class="btn btn-success" role="button">Table Stats</a>';
    div += '</p></div></div></div>';

    return div;
}

// Fetch ports for a node from OpenDaylight
// Populate ports stats section
function showSwitchPortStats(name, id) {
    var ports = null;
    $
            .ajax({
                url : sdnControllerURL
                        + "/controller/nb/v2/statistics/default/port/node/OF/"
                        + id,
                type : "GET",
                async : false,
                contentType : "application/json",
                success : function(data, textStatus, jqXHR) {
                    console.log(data);
                    ports = data;
                },
                error : function(jqXHR, textStatus, errorThrown) {
                    alert("Unable to fetch OpenDaylight Node Ports.\nDid you supply the credentials correct?");
                },
                beforeSend : function(xhr) {
                    // Default Base64 Encoding for (admin/admin)
                    xhr
                            .setRequestHeader("Authorization",
                                    base64EncodedPassword);
                }
            });

    if (ports != null && ports != undefined) {
        // Construct divs
        var finalDiv = '<div class="col-lg-12"><div class="panel panel-success"><div class="panel-heading"><h4>';
        finalDiv += name + ' : Port Statistics</h4>';
        finalDiv += '</div><div class="panel-body">';

        // For each port create a sub element in the final div
        $.each(ports.portStatistic, function(index, value) {
            var div = getPortsDiv(value.nodeConnector.id, value.receivePackets,
                    value.transmitPackets, value.receiveBytes,
                    value.transmitBytes, value.receiveDrops,
                    value.transmitDrops);
            finalDiv += div;
        });

        finalDiv += '</div></div></div>';
        $("#portDiv").append(finalDiv);
        $("#portDiv").removeClass("hidden").addClass("visible");
        $("#portButton").removeClass("visible").addClass("hidden");

    }

}

// Method to create the port stats div programatically
// No logic here, just plain factory generating divs on supplied inputs
function getPortsDiv(portId, receivePackets, transmitPackets, receiveBytes,
        transmitBytes, receiveDrops, transmitDrops) {
    var div = '<div class="col-sm-2 col-md-2"><div class="thumbnail"><img src="img/port.png" alt="..." height="60" width="60"><div class="caption">';
    div += '<p align="center">Port ' + portId + '</p></div>';
    div += 'Packet Rx: ' + receivePackets + '<br /> Packet Tx: '
            + transmitPackets + '<br />Byte Rx: ' + receiveBytes
            + '<br />Byte Tx: ' + transmitBytes + '<br />Drop Rx: '
            + receiveDrops + '<br />Drop Tx: ' + transmitDrops + '</div></div>';
    return div;
}

// Fetch table stats for a node from OpenDaylight
// Populate table stats section
function showSwitchTableStats(name, id) {
    var tableStats = null;
    $
            .ajax({
                url : sdnControllerURL
                        + "/controller/nb/v2/statistics/default/table/node/OF/"
                        + id,
                type : "GET",
                async : false,
                contentType : "application/json",
                success : function(data, textStatus, jqXHR) {
                    console.log(data);
                    tableStats = data;
                },
                error : function(jqXHR, textStatus, errorThrown) {
                    alert("Unable to fetch OpenDaylight Node Table Stats.\nDid you supply the credentials correct?");
                },
                beforeSend : function(xhr) {
                    // Default Base64 Encoding for (admin/admin)
                    xhr
                            .setRequestHeader("Authorization",
                                    base64EncodedPassword);
                }
            });

    if (tableStats != null && tableStats != undefined) {
        // Construct divs
        var firstTable = null;
        var finalDiv = '<div class="col-lg-12"><div class="panel panel-success"><div class="panel-heading">';
        finalDiv += '<h4>' + name + ' : Table Statistics</h4>';

        // I'm only considering one table for this demo project.
        // You can actually iterate thru them to have multiple as per OF spec
        $.each(tableStats.tableStatistic, function(index, value) {
            firstTable = value;
            return;
        });

        finalDiv += '</div><div class="panel-body"><p>Table Id: '
                + firstTable.nodeTable.id + '</p></div>';
        finalDiv += '<ul class="list-group">';
        finalDiv += '<li class="list-group-item"><span class="badge badge-success">'
                + firstTable.activeCount + '</span>Active Count</li>';
        finalDiv += '<li class="list-group-item"><span class="badge badge-success">'
                + firstTable.lookupCount + '</span>Lookup Count</li>';
        finalDiv += '<li class="list-group-item"><span class="badge badge-success">'
                + firstTable.matchedCount + '</span>Matched Count</li>';
        finalDiv += '<li class="list-group-item"><span class="badge badge-success">'
                + firstTable.maximumEntries
                + '</span>Maximum Supported Entries</li>';
        finalDiv += '</ul></div></div>';

        $("#tableDiv").append(finalDiv);
        $("#tableDiv").removeClass("hidden").addClass("visible");
        $("#tableButton").removeClass("visible").addClass("hidden");

    }

}

// Utility function to check not null user name password
function ifCredentialsNotNull(username, password) {
    if (username != null && password != null && username != ''
            && password != '') {
        return true;
    }
    return false;
}

Now, deploy this application into tomcat/jetty/jboss or any other web server of choice & you should see it running.

Sixth, you can verify the application, by enabling firebug or browser web developer tools to see if REST API calls are going thru, some sample calls as I get them are below.

6 11 8

 

Hope you are able to build and run the application by yourself using the above steps.

Let me know if you find any issues & i’ll try to help.

Advertisements

Example message exchange sequence between OpenFlow & SDN Controller

Further to understanding the OpenFlow messages that are exchanged between hardware and a SDN Controller in Understanding OpenFlow Communication Channel, I thought it will be nice to know the sequence in which these messages are exchanged.

Based on my understanding using wireshark, a OpenFlow switch and OpenDaylight, below is a visual sequence diagram explaining major message exchanges I could see.

OF Message Exchange Sequence

 

Debugging OpenDaylight Controller In Eclipse

Once you have installed OpenDaylight successfully and imported the code into eclipse you should get a clean workspace without any errors.

If you are facing any errors & you think we can help, please post below & I will see if I can.

So now you want to run the controller from eclipse & make changes & debug it? Let’s do that with a example change.

Goal (Cooked up): Change OpenDaylight Title to "SDN{Geeks}".

So for this, we will need to do the following:

1. Change the web component of OpenDaylight for title in eclipse.

Navigate to the web component of OpenDaylight & change/save main.jsp.

Main

2. Build web component (independently) via eclipse.

Right click on the web component & say “Run Configurations”. Create a maven job for the web project like below. Click Apply & Run.

Maven_Web

3. Build controller main component (independently, building the whole thing is somehow not smart) via eclipse.

Distribution project within OpenDaylight is the main project which holds the configurations for the controller startup etc. Once above step is done, we want that jar to be included for the run & hence need to build the master component.

Right click on the distribution component & say “Run Configurations”. Create a maven job for the distribution project like below. Click Apply & Run.

Dist_Component

Maven_Dist

4. Run the controller in Run/Debug mode from eclipse.

Now to run the controller from eclipse, navigate to below file. Right click and say “Run As” or “Debug As” “OpenDaylight Application”.

<Workspace> controller/opendaylight/distribution/opendaylight/opendaylight-application.launch

This will start the OpenDaylight controller in run/debug mode with your changes applied.

Launch

 

Goal Complete

You can repeat this for any component and it should be straight forward. If you find any issues with this, let me know below.

Installing OpenDaylight On CentOS

Please refer to Blinken’s blog for writing in detail about How to install OpenDaylight Controller on CentOS.

I suggest you follow his blog for this & come back here when you are at the step which says,

Configuring the Java variables

Belkins_Blog

As per the OpenDaylight Documentation:

Prerequisite: JVM 1.7+ (JAVA_HOME should be set to environment)

Now this is important as above blog uses java 1.6 with which controller might not fireup. Here are the steps to find out what version of java you are running & now to install 1.7

Find out all the versions available: yum search java | grep 'java-'

In the above list you should have java-1.7.0-openjdk.x86_64 (or development). Once you find that, use below command to install the right dev kit.

yum install java-1.7.0-openjdk.x86_64

You can opt out to remove 1.6 via yum itself if you don’t need it. (Like us). This should do it, you can follow the rest of instructions on Blinken’s blog.

Other problem that you might face while clean/install of controller is OutOfMemory.

Refer to the OpenDaylight Troubleshooting to increase your PermGen space for maven.

With these two additional steps, we were able to get controller up & running.