Table Of Contents
Tutorial Samples
Definition File Content of Health Monitor
Samples Used for Integrating a User Script
Content of the read_data_records() Method
Content of the send_cli_request() Method
Example of the Finished Script Content
Samples Used for a Standalone User Script
Content of the Main CLI Parsing Routine
Content of the node_xml_parse.pm Module
Tutorial Samples
This appendix provides the following tutorial samples:
•
Definition File Content of Health Monitor
•
Samples Used for Integrating a User Script
•
Samples Used for a Standalone User Script
Definition File Content of Health Monitor
The following example is the definition file from the existing Health Monitor application, which is <version>/definitions/elementbrowser/HealthMonitor.xml.
<Preferences Application="HealthMonitor">
<Version Major="1" Minor="0"/>
<TablePreferences Record="//Entity/CardHealthRecord"
<Property Name="EntityID" Display="NodeID" RefreshInterval="0"
<Property Name="UsedOverTotalMemoryRatio"
Display="Used/Total Memory"
Presenter="HorizontalBar"
RefreshInterval="0" Visible="true">
<DisplayProperties InTableCapable="true" OutTableCapable="false">
<HorizontalBarProperties HorizontalBarType="Binary">
<BarThreshold ThresholdValue="0.9" BarColor="RED"
ThresholdDescription="Threshold"
</HorizontalBarProperties>
<Property Name="EmergencyAlarmCount" Display="Emer"
Tooltip="Launch Active Alarm Viewer for associated card/alarm level"
LinkToApp="ActiveAlarmViewer"
RefreshInterval="0" Visible="true">
<Property Name="AlertAlarmCount" Display="Alert"
Tooltip="Launch Active Alarm Viewer for associated card/alarm level"
LinkToApp="ActiveAlarmViewer" RefreshInterval="0" Visible="true">
<Property Name="CriticalAlarmCount" Display="Crit"
Tooltip="Launch Active Alarm Viewer for associated card/alarm level"
LinkToApp="ActiveAlarmViewer" RefreshInterval="0"
<Property Name="ErrorAlarmCount" Display="Error"
Tooltip="Launch Active Alarm Viewer for associated card/alarm level"
LinkToApp="ActiveAlarmViewer" RefreshInterval="0" Visible="true">
<Property Name="WarningAlarmCount" Display="Warn"
Tooltip="Launch Active Alarm Viewer for associated card/alarm level"
LinkToApp="ActiveAlarmViewer" RefreshInterval="0" Visible="true">
<Property Name="CharacterDisplay" Display="Char" RefreshInterval="0"
<Property Name="AdminUpOverDown" Display="AdminUp/Down"
Tooltip="Launch Interface Viewer for associated card"
LinkToApp="InterfaceQuickViewer" RefreshInterval="0" Visible="true">
<Property Name="LineUpOverDown" Display="LineUp/Down"
Tooltip="Launch Interface Viewer for associated card"
LinkToApp="InterfaceQuickViewer" RefreshInterval="0" Visible="true">
<Property Name="ModelName" Display="Model" RefreshInterval="0" Visible="true">
<Property Name="HardwareRevision" Display="HW Rev" RefreshInterval="0"
<Property Name="SoftwareRevision" Display="SW" RefreshInterval="0"
<Property Name="FirmwareRevision" Display="FW" RefreshInterval="0"
Samples Used for Integrating a User Script
The following samples are presented for integrating a user script:
•
Content of the read_data_records() Method
•
Content of the send_cli_request() Method
•
Example of the Finished Script Content
Content of the read_data_records() Method
The following example shows the content of the read_data_records() method from the cwitalk.pm module:
my $inputRecord = start_data_read( $param );
# $input is the encoded string read from STDIN
$input = $inputRecord->[1];
my $start_tag = SIG_CWI_DATA;
my $end_tag = SIG_CWI_DATA_END;
# Now peel off the <SIG_CWI_DATA> and </SIG_CWI_DATA> tags
if ( $input =~ /^$start_tag(.*?)$end_tag$/ ){
return [1, "Invalid record received: $input"];
# Split into rows using "/" as delimiter
my @aRows = split /\//, $input;
# Split first row into columns using "$" as delimiter
# First row contains all column names
# First row has all column names
my @aColumnNames = split /\$/, $aRows[0];
# Every data token is URL-encoded, so we now decode each token
for ( my $i = 0; $i < @aColumnNames; $i++ ) {
$aColumnNames[$i] = unescape( $aColumnNames[$i] );
# Split all other rows into columns using "$" as delimiter
# Other rows contains data
for ( my $i = 1; $i < @aRows; $i++ ){
my @aTokens = split /\$/, $aRows[$i];
for ( my $i = 0; $i < @aTokens; $i++ ){
if ( defined $aTokens[$i] ){
$aTokens[$i] = unescape( $aTokens[$i] );
push @aDataRows, \@aTokens;
# Now we have the column names and values, create a container data structure
my $c = new container( \@aColumnNames, \@aDataRows );
Content of the send_cli_request() Method
The following example shows the content in the send_cli_request() method in the cwitalk.pm module:
sub send_cli_request( $$ )
my $outputString = encode_cli_request( $neName, $request );
# write the encoded string to STDOUT
printout_ref( \$outputString );
# Now wait for response from CWI. This read is blocking
my $sendResponse = read_data();
# Parse the encoded response to get the CLI output
my $ret = parse_pass_through_response( SIG_CWI_SEND_CLI,
Example of the Finished Script Content
The following example shows the finished script content:
# Basic script to demonstrate Integrated script capability
# This is elementbrowser location in CWI installation
# Must replace <CWI_DIR_LOCATION> with real CWI installation directory
# Must replace <MaxVersion.MinVersion> with version number
# For instance after replacement the quoted value can be the following:
# "C:/Document and Settings/userid/cwi/versions/17.1/definitions/elementbrowser/";
use constant LOCATION =>
"<CWI_DIR_LOCATION>/versions/<MaxVersion.MinVersion>/definitions/elementbrowser/";
# This is the directory where both the cwitalk.pm and container.pm are located.
use lib LOCATION . 'scripts/perl/lib';
use lib LOCATION . 'examples/integrated';
# Logfile name, please make sure the directory exists
# To deploy the script on another platform, modify this setting accordingly
use constant LOGFILE_NAME => LOCATION . '/examples/integrated/scriptlog.txt';
use ipv4_interface_brief;
# Initialize our logfile first
my $logFileHandle = new IO::File( LOGFILE_NAME, "w" );
if ( !defined $logFileHandle )
die( "Failed to open log file LOGFILE_NAME for writing!" );
my ( $logfh, $msg ) = @_;
print $logfh "<LOG>: $msg\n";
# $input_container: container with fields 'NEName', 'InterfaceName'
# [1, error_message] on error
# [0, $container] on success. container column names: IPv4Address
my ( $aRouters, $input_container ) = @_;
my $cli = 'show ipv4 interface brief';
# key: $ne_name + ^ + $interface_name
# Send CLI to each router
foreach my $ne_name ( @$aRouters )
my_log( $logFileHandle, "RouterName: $ne_name" );
my $cli_ret = cwitalk::send_cli_request( $ne_name, $cli );
my $err_msg = $cli_ret->[1];
my_log( $logFileHandle, "Error send cli: $err_msg" );
my $cli_output = $cli_ret->[1];
my_log( $logFileHandle, "CLI output: $cli_output" );
my @aLines = split /\n/, $cli_output;
ipv4_interface_brief::parse_ipv4addr( \@aLines, $ne_name );
while ( my ( $key, $val ) = each %{$hIPAddress} )
$hAllIPAddress{ $key } = $val;
$input_container->reset_iterator();
while ( my $row = $input_container->next() )
my $router = $row->{'NEName'};
my $interface_name = $row->{'InterfaceName'};
my $hashkey = $router . '^' . $interface_name;
my $ipv4addr = $hAllIPAddress{ $hashkey };
if ( !( defined $ipv4addr ) )
$ipv4addr = 'unassigned';
push @aResult, [ $ipv4addr ];
my @aColumns = ( 'IPv4Address' );
if ( !$ret->[0] && ( $#aResult >= 0 ) )
$ret = [0, new container( \@aColumns, \@aResult )];
my_log( $logFileHandle, "BEGIN MAIN" );
# Read CWI input from STDIN using cwitalk::read_data_records()
# The method returns an array:
# [1, $error_message] on error
# [0, $container] on success
my $aParsed = cwitalk::read_data_records();
# A traditional return mechanism in Perl is to use an array of two values.
# The first entry being 0 if successful and the second being either the
# actual data or an error message.
# Here we are getting back the data which was passed to us by CWI.
my_log( $logFileHandle, "ERROR: $msg" );
$dataContainer = $aParsed->[1];
$dataContainer->dump( $logFileHandle );
$dataContainer->reset_iterator();
while ( my $row = $dataContainer->next() )
my $ne_name = $row->{'NEName'};
$hRouters{ $ne_name } = 1;
$dataContainer->reset_iterator();
# What routers shall we send the CLI request to?
my @aRouters = keys %hRouters;
foreach my $router ( @aRouters )
my_log( $logFileHandle, "RouterName: $router" );
my $aFetched = fetchData( \@aRouters, $dataContainer );
# Notify CWI of error if any, otherwise serialize
# the container and write back
cwitalk::send_err( $aFetched->[1] );
my $retContainer = $aFetched->[1];
if ( defined $retContainer )
$retContainer->dump( $logFileHandle );
$retContainer->reset_iterator();
cwitalk::send_result( $retContainer );
# We are done so signal to CWI we are now done.
my_log( $logFileHandle, "END MAIN" );
Samples Used for a Standalone User Script
The following samples are presented for a standalone user script:
•
Content of the Main CLI Parsing Routine
•
Content of the node_xml_parse.pm Module
Content of the Main CLI Parsing Routine
The following example shows the content of the main CLI parsing routine in the memory_cli_parse.pm module:
package memory_cli_parse;
# Our parsing routing. It takes 1 argument.
# $aLines: reference to an array of CLI output lines to parse
# Output: classic perl output
# $aNodeMem: reference to an array. Each member in this array
# is a reference to a hash with keys of:
# node, totalmem, freedmem, usedratio
foreach my $line( @$aLines )
if ( $line =~ /^node:\s+(.*?)$/ )
if ( $node_name =~ /^node(.*?)$/ )
$hNode->{'node'} = $node_name;
# Match for "Physical Memory: 512M total"
elsif ( $line =~ /^\s?Physical Memory: (\d+)M total/ )
my $hNode = $aNodeMem[ $#aNodeMem ];
$hNode->{'totalmem'} = $1;
# Match for "Application Memory : 472M (231M available)"
# Used memory and Used Memory Ratio needs to be calculated
$line =~ /^\s+Application Memory\s+:\s+(\d+)M\s+\((\d+)M available\)/ )
my $hNode = $aNodeMem[ $#aNodeMem ];
$hNode->{'freemem'} = $2;
$hNode->{'usedmem'} = $hNode->{'totalmem'} - $hNode->{'freemem'};
$hNode->{'usedratio'} = $hNode->{'usedmem'}/$hNode->{'totalmem'};
Content of the node_xml_parse.pm Module
The following example is content from the node_xml_parse.pm Perl module:
# key: node name, in format like 0/0/CPU0
# values: card up time in seconds
# flag to indicate if inside the interested XML block
my $current_element_name = '';
my $parser = XML::Parser->new(Handlers => {
# $xmlstr: the XML string
# [1, error_message] if something is wrong
# [0, $hNodes] where $hNodes is reference to a hash.
Key: nodename, value: uptime
# $file_name: the XML file name
# [1, error_message] if something is wrong
# [0, $hNodes] where $hNodes is reference to a hash.
Key: nodename, value: uptime
sub parse_node_xml_file( $ )
$parser->parsefile( $file_name );
my ( $expat, $element, %attrs ) = @_;
$current_element_name = $element;
if ( $current_element_name eq 'NodeAttributes' )
elsif ( $inblock && ( $current_element_name eq 'CardUpTime' ) )
my ( $expat, $characters ) = @_;
if ( $current_element_name eq 'Name' )
$text = trim( $characters );
if ( length( $text ) > 0 )
elsif ( $in_carduptime == 1 )
if ( $current_element_name eq 'TimeInSeconds' )
$text = trim( $characters );
if ( length( $text ) > 0 )
my ( $expat, $element ) = @_;
if ( $element eq 'CardUpTime' )
$hNodes{ $node_name } = $up_time;
elsif ( $element eq 'NodeAttributes' )
$current_element_name = '';