Cisco Craft Works Interface User Guide, Release 3.6
Tutorial Samples
Downloads: This chapterpdf (PDF - 384.0KB) The complete bookPDF (PDF - 6.86MB) | Feedback

Tutorial Samples

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" 
                     RefreshInterval="None">
      <Presentation>
         <Property Name="EntityID" Display="NodeID" RefreshInterval="0" 
                   Visible="true">
         </Property>
         <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" 
                                ThresholdDirection="Up">
                 </BarThreshold>
               </HorizontalBarProperties>
            </DisplayProperties>
         </Property>
         <Property Name="EmergencyAlarmCount" Display="Emer"
                   Tooltip="Launch Active Alarm Viewer for associated card/alarm level" 
                   LinkToApp="ActiveAlarmViewer"
                   RefreshInterval="0" Visible="true">
         </Property>
         <Property Name="AlertAlarmCount" Display="Alert"
                   Tooltip="Launch Active Alarm Viewer for associated card/alarm level" 
                   LinkToApp="ActiveAlarmViewer" RefreshInterval="0" Visible="true">
         </Property>
         <Property Name="CriticalAlarmCount" Display="Crit"
                   Tooltip="Launch Active Alarm Viewer for associated card/alarm level" 
                   LinkToApp="ActiveAlarmViewer" RefreshInterval="0" 
                   Visible="true">
         </Property>
         <Property Name="ErrorAlarmCount" Display="Error" 
                   Tooltip="Launch Active Alarm Viewer for associated card/alarm level"  
                   LinkToApp="ActiveAlarmViewer" RefreshInterval="0" Visible="true">
         </Property>
         <Property Name="WarningAlarmCount" Display="Warn"
                   Tooltip="Launch Active Alarm Viewer for associated card/alarm level" 
                   LinkToApp="ActiveAlarmViewer" RefreshInterval="0" Visible="true">
         </Property>
         <Property Name="CharacterDisplay" Display="Char" RefreshInterval="0" 
                   Visible="true">
         </Property>
         <Property Name="AdminUpOverDown" Display="AdminUp/Down"
                   Tooltip="Launch Interface Viewer for associated card" 
                   LinkToApp="InterfaceQuickViewer" RefreshInterval="0" Visible="true">
         </Property>
         <Property Name="LineUpOverDown" Display="LineUp/Down" 
                   Tooltip="Launch Interface Viewer for associated card" 
                   LinkToApp="InterfaceQuickViewer" RefreshInterval="0" Visible="true">
         </Property>
         <Property Name="ModelName" Display="Model" RefreshInterval="0" Visible="true">
         </Property>
         <Property Name="HardwareRevision" Display="HW Rev" RefreshInterval="0" 
                   Visible="true">
         </Property>
         <Property Name="SoftwareRevision" Display="SW" RefreshInterval="0" 
                   Visible="true">
         </Property>
         <Property Name="FirmwareRevision" Display="FW" RefreshInterval="0" 
                   Visible="true">
         </Property>
      </Presentation>
   </TablePreferences>
</Preferences>
 
   

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:

sub read_data_records
{
   my $param = shift;
 
   
   my $input = "";
   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$/ ){
      $input = $1;
   } else {
      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
   # unescape the values
   for ( my $i = 0; $i < @aColumnNames; $i++ ) {
      $aColumnNames[$i] = unescape( $aColumnNames[$i] );
   }
   
   my @aDataRows = ();
# 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] );
         } else {
            $aTokens[$i] = '';
         }
      }
      push @aDataRows, \@aTokens;
   }
# Now we have the column names and values, create a container data structure
   my $c = new container( \@aColumnNames, \@aDataRows );
   
   return [0, $c];
}
 
   

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 $neName = shift;
   my $request = shift;
 
   
# encode the CLI to send  
   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, 
                                          SIG_CWI_SEND_CLI_END, 
                                          $sendResponse );
   return $ret;
}
 
   

Example of the Finished Script Content

The following example shows the finished script content:

#!/usr/local/bin/perl
# IPv4Addr.pl
# 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 cwitalk;
use container;
use IO::File;
use ipv4_interface_brief;
use strict;
 
   
# 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!" );
}
 
   
# Write data to log file
sub my_log( $$ )
{
   my ( $logfh, $msg ) = @_;
   print $logfh "<LOG>: $msg\n";
   $logfh->flush();
}
 
   
 
   
# Input: 
#   $input_container: container with fields 'NEName', 'InterfaceName'
# Output:
#   [1, error_message] on error
#   [0, $container] on success. container column names: IPv4Address
sub fetchData( $$ )
{
   my ( $aRouters, $input_container ) = @_;
 
   
   my @aResult = ();
   my $cli = 'show ipv4 interface brief';
 
   
   # key: $ne_name + ^ + $interface_name
   my %hAllIPAddress = ();
 
   
   my $ret = [0, undef];
   
   # 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 );
      if ( $cli_ret->[0] )
      {
         my $err_msg = $cli_ret->[1];
         my_log( $logFileHandle, "Error send cli: $err_msg" );
         $ret = $cli_ret;
         last;
      }
      else
      {
         my $cli_output = $cli_ret->[1];
         my_log( $logFileHandle, "CLI output: $cli_output" );
         my @aLines = split /\n/, $cli_output;
         my $hIPAddress = 
                  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 )];
   }
   
   return $ret;
}
 
   
# Main routine.
sub main()
{
   my $dataContainer;
   my $returnContainer;
   my $rc;
   
   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.
   if ( $aParsed->[0] )
   {
      my $msg = $aParsed->[1];
      my_log( $logFileHandle, "ERROR: $msg" );
   }
   else
   {
      $dataContainer = $aParsed->[1];
      $dataContainer->dump( $logFileHandle );
      $dataContainer->reset_iterator();
      
      my %hRouters = ();
      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
      if ( $aFetched->[ 0 ] )
      {
         cwitalk::send_err( $aFetched->[1] );
      }
      else
      {
         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.
   cwitalk::end_script();
   my_log( $logFileHandle, "END MAIN" );
}
 
   
# Start main method.
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;
 
   
use strict;
 
   
# Our parsing routing. It takes 1 argument.
# Input:
#    $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
#
sub parse_memory( $ )
{
   my ( $aLines ) = @_;
 
   
   my @aNodeMem = ();
 
   
   foreach my $line( @$aLines )
   {
      # Match for node name
      if ( $line =~ /^node:\s+(.*?)$/ )
      {
         my $node_name = $1;
         if ( $node_name =~ /^node(.*?)$/ )
         {
            $node_name = $1;
         }
         $node_name =~ tr/_/\//;
         my $hNode = {};
         $hNode->{'node'} = $node_name;
         push @aNodeMem, $hNode;
      }
      # 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
      elsif ( 
       $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'};
      }
   }
   
   return \@aNodeMem;
}
 
   
1;
 
   

Content of the node_xml_parse.pm Module

The following example is content from the node_xml_parse.pm Perl module:

package node_xml_parse;
 
   
use strict;
use XML::Parser;
 
   
# key: node name, in format like 0/0/CPU0
# values: card up time in seconds
my %hNodes = ();
 
   
# flag to indicate if inside the interested XML block
# 0: not in; 
# 1: in
my $inblock = 0;
my $in_carduptime = 0;
 
   
my $current_element_name = '';
my $node_name = '';
my $up_time = 0;
 
   
# create the parser
my $parser = XML::Parser->new(Handlers => {
                                   Start=>\&handle_start,
                                   Char=>\&char_handler,
                                   End=>\&handle_end,
                                 });
 
   
# Parse XML string
# Input 
#   $xmlstr: the XML string
# Output:
#   [1, error_message] if something is wrong
#   [0, $hNodes] where $hNodes is reference to a hash. 
                 Key: nodename, value: uptime
sub parse_node_xml( $ )
{
   my ( $xmlstr ) = @_;
   
   return [0, \%hNodes];
}
 
   
# Parse XML string
# Input 
#   $file_name: the XML file name
# Output:
#   [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( $ )
{
   my ( $file_name ) = @_;
 
   
   $parser->parsefile( $file_name );
   
   return [0, \%hNodes];
}
 
   
sub handle_start 
{
   my ( $expat, $element, %attrs ) = @_;
   $current_element_name = $element;
   
   if ( $current_element_name eq 'NodeAttributes' ) 
   {
      $inblock = 1;
   }
   elsif ( $inblock && ( $current_element_name eq 'CardUpTime' ) )
   {
      $in_carduptime = 1;
   }
}
 
   
sub char_handler
{
   my ( $expat, $characters ) = @_;
   my $text = '';
   
   if ( $inblock == 1 )
   {
      if ( $current_element_name eq 'Name' )
      {
         $text = trim( $characters );
         if ( length( $text ) > 0 )
         {
            $node_name = $text;
         }
      }
      elsif ( $in_carduptime == 1 )
      {
         if ( $current_element_name eq 'TimeInSeconds' )
         {
            $text = trim( $characters );
            if ( length( $text ) > 0 )
            {
               $up_time = $text;
            }
         }
      }
   }
}
 
   
 
   
sub handle_end
{
   my ( $expat, $element ) = @_;
 
   
   if ( $element eq 'CardUpTime' )
   {
      $hNodes{ $node_name } = $up_time;
      $in_carduptime = 0;
   }
   elsif ( $element eq 'NodeAttributes' ) 
   {
      $inblock = 0;
      $node_name = '';
      $up_time = 0;
   }
   
   $current_element_name = '';
}
 
   
sub trim
{
   my $text = shift;
   $text =~ s/^\s*//;
   $text =~ s/\s*$//;
   
   return $text;
}
 
   
1;