cancel
Showing results for 
Search instead for 
Did you mean: 

Pulse Secure vADC

Sort by:
The following code uses Stingray's RESTful API to a file to the extra directory.   The code is written in PHP and uses STMRESTClient.  This program adds the file 'validserialnumbers' to the extra directory and if the file already exists it will be overwrite it.  If this is not the desired behavior, code can be added to to check for the existence of the file as was done in the addpool example.   addextrafile.   <?php require_once 'stmrestclient.inc'; $fileName = 'validserialnumbers'; $validSerialNumbers = "123456\n234567\n345678\n"; try { # Set up the connection $client = new STMRestClient(); $client->put("extra/$fileName", $validSerialNumbers, 'application/octet-stream'); # If the file already exists, it will be replaced with this version and 204 will be returned # otherwise 201 will be returned. print("<br>statusCode=" . $client->statusCode . "<br><br>"); if ($client->statusCode == 201) { print("File $fileName added"); } else { print("File $fileName replaced"); } } catch (STMConnectException $e) { print ("Error connecting to the REST API: " . $e->getMessage()); } catch (STMAuthException $e) { print ("Error logging into the REST API: " . $e->getMessage()); } catch (exception $e) { print ("Error: " . $e->getMessage()); } ?>   Running the example   This code was tested with PHP 5.3.3 and uses the STMRestClient class that can be found here: Tech Tip: A Stingray Traffic Manager REST Client for PHP   To run this program, Make it available via a web server and the output should be:   File validserialnumbers added   or   File validserialnumbers replaced   Notes   Since this is a file and not a configuration resource, JSON will not be used and the MIME type will be "application/octet-stream".  Another difference when dealing with files is how Stingray handles adding a file that already exists.  If the file already exists, Stingray will overwrite the it and return a HTTP status code of 204.  If the file doesn't already exist, the HTTP status code will be a 201.   Read More   Stingray REST API Guide in the Stingray Product Documentation Feature Brief: Stingray's RESTful Control API Tech Tip: Using Stingray's RESTful Control API Tech Tip: Using the RESTful Control API with PHP - Overview Collected Tech Tips: Using the RESTful Control API
View full article
The following code uses Stingray's RESTful API to delete a pool.  The code is written in PHP and uses STMRESTClient.  This program deletes the "phptest" pool created by the addpool.php example.  To delete a resource you do a HTTP DELETE on the URI for the resource.  If the delete is successful a 204 HTTP status code will be returned.   deletepool.php   <?php # Delete a pool require_once 'stmrestclient.inc'; $poolName = 'phptest'; try { # Set up the connection $client = new STMRestClient(); /* Check to see if the pool exists. If it doesn't exist, then a * ResourceNotFoundException exception will be thrown. */ $response = $client->get("pools/$poolName"); $client->delete("pools/$poolName"); print("Pool $poolName deleted"); } catch (STMResourceNotFoundException $e) { # The pool doesn't exist print("Pool $poolName not found"); } catch (STMConnectException $e) { print ("Error connecting to the REST API: " . $e->getMessage()); } catch (STMAuthException $e) { print ("Error logging into the REST API: " . $e->getMessage()); } catch (Exception $e) { print ('Error: ' . $e->getMessage()); } print("</body></html>\n"); ?>   Running the example   This code was tested with PHP 5.3.3 and uses the STMRestClient class that can be found here: Tech Tip: A Stingray Traffic Manager REST Client for PHP   To run this program, Make it available via a web server and the output should be:   Pool phptest deleted   Read More   Stingray REST API Guide in the Stingray Product Documentation Feature Brief: Stingray's RESTful Control API Tech Tip: Using Stingray's RESTful Control API Tech Tip: Using the RESTful Control API with PHP - Overview Collected Tech Tips: Using the RESTful Control API
View full article
The following code uses Stingray's RESTful API to add a pool.   The code is written in PHP and uses STMRESTClient. This program creates a new pool, "phptest", first doing a GET to make sure the pool doesn't already exist, and if the pool doesn't exist, the pool is created by doing a PUT with just the minimum data needed to create a pool.  In this case the program creates a properties object with just one node.  All other values will get default values when Stingray creates the pool.   addpool.php   <?php # Add a pool require_once 'stmrestclient.inc'; $poolName = 'phptest'; $node = '192.168.168.135:80'; try { # Set up the connection $client = new STMRestClient(); /* Check to see if the pool already exist. If it does not yet exist, then a * ResourceNotFoundException exception will be thrown. */ $response = $client->get("pools/$poolName"); print("Pool $poolName already exists"); } catch (STMResourceNotFoundException $e) { # The pool doesn't exist, so it can be created $poolConfig->properties->basic->nodes = array($node); #$poolConfig = array('properties' => array('basic' => array('nodes' => array($node)))); try { $response = $client->put("pools/$poolName", $poolConfig); print("Pool $poolName added"); } catch (Exception $e) { print ("Error creating Pool $poolName: " . $e->getMessage()); } } catch (STMConnectException $e) { print ("Error connecting to the REST API: " . $e->getMessage()); } catch (STMAuthException $e) { print ("Error logging into the REST API: " . $e->getMessage()); } catch (Exception $e) { print ('Error: ' . $e->getMessage()); } print("</body></html>\n"); ?>   Running the example   This code was tested with PHP 5.3.3 and uses the STMRestClient class that can be found here: Tech Tip: A Stingray Traffic Manager REST Client for PHP   To run this program, Make it available via a web server and the output should be:   Pool phptest added   Notes   The only difference between doing a PUT to change a resource and a PUT to add a resource is the HTTP status code returned.  When changing a resource 200 is the expected status code and when adding a resource, 201 is the expected status code.   Read More   Stingray REST API Guide in the Stingray Product Documentation Feature Brief: Stingray's RESTful Control API Tech Tip: Using Stingray's RESTful Control API Tech Tip: Using the RESTful Control API with PHP - Overview Collected Tech Tips: Using the RESTful Control API
View full article
The following code uses Stingray's RESTful API to enable or disabled a specific Virtual Server.   The code is written in PHP and uses STMRESTClient.  This program checks to see if the Virtual Server "test vs" is enabled and if it is, it disables it and if it is disabled, it enables it.  A GET is done to retrieve the configuration data for the Virtual Server and the "enabled" value in the "basic" properties section is checked.  This is a boolean value, so if it is true it is set to false and if it is false it is set to true. The changed data is then sent to the server using a PUT.   startstopvs.php   <?php # Start or stop a virtual server require_once 'stmrestclient.inc'; $resource = 'vservers/test vs'; print("<html><body>\n"); try { # Set up the connection $client = new STMRestClient(); # Get the config data for the virtual server $vsConfig = $client->get($resource); if ($vsConfig->properties->basic->enabled) { $vsConfig->properties->basic->enabled = false; print("test vs is enabled. Disable it<br>\n"); } else { $vsConfig->properties->basic->enabled = true; print("test vs is Diabled. Enable it<br>\n"); } $response = $client->put($resource, $vsConfig); } catch (STMConnectException $e) { print ("Error connecting to the REST API: " . $e->getMessage()); } catch (STMAuthException $e) { print ("Error logging into the REST API: " . $e->getMessage()); } catch (exception $e) { print ('Error: ' . $e->getMessage()); } print("</body></html>\n"); ?>   Running the example   This code was tested with PHP 5.3.3 and uses the STMRestClient class that can be found here: Tech Tip: A Stingray Traffic Manager REST Client for PHP   To run this program, Make it available via a web server and the output should be:   test vs is enabled. Disable it.   or test vs is disabled. Enable it.     Read More   Stingray REST API Guide in the Stingray Product Documentation Feature Brief: Stingray's RESTful Control API Tech Tip: Using Stingray's RESTful Control API Tech Tip: Using the RESTful Control API with PHP - Overview Collected Tech Tips: Using the RESTful Control API
View full article
The following code uses Stingray's RESTful API to list all the pools defined for a cluster and for each pool it lists the nodes defined for that pool, including draining and disabled nodes. The code is written in PHP and uses STMRESTClient. This example builds on the previous listpools.php example.  This program does a GET request for the list of pool and then while looping through the list of pools, a GET is done for each pool to retrieve the configuration parameters for that pool.   listpoolnodes.php   <?php # Display a list of all nodes in all pools require_once 'stmrestclient.inc'; print("<html><body>\n<br><b>Pools:</b><br>\n"); try { $client = new STMRestClient(); $response = $client->get('pools'); $pools = $response->children; foreach ($pools as $pool) { $poolName = $pool->name; $poolConfig = $client->get("pools/$poolName"); $nodes = $poolConfig->properties->basic->nodes; $draining = $poolConfig->properties->basic->draining; $disabled = $poolConfig->properties->basic->disabled; print("<br>Pool: $poolName<br>\n"); print ' Nodes: '; foreach ($nodes as $node) { print "$node "; } print "\n"; if (count($draining)) { print ' Draining Nodes: '; foreach ($draining as $node) { print "$node "; } print "<br>\n"; } if (count($disabled)) { print ' Disabled Nodes: '; foreach ($disabled as $node) { print "$node "; } print "<br>\n"; } print "<br>\n"; } } catch (exception $e) { print ("Error: " . $e->getMessage()); } print("</body></html>\n"); ?>   Running the example   This code was tested with PHP 5.3.3 and uses the STMRestClient class that can be found here: Tech Tip: A Stingray Traffic Manager REST Client for PHP   To run this program, Make it available via a web server and the output should be:   Pools:   Pool1     Nodes:  192.168.1.100 192.168.1.101     Draining:  192.168.1.101     Disabled:  192.168.1.102   Pool2     Nodes:  192.168.1.103 192.168.1.104   Read More   Stingray REST API Guide in the Stingray Product Documentation Feature Brief: Stingray's RESTful Control API Tech Tip: Using Stingray's RESTful Control API Tech Tip: Using the RESTful Control API with PHP - Overview Collected Tech Tips: Using the RESTful Control API
View full article
The following code uses Stingray's RESTful API to list all the pools defined on a cluster. The code is written in PHP and uses STMRESTClient. This example has more extensive comments then the following examples and most of these are applicable to all the examples. This program does a single GET request for the list of pools and then loops through that list, each element of which is an associative array, and then outputs the pool name.   listpools.php   <?php # Display a list of pools require_once 'stmrestclient.inc'; print("<html><body>\n<br><b>Pools:</b><br><br>\n"); try { # Setup the connection to the REST API $client = new STMRestClient(); # Do the HTTP GET to get the lists of pools $response = $client->get('pools'); # Get the list of pools from the response as an object $pools = $response->children; # Loop through the list of pools foreach ($pools as $pool) { print($pool->name . "<br>\n"); } } catch (STMConnectException $e) { /* We weren't able to connect to the Stingray Traffic Manager REST API The most likely reasons for this are: - the hostname of the Stingray instance is incorrect - this client doesn't have network access to the Stingray instance or port 9070 - the RESTful API is disabled - the RESTful API is using using a different port */ print ("Error connecting to the REST API: " . $e->getMessage()); } catch (STMAuthException $e) { /* We were able to connect to the Stingray Traffic Manager REST API but could not log in The user name or password were incorrect. If they weren't specified then the default values are being used and may need to be changed or the user name and password can be specified */ print ("Error logging into the REST API: " . $e->getMessage()); } catch (exception $e) { print ('Error: ' . $e->getMessage()); } print("</body></html>\n"); ?>   Running the example   This code was tested with PHP 5.3.3 and uses the STMRestClient class that can be found here: Tech Tip: A Stingray Traffic Manager REST Client for PHP   To run this program, Make it available via a web server and the output should be:   Pools: Pool1 Pool2   Read More   Stingray REST API Guide in the Stingray Product Documentation Feature Brief: Stingray's RESTful Control API Tech Tip: Using Stingray's RESTful Control API Tech Tip: Using the RESTful Control API with PHP - Overview Collected Tech Tips: Using the RESTful Control API
View full article
This document covers how to migrate from a software version of our SSC product to the Virtual Appliance version.
View full article
Stingray Traffic Manager version 9.5 includes some important enhancements to the RESTful API.  These enhancements include the following:   A new API version The API version has moved to 2.0.  Versions 1.0 and 1.1 are still available but have been deprecated.   Statistics and Version Information A new resource, "status", is available that contains the child resources "information" and "statistics", under the host name.  Data can only be retrieved for these resources; no updates are allowed.  The URL for "information" is: http(s)://<host>:<port>/api/tm/2.0/status/<host>/information   and the URI for "statistics" is:   http(s)://<host>:<port>/api/tm/2.0/status/<host>/statistics   <host> can also be "local_tm", which is an alias for the Traffic Manager processing the REST request.  For this release, only statistics for the local Traffic Manager are available.   The "information" resource contains the version of the Stingray Traffic Manager, so for example the request:   http(s)://<host>:<port>/api/tm/2.0/status/local_tm/information   for version 9.5 would return:   tm_version9.5   The "statistics" resource contains the Stingray statistics that are also available with SNMP or the SOAP API.  The following child resources are available under "statistics":   actions, bandwidth, cache, cloud_api_credentials, connection_rate_limit, events, glb_services, globals, listen_ips, locations, network_interface, nodes, per_location_service, per_node_slm, pools, rule_authenticators, rules, service_level_monitors, service_protection, ssl_ocsp_stapling, traffic_ips, virtual_servers   The statistics that are available vary by resource.   Example:   To get the statistics for the pool "demo" on the Stingray Traffic Manager "stingray.example.com": https://stingray.example.com:9070/api/tm/2.0/status/local_tm/statistics/pools/demo { "statistics": { "algorithm": "roundrobin", "bytes_in": 20476976, "bytes_out": 53323, "conns_queued": 0, "disabled": 0, "draining": 0, "max_queue_time": 0, "mean_queue_time": 0, "min_queue_time": 0, "nodes": 1, "persistence": "none", "queue_timeouts": 0, "session_migrated": 0, "state": "active", "total_conn": 772 } } Resource Name Changes Some resources have been renamed to be more clear:   actionprogs-> action_programs auth-> user_authenticators authenticators-> rule_authenticators cloudcredentials-> cloud_api_credentials events-> event_types extra-> extra_files flipper-> traffic_ip_groups groups-> user_groups scripts-> monitor_scripts services-> glb_services settings.cfg-> global_settings slm-> service_level_monitors vservers-> virtual_servers zxtms-> traffic_managers   New Resource   One new resource, "custom" has been added to support the new Custom Configuration Sets feature.  This allows arbitrary name:value configuration pairs to be stored in the Traffic Manager configuration system. As part of the Traffic Manager configuration, this data is replicated across a cluster and is accessible using the REST API, SOAP API and ZCLI.  All data structures supported by the Stinray REST API are also supported for Custom Configuration Sets.  Please see the REST API Guide for more information.
View full article
This article discusses how to use the Stingray Traffic Manager's RESTful Control API with PHP.  The are different options for accessing the RESTful API with PHP, but I decided to create my own PHP REST Client, STMRESTClient, to make working with RESTful API easier, and this has been used in the PHP examples. Instructions for using STMRESTClient and the code can be found here: Tech Tip: A Stingray Traffic Manager REST Client for PHP   Resources   The RESTful API gives you access to the Stingray Configuration and statistics, presented in the form of resources.  The format of the data exchanged using the Stingray RESTful API will depend on the type of resource being accessed:   Data for Configuration ans Status Resources, such as Virtual Servers and Pools are exchanged in JSON format using the MIME type “application/json”, so when getting data for a resource with a GET request, the data will be returned in JSON format and must be deserialized or decoded into a PHP data structure.  When adding or changing a resource with a PUT request, the data must be serialized or encoded from a PHP data structure into JSON format.  Files, such as rules and those in the extra directory are exchanged in raw format using the MIME type “application/octet-stream”.   Working with JSON and PHP   PHP provides functions for JSON encoding and decoding. To take a PHP data structure and encode it into JSON format, use json_encode() and to decode a JSON formatted string into a PHP structure, use json_decode(). If using STMRestClient, JSON encoding and decoding will be done for you.   Working with the RESTful API and PHP   The base form of the URI for the Stingray RESTful API is:   https://<host>:<port>/api/tm/<version>   followed by paths to the different resource types:   For configuration resources: /config/active/ For statistic resources: /status/<host>/information/ "local_tm" can be used in place of <host> For information resources: /status/<host>/statistics/ "local_tm" can be used in place of <host>   followed by a actual resource, so for example to get a list of all the pools from the Stingray instance, stingray.example.com, it would be:   https://stingray.example.com:9070/api/tm/2.0/config/active/pools   and to get the configuration information for the pool, “testpool” it would be:   https://stingray.example.com:9070/api/tm/2.0/config/active/pools/testpool   and to get statistics for the pool "testpool", it would be:   https://stingray.example.com:9070/api/tm/2.0/status/local_tm/statistics/pools/testpool   Prerequisites   If your PHP environment does not have cURL installed, you will need to install it, even if you are using STMRestClient.   If using apt (assuming apache is the web server):   sudo apt-get install php5-curl sudo service apache2 restart   Data Structures   The PHP functions json_decode and json_encode convert between JSON strings and PHP data structures.  The PHP data structure can be either an associative array or an object.  In either case, the array or object will have one element.   The key to this element will be:   'children' for lists of configuration resources.  The value will be a PHP array with each element in the array being an associative array with the key, 'name', set to the name of the resource and the key, 'href', set to the URI of the resource. 'properties' for configuration resources.  The value will be an associative array with each key value pair being a section of properties with the key being set to the name of the section and the value being an associative array containing the configuration values as key/value pairs.  Configuration values can be scalars, arrays or associative arrays. 'statistics' for statistics resources.  The value will be an associative array. 'information' for information resources.  The value will be an associative array.   Please see Feature Brief: Stingray's RESTful Control API for examples of these data structures and something like the Chrome REST Console can be used to see what the actual data looks like.   Read More   The REST API Guide in the Stingray Product Documentation Feature Brief: Stingray's RESTful Control API Collected Tech Tips: Using the RESTful Control API Tech Tip: A Stingray Traffic Manager REST Client for PHP
View full article
The following code uses Stingray's RESTful API to delete a pool.  The code is written in TrafficScript.  This rule deletes the "tstest" pool created by the stmrest_addpool example.  To delete a resource you do a HTTP DELETE on the URI for the resource.  If the delete is successful a 204 HTTP status code will be returned.  Subroutines in stmrestclient are used to do the actual RESTful API calls.  stmrestclient is attached to the article Tech Tip: Using the RESTful Control API with TrafficScript - Overview. stmrest_deletepool ################################################################################ # stmrest_deletepool # # This rule deletes the pool "tspool". # # To run this rule add it as a request rule to an HTTP Virtual Server and in a # browser enter the path /rest/deletepool. # # It uses the subroutines in stmrestclient ################################################################################ import stmrestclient; if (http.getPath() != "/rest/deletepool") break; $pool = "tspool"; $resource = "pools/" . string.escape($pool); $accept = "json"; $html = "<br><b>Delete Pool " . $pool . "</b><br><br>"; # Check to make sure that the Pool exists $response = stmrestclient.stmRestGet($resource, $accept); if ($response["rc"] == 1) {    $response = stmrestclient.stmRestDelete($resource);    if ($response["rc"] == 1) {       $html = $html . "Pool " . $pool . " deleted";    } else {       $html = $html . "There was an error deleting pool " . $pool . ": " . $response['info'];    } } else {    if ($response['status'] == 404) {       $html = $html . "Pool " . $pool . " not found";    } else {       $html = $html . "There was an error getting the configuration for pool " . $pool . ": " . $response['info'];    } } http.sendResponse("200 OK", "text/html", $html, ""); Running the example This rule should be added as a request rule to a Virtual Server and run with the URL: http://<hostname>/rest/deletepool Pool tstest deleted Read More Stingray REST API Guide in the Stingray Product Documentation Tech Tip: Using the RESTful Control API with TrafficScript - Overview Feature Brief: Stingray's RESTful Control API Collected Tech Tips: Using the RESTful Control API
View full article
The following code uses Stingray's RESTful API to add a pool.   The code is written in TrafficScript. This rule creates a new pool, "tstest", first doing a GET to make sure the pool doesn't already exist, and if the pool doesn't exist, the pool is created by doing a PUT with just the minimum data needed to create a pool.  In this case the rule creates a properties hash with just one node.  All other values will get default values when Stingray creates the pool.  Subroutines in stmrestclient are used to do the actual RESTful API calls.  stmrestclient is attached to the article Tech Tip: Using the RESTful Control API with TrafficScript - Overview. stmrest_addpool ################################################################################ # stmrest_addpool # # This rule adds the pool "tspool" with the node "192.168.168.135:80" # # To run this rule add it as a request rule to an HTTP Virtual Server and in a # browser enter the path /rest/addpool. # # It uses a set of subroutines in stmrestclient ################################################################################ import stmrestclient; if (http.getPath() != "/rest/addpool") break; $pool = "tspool"; $node = "192.168.168.135"; $resource = "pools/" . string.escape($pool); $accept = "json"; $html = "<br><b>Add Pool " . $pool . "</b><br><br>"; # Check to make sure that the Pool doesn't already exist $response = stmrestclient.stmRestGet($resource, $accept); if ($response["rc"] == 1) {    $html = $html . "Pool " . $pool . " already exists"; } else {    $poolConfig =["properties" => ["basic" => ["nodes" => [$node . ":80"]]]];    $response = stmrestclient.stmRestPut($resource, $accept, $poolConfig);    if ($response["rc"] == 1) {       $html = $html . "Pool " . $pool . " created";    } else {       $html = $html . "There was an error creating pool " . $pool . ": "  . $response['info'];    } } http.sendResponse("200 OK", "text/html", $html, ""); Running the example This rule should be added as a request rule to a Virtual Server and run with the URL: http://<hostname>/rest/addpool Pool tstest added Notes The only difference between doing a PUT to change a resource and a PUT to add a resource is the HTTP status code returned.  When changing a resource, 200 is the expected status code and when adding a resource, 201 is the expected status code. Read More Stingray REST API Guide in the Stingray Product Documentation Tech Tip: Using the RESTful Control API with TrafficScript - Overview Feature Brief: Stingray's RESTful Control API Collected Tech Tips: Using the RESTful Control API
View full article
The following code uses Stingray's RESTful API to enable or disabled a specific Virtual Server.   The code is written in TrafficScript.  This rule checks to see if the Virtual Server "test vs" is enabled and if it is, it disables it and if it is disabled, it enables it.  A GET is done to retrieve the configuration data for the Virtual Server and the "enabled" value in the "basic" properties section is checked.  This is a boolean value, so if it is true it is set to false and if it is false it is set to true. The changed data is then sent to the server using a PUT.  Subroutines in stmrestclient are used to do the actual RESTful API calls.  stmrestclient is attached to the article Tech Tip: Using the RESTful Control API with TrafficScript - Overview. stmrest_startstopvs ################################################################################ # stmrest_startstopvs # # This rule toggles the enabled status of virtual server "test vs". # # To run this rule add it as a request rule to an HTTP Virtual Server and in a # browser enter the path /rest/startstopvs. # # It uses the subroutines in stmrestclient ################################################################################ import stmrestclient; if (http.getPath() != "/rest/startstopvs") break; $vs = "test vs"; $resource = "vservers/" . string.escape($vs); $accept = "json"; $html = "<br><b>Toggle the status of Virtual Server " . $vs . "</b><br><br>"; $response = stmrestclient.stmRestGet($resource, $accept); if ($response["rc"] == 1) {    $vsConfig = $response["data"];    if ($vsConfig["properties"]["basic"]["enabled"]) {       $vsConfig["properties"]["basic"]["enabled"] = false;    } else {       $vsConfig["properties"]["basic"]["enabled"] = true;    }    $response = stmrestclient.stmRestPut($resource, $accept, $vsConfig);    if ($response["rc"] == 1) {       if ($vsConfig["properties"]["basic"]["enabled"]) {          $html = $html . $vs . " is Disabled.  Enable it";       } else {          $html = $html . $vs . " is Enabled.  Disable it.";       }    } else {       $html = $html . "There was an error updating the virtul server configuration for " . $vs . ": " . $response["info"];    } } else {    if ($response["status"] == 404) {       $html = $html . "Virtual Server " . $vs . " not found";    } else {       $html = $html . "There was an error getting the virtul server configuration for " . $vs . ": " . $response["info"];    } } http.sendResponse("200 OK", "text/html", $html, ""); Running the example This rule should be added as a request rule to a Virtual Server and run with the URL: http://<hostname>/rest/startstopvs test vs is enabled. Disable it. Notes This rule it is sending only the "enabled" value to the server by creating a new hash with just this value in the "basic" properties section.  Alternatively, the entire Virtual Server configuration could have been returned to the server with just the "enabled" value changed.  Sending just the data that has changed reduces the chances of overwriting another user's changes if multiple programs are concurrently accessing the RESTful API. Read More Stingray REST API Guide in the Stingray Product Documentation Tech Tip: Using the RESTful Control API with TrafficScript - Overview Feature Brief: Stingray's RESTful Control API Collected Tech Tips: Using the RESTful Control API
View full article
The following code uses Stingray's RESTful API to list all the pools defined for a cluster. The code is written in TrafficScript. This example has more extensive comments then the other examples and most of these are applicable to all the examples. This rule does a single GET request for the list of pools and then loops through that list, each element of which is a hash, and outputs the pool name to a web page. A subroutine in stmrestclient is used to do the actual RESTful API call.  stmrestclient is attached to the article Tech Tip: Using the RESTful Control API with TrafficScript - Overview. stmrest_listpools ################################################################################ # stmrest_listpools # # This rule gets the lists of pools and creates a webpage to display the list. # # To run this rule add it as a request rule to an HTTP Virtual Server and in a # browser enter the path /rest/listpools. # # It uses the subroutines in stmrestclient ################################################################################ import stmrestclient ; # Only run the rule for the path /rest/listpools if ( http.getPath ( ) != "/rest/listpools" ) break ;    $resource = "pools" ; # The configuration resource $accept = "json" ; # The format of the response $html = "<b>Pools:</b><br><br>\n" ; # Do the HTTP GET to get the lists of pools $response = stmrestclient.stmRestGet ( $resource , $accept ) ; if ( $response [ "rc" ] == 1 ) {    # Since we are getting a list of pools we expect the first element to be 'children'     $pools = $response [ "data" ] [ "children" ] ;    # The value for the key 'children' will be a hash containing an array for each pool     # with the key, 'name', set to the name of the pool and the key, 'href', set to the    # URL of the pool     foreach ( $pool in $pools ) {       $html = $html . $pool [ "name" ] . "<br>" ;    } } else {    # Either the attempt to connect to the Stingray REST API failed, or the request had an    # error.  $response['info'] should provide some information.  If The connection attempt    # failed, the most likely reasons for this are that the port, username or password for    # the Stingray REST API are incorrect, or the REST API is disabled.   The above request    # should not have an error, but the most common reason for a GET error is that the    # incorrect resource is specified.     $html = $html . "There was an error getting the pool list: " . $response [ 'info' ] ; } http.sendResponse ( "200 OK" , "text/html" , $html , "" ) ; Running the example This rule should be added as a request rule to a Virtual Server and run with the URL: http:// <hostname>/rest/listpools Pools: Pool1 Pool2 Read More Stingray REST API Guide in the Stingray Product Documentation Tech Tip: Using the RESTful Control API with TrafficScript - Overview Feature Brief: Stingray's RESTful Control API Collected Tech Tips: Using the RESTful Control API
View full article
This article discusses how to use Stingray's RESTful Control API with TrafficScript, Stingray's scripting language.  The RESTful API can be accessed in a TrafficScript rule using the http.request.get(), http.request.put() and http.request.delete() functions, but to make programming easier I have taken advantage of the support in TrafficScript for subroutine libraries to create a TrafficScript REST client that contains subroutines that be can be used by TrafficScript rules and that abstract many of the RESTful API programming details.   Resources   The RESTful API gives you access to the Stingray Configuration, presented in the form of resources.  The format of the data exchanged using the Stingray RESTful API will depend on the type of resource being accessed:   Data for Configuration Resources, such as Virtual Servers and Pools are exchanged in JSON format using the MIME type “application/json”, so when getting data on a resource with a GET request the data will be returned in JSON format and must be deserialized or decoded into a TrafficScript data structure.  When adding or changing a resource with a PUT request the data must be serialized or encoded from a TrafficScript data structure into JSON format. Files, such as rules and those in the extra directory are exchanged in raw format using the MIME type “application/octet-stream”.   Working with JSON and TrafficScript   TrafficScript provides functions for JSON serializing and deserializing.  To take a TrafficScript data structure and serialize it into JSON format, use json.serialize() and to deserialize a JSON formatted string into a TrafficScript structure, use json.deserialize().   Working with the RESTful API and TrafficScript   To make the programming easier, the program examples that follow utilize the stmrestclient REST client. To use stmrestclient in a TrafficScript rule you include it with the import statement:   import stmrestclient;   The hostname of the STM to access, the base URI path and the userid and password are defined in stmrestclient and can be changed there if needed.  The default is for the hostname to be the same as the STM on which the rule is running and the userid and password are both "admin".  There are three subroutines for use in rules:   stmRestGet(resourcepath, type) stmRestPut(resourcepath, type, data) stmRestDelete(resourcepath)   resourcepath identifies the part of the URI path to the resource that needs to be added to the base URI path.  The base URI path is "/api/tm/1.0/config/active/". So for example, to get a list of pools, resourcepath would be set to "pools" or to work with the pool "testpool", resourcepath would be set to "pools/testpool", resulting in a URI path of "/api/tm/1.0/config/active/pools" and "/api/tm/1.0/config/active/pools/testpool" respectively.   type identifies the format of the data and should be set to "json", "file" or "any".  These will be translated to “application/json”, “application/octet-stream” and “application/json, application/octet-stream” respectively.   data should be a TrafficScript hash for configurations resources or a string for files.   Each subroutine returns the following hash:   rc: Return code.  1=OK, 0=Error info: "OK" if OK, and an error message if there was an error data: The data from the response.  A hash containing one element, "children" for lists or "properties" for configuration data. For files it is the file. content-type: The Content-Type header of the response, either “application/json” or “application/octet-stream” status: The HTTP response code   Data Structures   Responses that return a JSON formatted string will be deseriailized by the stmrestclient subroutines into a hash that always contains one element. The key to this element will be:   "children" for lists of resources.  The value will be an array with each element in the array being a hash with the key, "name", set to the name of the resource and the key, "href", set to the URI of the resource. "properties" for configuration resources.  The value will be a hash with each key value pair being a section of properties with the key being set to the name of the section and the value being a hash containing the configuration values as key/value pairs.  Configuration values can be scalars, arrays or hashes.   Please see Feature Brief: Stingray's RESTful Control API for examples of these data structures and tools such as the Chrome REST Console can be used to see what the actual data looks like.   Example   The following rule will get a list of all pools and create a webpage listing them:   import stmrestclient; $html = "Pools: \n"; $response = stmrestclient.stmRestclient("pools", "json"); if ($response["rc"]) { $pools = $response["data"]["children"]; foreach ($pool in $pools) { $html = $html . $pool["name"] . " "; } } else { $html = $html . "There was an error getting the pool list: " . $response['info']; } http.sendResponse("200 OK", "text/html", $html, "");   stmrestclient   The TrafficScript code for the STM REST client, stmrestclient, has been attached to this article.   Read More   The REST API Guide in the Stingray Product Documentation Feature Brief: Stingray's RESTful Control API Collected Tech Tips: Using the RESTful Control API
View full article
This article describes how to integrate SLF4J logging into the the Stingray event log when using Stingray Java extensions that make use of libraries that use the Simple Logging Facade for Java (SLF4J).  The standard SLF4J package includes various jar files that can be used to control logging, such as slf4j-nop-1.7.5.jar that suppresses all messages and slf4j-simple-1.7.5.jar that will output all messages to the Stingray Event Log.  By uploading one and only one of these files, you can control whether or not messages are output to the Stingray Event Log.  The issue with using slf4j-simple-1.7.5.jar is that it is unaware of Stingray's log levels and logging infrastructure so all messages show up as warnings:     To address this we need to create our own StingrayLogger class that can map log messages to the Stingray log levels. The implementation in this article is based off of slf4j-simple.  To create a StingrayLogger class we also need StingrayLoggerBinder, StingrayMarkerBinder, StingrayMDCBinder and StingrayLoggerFactory classes.  The source files and jar file are attached to this article.  In order to compile these classes yourself, you will need to obtain zeus.java.jar from $ZEUSHOME/zxtm/lib on your Stingray Traffic Manager and slf4j-api-1.7.5.jar.  These class files should be in a jar under org.slf4j.impl.     The system property, org.slf4j.stingrayLogger.logLevel, specifies what level of logging to output to the Stingray event log.  Any message at that level or higher will be output, any below that level will be suppressed. The value of this property should be one of: "trace", "debug", "info", "warn", or "error". If not specified it defaults to "warn".  Stingray doesn't have trace or debug log levels, so any messages at those levels will be mapped to info.   An example of a Java Extension using the StingrayLogger class is described in WURFL - Updated
View full article
This is an update to the "Simply WURFL" article written in 2009.  Since that time, some of the underlying libraries have changed, especially with regards to logging.   What is WURFL?  WURFL stands for Wireless Universal Resource FiLe.  From the WURFL webpage: "WURFL is a Device Description Repository (DDR), i.e. a framework that enables applications to map HTTP requests to a description of the capability of the mobile device that requests the page."  WURFL is licensed and maintained by ScientiaMobile, Inc. and includes a Java API so it can be used with the Stingray Traffic Manager.  It is up to the user to make sure they comply with the ScientiaMobile WURFL license.  You do not need to know Java to get these examples working since all the necessary class and jar files are either attached to this article or available at the links below, but if you do want to modify the source code, the source files are also attached.  To get started with WURFL you first need to ensure your Stingray Traffic Manager has working Java support and then download the following items.   wurfl Java API : This code has been tested with version 1.4.4.3.  You will need wurfl-1.4.4.3.jar. wurfl: The WURFL respository.  wurfl.zip which can be used as is or extracted as wurfl.xml.  This code has been tested with version 2.3.4. commons-collections: Java interfaces, implementations and utilities.  This code has been tested with version 3.2.1.  You will need commons-collections-3.2.1.jar. commons-lang: Helper utilities.  This code has been tested with version 3.1.  You will need commons-lang3-3.1.jar. sl4j: Logging framework.  This code has been tested with version 1.7.5.  You will need slf4j-api-1.7.5.jar and slf4j-noop-1.7.5.jar or slf4j-simple-1.7.5.jar depending on what you want to see with regards to logging messages.  slf4j-noop-1.7.5.jar will cause all messages to be suppressed while slf4j-simple-1.7.5.jar will cause all messages to appear in the Stingray event log as warnings.   Upload the files specified using the Calalogs > Java UI page on your Stingray Traffic Manager. Now you're all set to experiment with the following examples.   The first sample servlet is a very simple use-case of WURFL, useful as a base for your own servlet or for debugging. The intention is for it to introduce the WURFL API as it fits within the framework of Stingray Java Extensions. WURFL is typically configured using the ConfigListener model, but Stingray doesn't go as far as implementing all the nuts and bolts required by full web applications. Our WURFL servlet must perform the required initialization itself, so an init method has been implemented that sets up a WURFLManager. As much work as possible is done at servlet initialization time. Then all the doGet method needs to do is check the request against the pre-initialised WURFLManager.   The source code and the compiled class for this example, StingrayWURFLInfoServlet are attached to this article.  To compile the code yourself on the Stingray instance, after uploading the specified jar files, you can upload the source file as well and then from the $ZEUSHOME/zxtm/conf/jars directory execute the following command:   javac -cp "$ZEUSHOME/zxtm/lib/*:$ZEUSHOME/zxtm/conf/jars/* StingrayWURFLInfoServlet.java   To compile it on a different machine, you will need the following jar files that were uploaded to Stingray: commons-collections-3.2.1.jar, commons-lang3.3.1.jar, wurfl-1.4.4.3.jar as well as servlet.jar and zxtm-servlet.jar from Stingray.  These are available via links in the Stingray UI under Catalogs > Java.  Please see the Stingray Java Development Guide for more information.   To get the servlet working follow these steps:   Upload the StingrayWURFLInfoServlet.class file using Stingray Catalogs > Java UI page, leave the "Automatically create TrafficScript rule" checkbox ticked If you want devices that are not included in the standard WURFL repository, like desktop browsers, to be detected properly, a patches file can be created and uploaded to Stingray using the Catalogs > Java page.   If this file is uploaded, click on the StingrayWURFLInfoServlet link on the UI page and add the parameter "wurfl_patches" with the value of the name of the patches file, e.g., "web_browsers_patch.xml".  For details on creating a patches file see the ScientiaMobile website. Set up a Virtual Server for testing.  The pool can be set to "discard" since StingrayWURFLInfoServlet will create a response. Associate the auto-generated StingrayWURFLInfoServlet rule with the test Virtual Server as a request rule. Visit the virtual server from different browsers on different devices.   The result should be a page showing some general information about your browser at the top followed by the full table of WURFL capabilities and their values. The following screenshots show the top part of the output using a iPhone and a desktop browser:   Safari on iPhone:     Firefox on Windows:   This is fine as a demo but not particularly useful for a real world application. What we really want is a module that can export capabilities so that they can be used by TrafficScript and other extensions. We also don't want to specifically be a doGet processor, so we'll modify the servlet along the lines described in the article: Writing TrafficScript functions in Java   in Java*</a>   There are a lot of capabilities covered by WURFL, the tables in the above screenshots go on for several pages - more than 500 capabilities in all. So we'll make it possible for TrafficScript to specify what capability fields it wants.   This gives us a servlet that could be used from TrafficScript using a rule such as the following example.  This rule extracts a few values and caches them in the Global Associative Array using the user-agent as the key so that subsequent requests for the same user-agent don't require another look up.   sub checkWURFL() { $ua = http.getHeader( "User-Agent" ); if (!string.length($ua)) return; $markup = data.get("WURFL" . $ua . "preferred_markup"); $datarate = data.get("WURFL" . $ua . "max_data_rate"); $brand = data.get("WURFL" . $ua . "brand_name"); $cookie = data.get("WURFL" . $ua . "cookie_support"); $os = data.get("WURFL" . $ua . "device_os"); $osVersion = data.get("WURFL" . $ua . "device_os_version"); $isWireless = data.get("WURFL" . $ua . "is_wireless_device"); if (string.length($markup)) { log.info("Returning cached values for User-Agent: " . $ua . ", datarate: " . $datarate . ", markup: " . $markup . ", brand: " . $brand . ", cookies: " . $cookie . ", os: " . $os . ", version: " . $osVersion . ", iswireless: " . $isWireless); $1 = $markup; $2 = $datarate; $3 = $brand; $4 = $cookie; $5 = $os; $6 = $osVersion; $7 = $isWireLess; return; } # no cached values for the UA, so run it through WURFL java.run("StingrayWURFLServlet", "max_data_rate", "preferred_markup", "brand_name", "cookie_support", "device_os", "device_os_version", "is_wireless_device"); $markup = connection.data.get("preferred_markup"); $datarate = connection.data.get("max_data_rate"); $brand = connection.data.get("brand_name"); $cookie = connection.data.get("cookie_support"); $os = connection.data.get("device_os"); $osVersion = connection.data.get("device_os_version"); $isWireless = connection.data.get("is_wireless_device"); data.set("WURFL" . $ua . "preferred_markup", $markup); data.set("WURFL" . $ua . "max_data_rate", $datarate); data.set("WURFL" . $ua . "max_data_rate", $datarate); data.set("WURFL" . $ua . "brand_name", $brand); data.set("WURFL" . $ua . "cookie_support", $cookie); data.set("WURFL" . $ua . "device_os", $os); data.set("WURFL" . $ua . "device_os_version", $osVersion); data.set("WURFL" . $ua . "is_wireless_device", $isWireless); log.info("Returning fresh WURFL values for User-Agent: " . $ua . ", datarate: " . $datarate . ", markup: " . $markup . ", brand: " . $brand . ", cookies: " . $cookie . ", os: " . $os . ", version: " . $osVersion . ", iswireless:" . $isWireless); $1 = $markup; $2 = $datarate; $3 = $brand; $4 = $cookie; $5 = $os; $6 = $osVersion; $7 = $isWireless; return; } # simple case to test the checkWURFL function checkWURFL(); $html = "Max Data Rate: " . $2 . "kbps Preferred Markup: " . $1 . " Brand: " . $3 . " OS: " . $5 . " Version: " . $6 . " Cookie Support: " . $4 . " Is Wireless Device: " . $7; http.sendResponse("200", "text/html", $html, "");   The corresponding StingrayWURFLServlet source and compiled class file and slf4j-stingray.jar file are attached to this article.  The slf4j-stingray.jar file includes an implementation to properly direct slf4j messages to the Stingray Event Log. The logging code is documented here: slf4j Logging and Stingray Java Extensions . The slf4j-noop-1.7.5.jar or slf4j-simple-1.7.5.jar file uploaded for the previous example should be deleted.  To control what level of logging to output to the Stingray Event Log, a parameter, "log_level" can be set to a value (case insensitive) of "debug", "info", "warn" or "error".  If no value is set, the default is "warn".   Rather than the doGet method implemented in the StingrayWURFLInfoServlet we now have a simpler service method.   public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String[] args = (String[])request.getAttribute( "args" ); if (args == null || args.length == 0) { throw new ServletException("error: no arguments supplied"); } Device device = manager.getDeviceForRequest(request); Map capabilities = device.getCapabilities(); for (int i = 0; i < args.length; ++i) { String cap = (String)capabilities.get(args[i]); if (cap == null) { Logger.warn("Java Servlet StingrayWURFLServlet: No capability found matching: " + args[i]); } else { ((ZXTMServletRequest)request).setConnectionData( args[i], cap ); } } }   There is much more you could do with the StingrayWURFLServlet Java Extension provided in this article. Think of it as a starting point for developing your own solutions to improve the web browsing experience of your mobile users.   A few examples:   The max_data_rate value retrieved above could be used to reduce image quality or size for people with low bandwidth devices. This would result in a snappier web browsing experience for these people as there would be less data for them to retrieve over their slow links. The preferred_markup value can be used to direct clients to different backend pools based on whether they can handle XHTML, or should be served WML. The streaming_flv value can be checked to see if the device has Flash video support and can thus be sent to your full bells-and-whistles website. A scaled down version could be made for those that only have Flash Lite, which is specified by the value of flash_lite_version. Devices that don't support flash at all (such as the iPhone) can be sent to a plain HTML version of the site, or WML as in the previous bullet point.   Speaking of the iPhone, it doesn't have Flash but its browser does have excellent AJAX support. You know your site is being visited by an iPhone user using the normal iPhone web browser when model_name is "iPhone" and mobile_browser is "Safari". If there are important differences between iPhone OS releases you can also check model_extra_info ordevice_os_version for this detail. For AJAX in general there are a whole set of specific properties: ajax_manipulate_css ajax_manipulate_dom ajax_support_event_listener ajax_support_events   Up to date documentation on all the WURFL capabilities can be found on the WURFL website.   <a title=h   Any of these values can also be passed on to your backend nodes of course. You could add special headers containing the values, a cookie, or a URL argument. You could also cache browser capabilities uniquely to each device with cookies or another method of session tracking, rather than cache the capabilities based solely on the user agent. Then you could offer users the ability to override special mobile device modes.   We would love to hear your ideas and learn how we can help you in this exciting area - the opportunities are practically limitless.
View full article
The 'draining' property of a node is configured per-pool.  If the node is used in several pools and you want to prepare it to take out of service, you need to edit each pool to set it to be draining. With the Stingray UI, you can use the 'Drain a Node' and 'Undrain a Node' wizards.  These checks all pools and ensures that the node is updated in all of them. This Python script does the same thing: #!/usr/bin/python import requests import json import sys def usage():   print "Syntax: %s drain|undrain nodename:[port]" % sys.argv[0]   sys.exit( -1 ) if len( sys.argv ) != 3:   usage() action = sys.argv[1] target = sys.argv[2] if action == "drain":   print "Draining node " + target + ":" elif action == "undrain":   print "Stopping draining node " + target + ":" else:   usage() url = ' https://stingray:9070/api/tm/1.0/config/active/pools/'; client = requests.Session() client.auth = ('admin', 'admin') client.verify = 0 response = client.get(url) pools = json.loads(response.content) def nodeInList( node, list ):   for n in list:     if n.startswith( node ):       return n   return '' for pool in pools['children']:   print "  checking pool "+pool['name']   response = client.get(url+pool['name'])   data = json.loads(response.content)   matchednode = nodeInList( target, data['properties']['basic']['nodes'] )   if matchednode == '':     continue   if action == 'drain' and nodeInList( matchednode, data['properties']['basic']['draining'] ) == '':     data['properties']['basic']['draining'].append( matchednode )     print "    draining node %s in pool %s" % ( matchednode, pool['name'] )     client.put( url+pool['name'], data = json.dumps( data ), headers = {'content-type': 'application/json'} )   if action == 'undrain' and nodeInList( matchednode, data['properties']['basic']['draining'] ) != '':     data['properties']['basic']['draining'].remove( matchednode )     print "    undraining node %s in pool %s" % ( matchednode, pool['name'] )     client.put( url+pool['name'], data = json.dumps( data ), headers = {'content-type': 'application/json'} ) Use it as follows: $ ./setDraining.py drain splash.riverbed.com Draining node splash.riverbed.com:   checking pool web site pool   checking pool Forward Proxy Pool   checking pool splash     draining node splash.riverbed.com:443 in pool splash   checking pool SSH   checking pool Internal HTTPS loopback   checking pool WebSocket test   checking pool splash proxy     draining node splash.riverbed.com:443 in pool splash proxy $ ./setDraining.py undrain splash.riverbed.com Stopping draining node splash.riverbed.com:   checking pool web site pool   checking pool Forward Proxy Pool   checking pool splash     undraining node splash.riverbed.com:443 in pool splash   checking pool SSH   checking pool Internal HTTPS loopback   checking pool WebSocket test   checking pool splash proxy     undraining node splash.riverbed.com:443 in pool splash proxy
View full article
Following is a library that I am working on that has one simple design goal: Make it easier to do authentication overlay with Stingray.   I want to have the ability to deploy a configuration that uses a single line to input an authentication element (basic auth or forms based) that takes the name of an authenticator, and uses a simple list to define what resources are protected and which groups can access them.   Below is the beginning of this library.  Once we have better code revision handling in splash (hint hint Owen Garrett!! ) I will move it to something more re-usable.  Until then, here it is.   As always, comments, suggestions, flames or gifts of mutton and mead most welcome...   The way I want to call it is like this:   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import lib_auth_overlay as aaa;   # Here we challenge for user/pass $userpasswd = aaa.promptAuth401();   # extract the entered username / password into variables for clarity $username = $userpasswd [0]; $password = $userpasswd [1];   # Here we authenticate check that the user is a member of the listed group # We are using the "user_ldap" authenticator that I set up against my laptop.snrkl.org # AD domain controller. $authResult = aaa.doAuthAndCheckGroup( "user_ldap" , $username , $password , "CN=staff,CN=Users,DC=laptop,DC=snrkl,DC=org" );   # for convienience we will tell the user the result of their Auth in an http response aaa.doHtmlResponse.200( "Auth Result:" . $authResult );   here is the lib_auth_overlay that is referenced in the above element.  Please note the promptAuthHttpForm() sub routine is not yet finished...   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 sub doHtmlResponse.200 ( $message ){       http.sendResponse(          "200 OK" ,          "text/html" ,          $message ,          ""          ); }   sub challengeBasicAuth( $errorMessage , $realm ){       http.sendResponse(          "401 Access Denied" ,          "text/html" ,          $errorMessage ,          "WWW-Authenticate: Basic realm=\"" . $realm . "\"" );   }   sub basicAuthExtractUserPass( $ah ){ #// $ah is $authHeader,          $enc = string.skip( $ah , 6 );          $up = string. split (string.base64decode( $enc ), ":" );          return $up ;       }   sub doAuthAndGetGroups ( $authenticator , $u , $p ){       $auth = auth.query( $authenticator , $u , $p );       if ( $auth [ 'Error' ] ) {          log .error( "Error with authenticator " . $authenticator . ": " . $auth [ 'Error' ] );          return "Authentication Error" ;       } else if ( ! $auth [ 'OK' ] ) { #//Auth is not OK          # Unauthorised          log . warn ( "Access Denied - invalid username or password for user: \"" . $u . "\"" );          return "Access Denied - invalid username or password" ;       } else if ( $auth [ 'OK' ] ){          log .info( "Authenticated \"" . $u . "\" successfully at " . sys. localtime . format ( "%a, %d %b %Y %T EST" ));          return $auth [ 'memberOf' ];       }   }   sub doAuthAndCheckGroup ( $authenticator , $u , $p , $g ){       $auth = auth.query( $authenticator , $u , $p );       if ( $auth [ 'Error' ] ) {          log .error( "Error with authenticator \"" . $authenticator . "\": " . $auth [ 'Error' ] );          return "Authentication Error" ;       } else if ( ! $auth [ 'OK' ] ) { #//Auth is not OK          # Unauthorised          log . warn ( "Access Denied - invalid username or password for user: \"" . $u . "\"" );          return "Access Denied - invalid username or password" ;       } else if ( $auth [ 'OK' ] ){          log .info( "Authenticated \"" . $u . "\" successfully at " . sys. localtime . format ( "%a, %d %b %Y %T EST" ));          if ( lang.isArray( $auth [ 'memberOf' ])){ #//More than one group returned             foreach ( $group in $auth [ 'memberOf' ]){                if ( $group == $g ) {                   log .info( "User \"" . $u . "\" permitted access at " . sys. localtime . format ( "%a, %d %b %Y %T EST" ));                   return "PASS" ;                   break;                } else {                   log . warn ( "User \"" . $u . "\" denied access - not a member of \"" . $g . "\" at " . sys. localtime . format ( "%a, %d %b %Y %T EST" ));                }           }             #// If we get to here, we have exhausted list of groups with no match             return "FAIL" ;            } else { #// This means that only one group is returned             $group = $auth [ 'memberOf' ];                if ( $group == $g ) {                   log .info( "User \"" . $u . "\" permitted access " . sys. localtime . format ( "%a, %d %b %Y %T EST" ));                   return "PASS" ;                   break;                } else {                   log . warn ( "User \"" . $u . "\" denied access - not a member of \"" . $g . "\" at " . sys. localtime . format ( "%a, %d %b %Y %T EST" ));                   return "FAIL" ;                }        }     } }   sub promptAuth401(){       if (!http.getHeader( "Authorization" )) { #// no Authorization header present, lets challenge for credentials          challengeBasicAuth( "Error Message" , "Realm" );       } else {          $authHeader = http.getHeader( "Authorization" );          $up = basicAuthExtractUserPass( $authHeader );            return $up ;     } }   sub promptAuthHttpForm(){       $response = "<html> <head>Authenticate me...</head> <form action=/login method=POST> <input name=user required> <input name=realm type=hidden value=stingray> <input name=pass type=password required> <button>Log In</button> </form> </html>" ;       doHtmlResponse.200( $response ); }  
View full article
Why write a health monitor in TrafficScript?   The Health Monitoring capabilities (as described in Feature Brief: Health Monitoring in Stingray Traffic Manager) are very comprehensive, and the built-in templates allow you to conduct sophisticated custom dialogues, but sometimes you might wish to resort to a full programming language to implement the tests you need.   Particularly on the Stingray Virtual Appliance, your options can be limited.  There's a minimal Perl interpreter included (see Tech Tip: Running Perl code on the Stingray Virtual Appliance), and you can upload compiled binaries (Writing a custom Stingray Health Monitor in C) and shell scripts.  This article explains how you can use TrafficScript to implement health monitors, and of course with Java Extensions, TrafficScript can 'call out' to a range of third-party libraries as well.   Overview   We'll implement the solution using a custom 'script' health monitor.  This health monitor will probe a virtual server running on the local Stingray (using an HTTP request), and pass it all of the parameters relevant to the health request.   A TrafficScript rule running on the Stingray can perform the appropriate health check and respond with a 'PASS' (200 OK) or 'FAIL' (500 Error) response.   The health monitor script   The health monitor script is straightforward and should not need any customization.  It will take its input from the health monitor configuration.   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #!/bin/sh  exec $ZEUSHOME /perl/miniperl -wx $0 ${1+ "$@" }       if 0;       #!/usr/bin/perl  #line 7       BEGIN{          # Pull in the Stingray (Zeus) libraries for HTTP requests          unshift @INC , "$ENV{ZEUSHOME}/zxtmadmin/lib/perl" , "$ENV{ZEUSHOME}/zxtm/lib/perl" ;  }       use Zeus::ZXTM::Monitor qw( ParseArguments MonitorWorked MonitorFailed Log );  use Zeus::HTMLUtils qw( make_query_string );  use Zeus::HTTP;       my %args = ParseArguments();       my $url = "http://localhost:$args{vsport}$args{path}?" .make_query_string( %args );  my $http = new Zeus::HTTP( GET => $url );  $http ->load();       Log ( "HTTP GET for $url returned status: " . $http ->code() );       if ( $http ->code() == 200 ) {      MonitorWorked();  } else {      MonitorFailed( "Monitor failed: " . $http ->code() . " " . $http ->body() );  }   Upload this to the Monitor Programs of the Extra Files section of the catalog, and then create an "External Program Monitor" based on that script.  You will need to add two more configuration parameters to this health monitor configuration:   vsport: This should be set to the port of the virutal server that will host the trafficscript test path: This is optional - you can use it if you want to run several different health tests from the trafficscript rule   Your configuration should look something like this:   The virtual server   Create an HTTP virtual server listening on the appropriate port number (vsport).  You can bind this virtual server to localhost if you want to prevent external clients from accessing it.   The virtual server should use the 'discard' pool - we're going to add a request rule that always sends a response, so there's no need for any backend nodes.   The TrafficScript Rule   The 'business end' of your TrafficScript health monitor resides in the TrafficScript rule.  This rule is invoked every time the health monitor script is run, and it is given the details of the node which is to be checked.   The rule should return a 200 OK HTTP response if the node is OK, and a different response (such as 500 Error) if the node has failed the test.   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 $path = http.getPath(); # Use 'path' if you would like to publish                           # several different tests from this rule       $ip = http.getFormParam( "ipaddr" );  $port = http.getFormParam( "port" );  $nodename = http.getFormParam( "node" );       # We're going to test the node $nodename on $ip:$port  #  # Useful functions include:  #   http.request.get/put/post/delete()  #   tcp.connect/read/write/close()  #   auth.query()  #   java.run()            sub Failed( $msg ) {      http.sendResponse( 500, "text/plain" , $msg , "" );  }       # Let's run a simple GET  $req = 'GET / HTTP/1.0  Host: www.riverbed.com       ';  $timeout = 1000; # ms  $sock = tcp. connect ( $ip , $port , $timeout );  tcp. write ( $sock , $req , $timeout );  $resp = tcp. read ( $sock , 102400, $timeout );       # Perform whatever tests we want on the response data.   # For example, it should begin with '200 OK'       if ( ! string.startsWith( $resp , "HTTP/1.1 200 OK" ) ) {      Failed( "Didn't get expected response status" );  }       # All good  http.sendResponse( 200, "text/plain" , "" , "" );  
View full article
If you wish to use Stingray to inspect or modify the headers or body of SMTP messages, you need to write some TrafficScript to do so.   Your TrafficScript code will run on a Server First protocol, and you will need to detect the start and end of each SMTP message (request and response) so that your code is executed correctly and you don't stall the connection by waiting for client (or server) data that will never arrive.  This article explains how to do this, and presents an example TrafficScript rule that can be used as the basis of more advanced SMTP rules.   Background - salient details of SMTP   SMTP is a server first TCP protocol, usually run on port 25. Lines are terminated by carriage return and line feed (CRLF). A typical SMTP conversation proceeds as follows:       Client                                  Server   1. Opens connection to server                                           2. Server sends banner   3. sends EHLO ...                                           4. Server acknowledges/rejects   # Header processing   5. sends MAIL FROM: someone@example.com                                           6. Server acknowledges/rejects   7. sends RCPT TO: other@example.com                                           8. Server acknowledges/rejects   9. sends DATA                                           10. Server acknowledges   10. Client sends body terminated by '.' on new line                                           11. Server acknowledges   (Client optionally sends another email, by returning to step 5., otherwise sends "QUIT" and server closes connection)   It's important to note the following:   The protocol is server first (use the server_first banner setting in Stingray), so a proxy must send the server's banner before waiting for a request from the client. Each request or response is terminated by CRLF. However, once the server has acknowledged the client's "DATA" request, the client continues to send CRLF terminated lines, without waiting for any response from the server, until it sends the final '.' Real world clients may not wait for a response from the server before sending the next header line. It's quite usual to see a client send all the headers in the packet it sends at step 5. The proxy must handle this situation as well as the conventional alternating request/response sequence of events.   Synchronizing SMTP with TrafficScript   To handle SMTP, we need a TrafficScript rule that creates a state-machine with two states:   State 1. Reading Headers: Read the request a line at a time, sending each line on to the server. If the request is "DATA", then transition to state 2.   State 2. Reading Body: Read the request a line at a time, but don't send it to the server straight away. Instead buffer the request until the entire body has been received. Once the whole body has been received, send the body and transition to state 1, ready to receive another email or another command, such as QUIT.   Since our TrafficScript rule is going to be executed every time a new line of data is received from the client, not just once per email sent, we need to keep track of the state between invocations of the rule. TrafficScript provides connection.data.set() and connection.data.get() to set flags that will let us keep track of which state we are in. As their names suggest, these commands allow you to set and read variables that persist as long as the TCP connection they are associated with.   Multiple lines might arrive in the same packet from the client, even though we only wish to pass them to the server one at a time. We therefore need to indicate to Stingray how to split the data into individual lines. We can do this using request.endsWith() , which takes a single argument, a regular expression that defines the end of the current request. Any data that comes after this will be kept for the next time the request rule is run.   Putting this together, the following is a request rule that will synchronise SMTP. There are three places in it from which you could hook in code to inspect/modify the headers and body on the way through.   # SMTP Synchronizer - Stingray Request Rule if (connection.data.get("BODY_COMPLETE")) { # We've finished one email, so reset the state flags, in case # another email is on its way. connection.data.set ("DATA_RECEIVED", 0); connection.data.set ("BODY_COMPLETE", 0); } if (! connection.data.get("DATA_RECEIVED")) { # State 1: Reading Headers # We haven't seen "DATA" yet, so process headers, line by line # Read the next line of the request into $request $request = request.endsWith("\r\n"); if (string.regexmatch($request, "^DATA\r\n")) { # DATA seen - set the DATA_RECEIVED flag to switch into # the Reading Body state connection.data.set("DATA_RECEIVED", 1); } #-------------------------------------------------- # INSPECTION/MODIFICATION HOOK # USE $request TO MODIFY/INSPECT HEADERS HERE #-------------------------------------------------- } else { # State 2: Reading Body # Keep reading the body until we see the end of it $line = ""; $next = 0; # The regular expression in the next line matches the # "end of body" indicator (a full stop on a line on its own) while ( ! string.regexmatch($line, "^\\.\r\n")) { $line = request.getline("\r\n", $next); $next = $1; #---------------------------------------------------- # INSPECTION/MODIFICATION HOOK # USE $line TO INSPECT/MODIFY BODY LINE BY LINE HERE #---------------------------------------------------- $request = $request . $line; } #-------------------------------------------------- # INSPECTION/MODIFICATION HOOK # USE $request to INSPECT/MODIFY WHOLE BODY HERE #-------------------------------------------------- # We've read the whole body, so set the BODY_COMPLETE flag connection.data.set("BODY_COMPLETE", 1); } # Send the request on to the back-end server. # At this point $request will contain either a single header line, or # the entire body, depending on which state we are in. request.set($request);   You should note the difference in behavior of request.endsWith() and request.getline() . Whilst they both return some data from the request, request.endsWith() tells Stingray that this is the end of this request, and no more data should be read from the client until after the request has been sent to the server and a response received. request.getline() , on the other hand, merely reads some data from the client without ending the request, and so can be called multiple times within the same invocation of a request rule. If no data is available when request.getline() is called, processing will pause until the client sends some. We can thus use it to read and buffer the body. If we tried to use request.endsWith() instead, then the connection would stall. Stingray would read the first line of the body, send it to the server, then pause waiting for a response from the server, which would never come.   The condensed version   If you don't want to perform line by line inspection or modification of the body, you can condense the above rule into the more elegant one below. This uses request.endswith() to read both the headers and the body, with only a single connection.data variable that both tracks the state and supplies the appropriate regular expression to request.endswith() .   # SMTP Synchronizer Condensed- ZXTM Request Rule # State is tracked in the connection.data variable "STATE", which is # set to one of the following two constants. The constants themselves # are the regular expressions supplied as arguments to # request.endsWith() to define the end of a request in each state. $STATE_HEADERS = "\r\n"; $STATE_BODY = "\r\n\\.\r\n"; # Initialise STATE if this is the first request on a new connection if (! connection.data.get("STATE")) { connection.data.set("STATE", $STATE_HEADERS); } $request = request.endsWith(connection.data.get("STATE")); #### STATE SWITCHING # We read the whole body in one go, so if we've got to here, and the # state is STATE_BODY, then switch back to reading headers. if (connection.data.get("STATE") == $STATE_BODY) { connection.data.set("STATE", $STATE_HEADERS); } else if (string.regexmatch($request, "^DATA\r\n")) { # Whereas if we were reading headers, and see DATA, we should # prepare ourselves for the body connection.data.set("STATE", $STATE_BODY); } #### END STATE SWITCHING #---------------------------------------------------------------------- # INSPECTION/MODIFICATION HOOK # USE $request to INSPECT/MODIFY WHOLE BODY OR INDIVIDUAL HEADERS HERE #---------------------------------------------------------------------- request.set($request);   For more details and examples of this synchronization technique, you can refer to the following resources:   Feature Brief: Server First, Client First and Generic Streaming Protocols Building a load-balancing MySQL proxy with TrafficScript
View full article