This article includes code for a PHP proxy that can be used in conjunction with an Adobe FLEX 3 application or other applications, if desired. The included FLEX code (MXML and ActionScript) implements a GUI that uses the proxy to retrieve data from the PHP iControl proxy for display either in real-time or on-demand.

Three iControl WSDL documents are required for the proxy. It is suggested that these be retrieved from BIG-IP and saved/referenced locally from the proxy. The three WSDL documents required are:

System.Statistics
LocalLB.VirtualServer 

LocalLB.Pool

The PHP proxy code provides the following functionality:

1. getvslist.php

Returns a list of all virtual servers

2. getpoollistsair.php

Returns a list of all pools

3. getpoolmembersair.php

Returns a list of all nodes for a given pool

4. getstats.php

Returns a subset of HTTP system statistics.

5. airstats.php

Uses getstats.php to retrieve and format the statistics for use with the Adobe AIR BIG-IP sample monitoring application.

6. main.mxml

The main MXML (FLEX) application code

7. airstats.as

The main ActionScript code used by main.mxml

 The PHP files assume they reside in the same directory. If this is not the case, appropriately modify the "require_once" lines to indicate the proper path to the files. You must also supply the appropriate credentials for access to the BIG-IP you desire to monitor, and the correct host and path to the referenced WSDL files. The referenced images are not included; they may be deleted without any detrimental effect to the application.

 


 

File: AirStats.as

// ActionScript file

import mx.collections.XMLListCollection;

import mx.rpc.events.ResultEvent;

private var params:Object = new Object();
[Bindable]
private var listData:XMLListCollection;
[Bindable]
private var virtualData:XMLListCollection;
private var params2:Object = new Object();
[Bindable]
private var poolData:XMLListCollection;
[Bindable]
private var memberData:XMLListCollection;

public function resultHandler(event:ResultEvent):void {   
    var result:XML = XML(event.result); 
    var xmlList:XMLList = result.data.children();   
    listData = new XMLListCollection(xmlList);
}

public function memberHandler(event:ResultEvent):void {   
    var result:XML = XML(event.result); 
    var xmlList:XMLList = result.data.children();   
    memberData = new XMLListCollection(xmlList);
}

public function vsHandler(event:ResultEvent):void {   
    var result:XML = XML(event.result); 
    var xmlList:XMLList = result.data.children();   
    virtualData = new XMLListCollection(xmlList);
}

public function statusHandler(event:ResultEvent):void {
    xmlTree = XML(poolStatusService.lastResult.node);
    poolTreeList.dataProvider = xmlTree;
}

public function insertItemHandler(event:ResultEvent):void {   
    getStats();
}

public function statusItemHandler(event:ResultEvent):void {   
    getPoolStatus();
}

public function memberItemHandler(event:ResultEvent):void {   
}

public function getVirtualServers():void {

    params = new Object();
    vsService.addEventListener(ResultEvent.RESULT,vsHandler);
    vsService.method = "GET";
    vsService.cancel();
    vsService.send(params);
}

public function getMembers(mname:String):void {
    params = new Object();
    memberService.addEventListener(ResultEvent.RESULT,memberHandler);
    memberService.method = "GET";
    memberService.cancel();
    params["pname"] = mname;
    memberService.send(params);
}

public function getPoolStatus():void{
    params = new Object();
    poolStatusService.removeEventListener(ResultEvent.RESULT,statusItemHandler);
    poolStatusService.addEventListener(ResultEvent.RESULT,statusHandler);
    poolStatusService.method = "GET";
    poolStatusService.cancel();
    poolStatusService.send(params);
}

public function getStats():void{
    params = new Object();   
    statsService.removeEventListener(ResultEvent.RESULT,insertItemHandler);
    statsService.addEventListener(ResultEvent.RESULT,resultHandler);
    statsService.method = "GET";
    statsService.cancel();
    statsService.send(params);
}

 


File: main.mxml

 

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="absolute" height="504"
    showStatusBar="true"
    showTitleBar="false"
    title="BIG-IP Deck"
    creationComplete="init()"
    width="788" backgroundGradientAlphas="[0.0, 0.0]" cornerRadius="20" borderColor="#6C2C2C" viewSourceURL="srcview/index.html" backgroundGradientColors="[#000000, #000000]" color="#000000">
    <mx:Script>
        <![CDATA[
            include "AirStats.as";
                   
            import flash.utils.Timer;
            import flash.events.TimerEvent;
 
            private const TIMER_INTERVAL:int = 30000;
            private var baseTimer:int;
            private var t:Timer;
            private var counter:int = 0;
           
            [Bindable]
            public var selectedNode:XML;

            // Event handler for the Tree control change event.
            public function treeChanged(event:Event):void {
                selectedNode=Tree(event.target).selectedItem as XML;
                if (selectedNode.attribute("pool").toString() == "1")
                    getMembers(selectedNode.attribute("label").toString());
               
            }

            private function init():void {
                t = new Timer(TIMER_INTERVAL);
                t.addEventListener(TimerEvent.TIMER, updateTimer);
            }

            private function updateTimer(evt:TimerEvent):void {
                getStats();
                getPoolStatus();
            }
           
            private function doUpdate():void {
                getStats();
                getPoolStatus();
                getVirtualServers();
            }
           
            private function startTimer():void {
                baseTimer = getTimer();
                t.start();
                stopButton.enabled = true;
                startButton.enabled = false;
                checkButton.enabled = false;
                doUpdate();
            }

            private function stopTimer():void {
                t.stop();
                stopButton.enabled = false;
                startButton.enabled = true;
                checkButton.enabled = true;
            }
        ]]>
    </mx:Script>
   
<mx:XML id="xmlTree"/>


<mx:HTTPService
        id="vsService"
         url="http://<your domain here>/getvslist.php"
        resultFormat="xml"
        useProxy="false"/>
       
<mx:HTTPService
        id="statsService"
         url="http://<your domain here>/airstats.php"
        resultFormat="xml"
        useProxy="false"/>
       
<mx:HTTPService
        id="memberService"
         url="http://<your domain here>/getpoolmembersair.php"
        resultFormat="xml"
        useProxy="false"/>
   
<mx:HTTPService
        id="poolStatusService"
         url="http://<your domain here>/getpoollistair.php"
        resultFormat="e4x"
        useProxy="false"/>
          
    <mx:ApplicationControlBar dock="true" fillAlphas="[1.0, 1.0]" fillColors="[#FFFFFF, #FFFFFF]">
        <mx:Image scaleContent="true" autoLoad="true" width="41" height="35">
            <mx:source>@Embed(source='f5-logo.gif')</mx:source>
        </mx:Image>
        <mx:Text id="counterText" />
        <mx:Button label="START" click="startTimer()"  id="startButton" color="#782F2F"/>
        <mx:Button label="STOP" click="stopTimer()"  id="stopButton" enabled="false" color="#782F2F"/>
        <mx:Button id="checkButton" label="MANUAL CHECK" click="doUpdate()" color="#782F2F"/>
       
    </mx:ApplicationControlBar>   
   
    <mx:Panel x="17.5" y="10" width="413" height="437" layout="absolute" title="BIG-IP Statistics" id="stats" borderColor="#9D1E1E">
        <mx:DataGrid x="10" y="10" dataProvider="{listData}" width="373" height="166">
            <mx:columns>
                <mx:DataGridColumn headerText="Statistic" dataField="statName"/>
                <mx:DataGridColumn headerText="Value" dataField="statValue"/>
            </mx:columns>
        </mx:DataGrid>
        <mx:DataGrid x="10" y="184" width="373" height="203" dataProvider="{virtualData}" id="virtualGrid">
            <mx:columns>
                <mx:DataGridColumn headerText="Virtual Servers" dataField="vName"/>
                <mx:DataGridColumn headerText="Status" dataField="status"/>
            </mx:columns>
        </mx:DataGrid>
    </mx:Panel>
    <mx:Style source="Desktop.css"/>
    <mx:Panel x="438" y="10" width="338" height="437" layout="absolute" title="BIG-IP Pools" borderColor="#9D1E1E">
        <mx:Tree folderClosedIcon="{null}"  folderOpenIcon="{null}" selectionColor="0xC0C0C0" textSelectedColor="0xFF0000" x="10" y="10" width="298" height="249" showRoot="false" dataProvider="{poolData}" id="poolTreeList" labelField="@label"  change="treeChanged(event)"></mx:Tree>
        <mx:DataGrid x="10" y="267" height="120" dataProvider="{memberData}" id="memberGrid" width="298">
            <mx:columns>
                <mx:DataGridColumn headerText="IP Address" dataField="address"/>
                <mx:DataGridColumn headerText="Port" dataField="port"/>
            </mx:columns>
        </mx:DataGrid>
    </mx:Panel>
   
</mx:WindowedApplication>

 


 

File: airstats.php

<?
// Returns a a subset of HTTP system statistics formatted in XML for use in FLEX monitoring sample app 
require_once 'getstats.php';

// get each set of stats and put in an xml response
$xmlresponse = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n";
$xmlresponse .= "<response><data>";

$showlabel = 0;

$stats = getstats("ALL");
$xmlresponse .= "<row><statName>Total Requests</statName><statValue>$stats</statValue></row>";
$stats = getstats("200");
$xmlresponse .= "<row><statName>Total 2xx Responses</statName><statValue>$stats</statValue></row>";
$stats = getstats("400");
$xmlresponse .= "<row><statName>Total 4xx Responses</statName><statValue>$stats</statValue></row>";
$stats = getstats("500");
$xmlresponse .= "<row><statName>Total 5xx Responses</statName><statValue>$stats</statValue></row>";
$stats = getstats("GET");
$xmlresponse .= "<row><statName>Total GET Requests</statName><statValue>$stats</statValue></row>";
$stats = getstats("POST");
$xmlresponse .= "<row><statName>Total POST Requests</statName><statValue>$stats</statValue></row>";

$xmlresponse .= "</data></response>";

echo $xmlresponse;

?>

 


File: getstats.php

 

<? 
  // gets a subset of HTTP statistics for return in an XML format
require_once 'SOAP/Client.php';
  // Thanks to "thegeek" for the solution to dealing with 64-bit integers in PHP
  // http://www.gerd-riesselmann.net/archives/2006/01/a-64-bit-integer-for-php

  define("BIGINT_DIVIDER", 0x7fffffff + 1);

function getstats($reqstat) {
  $soapoptions = array('namespace' => 'urn:iControl'); 
  $wsdl_url = 'http://<domain/path to wsdl here>/System.Statistics.wsdl'; 

  $proxy_parms = array( 'user' => 'your user name here', 'pass' => 'your password here!');

  $params = array(); 
  $client = new SOAP_Client($wsdl_url, true, '', $proxy_parms );  
  $client->setOpt('curl', CURLOPT_SSL_VERIFYPEER, 0); 
  $client->setOpt('curl', CURLOPT_SSL_VERIFYHOST, 0); 

  // call to get_http_statistics takes no parms, returns a System.Statistics structure
  $response    = $client->call('get_http_statistics', $params, $soapoptions);

  if (PEAR::isError($response)) {        
          print "an error occurred in the call " . $response;
  }
  else {
	  $finalresponse = "";
          foreach ($response as $stat) {   
	     foreach ($stat as $stats) {	
		  $label = "";
	          if ($stats->type == 'STATISTIC_HTTP_TOTAL_REQUESTS' && $reqstat == "ALL") {
              	     $label = "TOTAL REQUESTS"; 
                  }    
                  else 
                  if ($stats->type == 'STATISTIC_HTTP_2XX_RESPONSES' && $reqstat == "200") {
                     $label = "HTTP 2xx RESPONSES";
                  }
                  else 
                  if ($stats->type == 'STATISTIC_HTTP_4XX_RESPONSES' && $reqstat == "400") {
                     $label = "HTTP 4xx RESPONSES";
                  }
                  else 
                  if ($stats->type == 'STATISTIC_HTTP_5XX_RESPONSES' && $reqstat == "500") {
                     $label = "HTTP 5xx RESPONSES";
                  }
                  else 
                  if ($stats->type == 'STATISTIC_HTTP_GET_REQUESTS' && $reqstat == "GET") {
                     $label = "GET REQUESTS";
                  }
                  else 
                  if ($stats->type == 'STATISTIC_HTTP_POST_REQUESTS' && $reqstat == "POST") {
                     $label = "POST REQUESTS";
                  }
		  
                  if ($label != "" ) {
		   $realvalue1 = $stats->value->high * BIGINT_DIVIDER + $stats->value->low;
		   $finalresponse .= "$label:$realvalue1   ";
                  }
	     }
          }
       return $finalresponse;
  }

}
?>

File: getvslist.php
// Get a list of all virtual servers on BIG-IP
<? 
  require_once 'SOAP/Client.php';

function getvslist() {

  $soapoptions = array('namespace' => 'urn:iControl'); 
  $wsdl_url = 'http://<domain and path to WSDL here>/LocalLB.VirtualServer.wsdl'; 

  $proxy_parms = array( 'user' => 'your user name here', 'pass' => 'your password here!');

  $params = array(); 
  $client = new SOAP_Client($wsdl_url, true, '', $proxy_parms );  
  $client->setOpt('curl', CURLOPT_SSL_VERIFYPEER, 0); 
  $client->setOpt('curl', CURLOPT_SSL_VERIFYHOST, 0); 

  $response    = $client->call('get_list', $params, $soapoptions);
  
  if (PEAR::isError($response)) {        
          print "an error occurred in the call " . $response;
  }
  else {
	  $finalresponse = "";
  	  $client2 = new SOAP_Client($wsdl_url, true, '', $proxy_parms );  
  	  $client2->setOpt('curl', CURLOPT_SSL_VERIFYPEER, 0); 
   	  $client2->setOpt('curl', CURLOPT_SSL_VERIFYHOST, 0); 
	  foreach ($response as $vserver) {
		$finalresponse .= "<row>";
		$finalresponse .= "<vName>$vserver</vName>";
                $vsname = array($vserver);        
                $params = array( 'virtual_servers' => $vsname );        
  		$response2    = $client2->call('get_object_status', $params, $soapoptions);
                if ($response2[0]->availability_status == 'AVAILABILITY_STATUS_GREEN') {               
			$finalresponse .= "<status>AVAILABLE</status>";
		} else
			$finalresponse .= "<status>UNAVAILABLE</status>";
		$finalresponse .= "</row>";
	  }
          return $finalresponse;
  }

}


echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n";
echo "<response><data>";
echo getvslist();
echo "</data></response>";

 


File: getpoollistair.php

 

<? 
// gets a list of all pools and their associated status and formats in XML for use with FLEX monitoring sample app
require_once 'SOAP/Client.php';
  // Thanks to "thegeek" for the solution to dealing with 64-bit integers in PHP
  // http://www.gerd-riesselmann.net/archives/2006/01/a-64-bit-integer-for-php

  define("BIGINT_DIVIDER", 0x7fffffff + 1);

function get_pool_names($ar) {
  $soapoptions = array('namespace' => 'urn:iControl'); 
  $wsdl_url = 'http://<domain and path to WSDL here>/LocalLB.Pool.wsdl'; 

  $proxy_parms = array( 'user' => 'your user name here', 'pass' => 'your password here');

  $params = array(); 
  $client = new SOAP_Client($wsdl_url, true, '', $proxy_parms );  
  $client->setOpt('curl', CURLOPT_SSL_VERIFYPEER, 0); 
  $client->setOpt('curl', CURLOPT_SSL_VERIFYHOST, 0); 

  // call to get_http_statistics takes no parms, returns a System.Statistics structure
  $response    = $client->call('get_list', $params, $soapoptions);

  if (PEAR::isError($response)) {        
          print "an error occurred in the call " . $response;
  }
  else {
	  if ($ar == 1) {
		// want return as a array 
		return $response;
	  }

	  $finalresponse = "";
          foreach ($response as $poolname) {   
		$finalresponse .= "$poolname<br/>";
          }
          return $finalresponse;
  }

}

function get_pool_status($poolname) {
  $soapoptions = array('namespace' => 'urn:iControl'); 
  $wsdl_url = 'http://<domain and path to WSDL here>/LocalLB.Pool.wsdl'; 

  $proxy_parms = array( 'user' => 'your user name here', 'pass' => 'your password here!');

  $pools = array($poolname);        
  $params = array( 'pool_names' => $pools );        

  $client = new SOAP_Client($wsdl_url, true, '', $proxy_parms );  
  $client->setOpt('curl', CURLOPT_SSL_VERIFYPEER, 0); 
  $client->setOpt('curl', CURLOPT_SSL_VERIFYHOST, 0); 

  $response    = $client->call('get_object_status', $params, $soapoptions);

  if (PEAR::isError($response)) {        
          print "an error occurred in the call " . $response->toString();
  }
  else {
	  $finalresponse = "";
          foreach ($response as $status) { 
		$finalresponse .= "<node pool=\"0\" label=\"";
		if ($status->availability_status == "AVAILABILITY_STATUS_BLUE") {
			$finalresponse .= "UNSURE OF STATUS";
		} else 
		if ($status->availability_status == "AVAILABILITY_STATUS_GREEN") {
			$finalresponse .= "UP";
		} else 
			$finalresponse .= "DOWN";
		$finalresponse .= "\"/>";	
		
		$finalresponse .= "<node pool=\"0\" label=\"";
		if ($status->enabled_status == "ENABLED_STATUS_ENABLED") {
			$finalresponse .= "ENABLED";
		} else
			$finalresponse .= "DISABLED";
		$finalresponse .= "\"/>";
	
          }
          return $finalresponse;
  }

}

echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n";
echo "<PoolTree>";
echo "<node label=\"Root\">";
$list =  get_pool_names(1);
foreach ($list as $member) {
	echo "<node pool=\"1\" label=\"$member\">";
	echo get_pool_status($member); 
	echo "</node>";
}
echo "</node>";
echo "</PoolTree>";
?>

 


File: getpoolmembersair.php

 

<? 
// gets a list of all nodes (IP address and port) for the pool specified by $poolname
require_once 'SOAP/Client.php';
  // Thanks to "thegeek" for the solution to dealing with 64-bit integers in PHP
  // http://www.gerd-riesselmann.net/archives/2006/01/a-64-bit-integer-for-php

  define("BIGINT_DIVIDER", 0x7fffffff + 1);

function get_pool_members($poolname) {
  $soapoptions = array('namespace' => 'urn:iControl'); 
  $wsdl_url = 'http://<domain and path to WSDL here>/LocalLB.Pool.wsdl'; 

  $proxy_parms = array( 'user' => 'your user name here', 'pass' => 'your password here');

  $pools = array($poolname);        
  $params = array( 'pool_names' => $pools );        

  $client = new SOAP_Client($wsdl_url, true, '', $proxy_parms );  
  $client->setOpt('curl', CURLOPT_SSL_VERIFYPEER, 0); 
  $client->setOpt('curl', CURLOPT_SSL_VERIFYHOST, 0); 

  $response    = $client->call('get_member', $params, $soapoptions);

  if (PEAR::isError($response)) {        
          print "an error occurred in the call " . $response->toString();
  }
  else {
	  $finalresponse = "";
          foreach ($response as $portdef) { 
		foreach ($portdef as $member) {
			$finalresponse .= "<row>";
		    	$finalresponse .= "<address>$member->address</address>";
			$finalresponse .= "<port>$member->port</port>";
			$finalresponse .= "</row>";
		}
          }
          return $finalresponse;
  }

}

echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n";
echo "<response>";
echo "<data>";
echo get_pool_members($pname);
echo "</data>";
echo "</response>";
?>