|
Service Monitoring from within Lithium is handled by Service Monitoring Scripts, this way the type of services you want to monitor and the information you want available is really only limited by your imagination (and script writing skills). The best place to look for information on what kind of service monitoring scripts are currently available (and for hints on modifying scripts) is the Lithium Forums. This document is designed to help guide you through the structure of a service monitoring script, so that if you need to, you can create your own scripts. If you have played around with Action scripts, you will notice a few similarities in the structure of the scripts. Anatomy of a Service Monitoring ScriptEach script is broken down into 6 sections: Use Modules , Script Configuration Variables, Command Processing , Service Check/Testing, and Standard funcitons . Out of all the sections in the script, the only parts that require any modification are: Use modules, Configuration Variables, and Service Check/Testing. Everything else should not be modified. All code and examples used in this document are taken from the imap_check.pl script available from the Lithium forums. Use Modules For those of you not familiar with Perl scripts, the very first line of the script: #! /usr/bin/perl
Instructs the script where to find your Perl interpreter. This line is required and doesn't need to be changed. The next group of lines beginning with the word "use", indicates to Perl which modules this script uses. Because the transfer of Configuration variables is all done via XML, the "XML::Simple" module is required for ALL action scripts. Because service monitoring scripts also report on response and transaction time, it is best to ensure the Time::HiRes and Data::Types modules are also included. So the beginning of each Service monitoring script should look like: #! /usr/bin/perl use XML::Simple; use Time::HiRes qw(gettimeofday tv_interval); use Data::Types qw(:all);
|
The modules your scripts require will obviously depend on what you need the script to do. Lithium installs all required modules from the Comprehensive Perl Archive Network (http://www.cpan.org ). The CPAN website is a good place to begin your search for any module you may require. You don't need to worry about manually installing any Perl modules onto your Lithium server. When you Upload a script via the Script Management window of Lithium (or click the Repair button), Lithium parses over the script and ensures all required Modules are installed and up to date. Below the use statements, are the error values a script returns to console. These values indicate the current state of the of the service. These values are set within Lithium and as such you shouldn't have a need to change them: Return Value
| Script Variable | Description | | 1 | $NORMAL | This service is behaving as expected
| | 2 | $CONN_REFUSED | The remote device refused the connection | | 3 | $CONN_TIMEOUT | The connection has timed out | | 4 | $PROTO_TIMEOUT | The protocol has timed out | | 5 | $PROTO_ERROR
| There was a protocol error | | 6 | $SCRIPT_ERROR | A generic return error, used when the script is incorrectly configured |
Script Configuration VariablesThis is the section where you declare the variables that your script will use, eg. For the imap_check.pl script we need to know the account to test and the password. At this stage we don't need to worry about the IP address of the server/device we are querying, I will explain how we find that, later. Because Core communicates these variables to Console via XML, the variables are structured in a similar structure inside the script and stored in the %infostruct hash. %infostruct = ( 'version' => '1.0', 'desc' => "IMAP Service Check", 'proto' => "IMAP", 'port' => "143", 'transport' => "TCP", 'info' => "Connects to the device using the IMAP protocol and optionally attempts to log in", 'config_variable'=> [ { 'name' => 'test_account', 'desc' => 'Test IMAP Account' }, { 'name' => 'test_password', 'desc'=> 'Test IMAP Account Password' } ] ); |
Note, that the console expects to see all of the Key-Value Pairs listed above. Key
| Description | | version | The version number of this script | desc
| A short description of what the script does | proto
| The acronym of the protocol you are checking eg: HTTP, DNS, IMAP
| port
| The port number you wish this script to run over eg: For IMAP use 143 | | transport | The transport layer protocol to use. Either TCP or UDP | | info | A longer description of what the script checks
| config_variables
| A List of variables unique to this script eg. test_account, test_password |
Because each script will require a different set of variables to complete it's task you are provided with the config_variables list. 'config_variable'=> [ { 'name' => 'test_account', 'desc' => 'Test IMAP Account' }, { 'name' => 'test_password', 'desc'=> 'Test IMAP Account Password' } ] |
Each variable you want your script to use, needs to be in the same format as the test_account and test_password variables in the imap_check.pl script. If we want to mark a config variable as being required, we simply add a 'required' value to the variable and set it to 1. 'config_variable'=> [ { 'name' => 'test_account', 'desc' => 'Test IMAP Account', 'required' => '1' }, { 'name' => 'test_password', 'desc'=> 'Test IMAP Account Password' } ]
|
In this example, Console will now indicate that the test_account variable is required, and won't let the end user configure a service without it. Command ProcessingThis section of the script handles how the script runs depending on the command line arguments passed to it when it is run. There is nothing that needs to be changed in this section. For those that are interested, each script can be run in one of 3 modes: Info, Object, and Check. Info: This is the mode that Console runs to gather all the information about this script (description and config variables). When run in Info mode, the script simply prints the XML config structure to stdout. Console reads this output and interprets the information into Config variables. Object: This is the mode that Console runs to gather information about the custom object that this script returns Check: In this mode a script is run to check the service. All of the custom logic for this script is implemented in this mode.
As mentioned above, you don't need to change anything in this section. It just provides a good view of the different modes that a script can be run. Service Check/Testing This is the sub where all of the logic of your script is found. The beginning of this sub contains some general housekeeping required to access all of the variables passed to the script. 1. # 1 Parameter: Filename for the config XML 2. my ($filename) = @_; 3. 4. # Extract Variables from XML and store in %variables Hash 5. %variables = &getVariables($filename); 6. 7. # Set IP 8. my $ip; 9. if (is_string(%variables->{alt_ip}->{value})) { $ip = %variables->{alt_ip}->{value}; } 10. else { $ip = %variables->{dev_ip}->{value}; } 11. 12. # Set Port 13. my $port; 14. if (is_string(%variables->{alt_port}->{value})) { $port = %variables->{alt_port}->{value}; } 15. else { $port = '143'; }
|
Remember that the lines beginning with a # are comments, so in the above example lines 1,4,7 and 12 are all comments. Console sends all the required variables to a script via a temporary file, the filename of this file is passed directly to this sub. Line 2 stores this in the $filename variable. The next step is to read in all of the variables from the temporary file. Line 5 calls the getVariables function and passes it the filename. This function returns a Hash of all the variables. We store this hash in %variables.Because, we can monitor services on the current Lithium device, or a remote device, next we need to configure the IP address we are querying. This is done in lines 9 and 10. Line 9, first checks to see if the user configured an alternate IP address, and uses this if there is one. If there is no alternate IP address Line 10 tells the script to use the devices IP address. The next step is to work out what port we want to use: Either the default, as set in %infostruct at the beginning of the script or an alternate port as configured by the user. This is done in a similar way that the IP address is checked and stored. See lines 14 and 15. TCP Connection TestThe next part of this sub, is used to test TCP connectivity with the remote device. Obviously for UDP based protocols (such as DNS) this part can be skipped. 1. # Get Start Time 2. $tstart = [gettimeofday]; 3. 4. # Check TCP Connection and server response 5. $tcp = new IO::Socket::INET (PeerAddr => $ip, PeerPort => $port, Proto => 'tcp'); 6. if ($tcp) 7. { 8. $line = <$tcp>; 9. if ($line) 10. { 11. $message = $line; 12. if ($line =~ /OK/) 13. { $status = $NORMAL } 14. else 15. { $status = $PROTO_ERROR; } 16. } 17. else 18. { 19. $status = $PROTO_ERROR; 20. $message = "No output received from server."; 21. } 22. $tcp->close(); 23. } 24. else 25. { 26. $status = $CONN_REFUSED; 27. $message = "TCP Connection Refused"; 28. } 29. $tresp = [gettimeofday];
|
So, walking through this next snippet of code: Line 2 simply stores the current time of day, this is used to calculate the response time and total transaction time. On line 5, we attempt to make our TCP connection. Line 6 just checks if we were successful in making our connection or not. Lines 8 through to Line 21, read over the results from the connection to determine if the session was created successfully. One important aspect of this part of the script is the setting of the $status and $message variables (lines 11,13,15,19 and 20). We will use these variables to communicate back to console. Note: that the values for $status were defined at the beginning of the script. After all of that, lines 24 through 28, handle what happens when we are refused a TCP Connection. And finally, Line 29, grabs the current time of day. Later on, we return this back to console as the total response time ($tresp - $tstart = total Response Time). Still with me? There's not too much more to go. 1. if ($status == $NORMAL) 2. { 3. # Check for test_recipients 4. my $test_account = %variables->{test_account}->{value}; 5. my $test_password = %variables->{test_password}->{value}; 6. if (is_string($test_account)) 7. { 8. # test_account has been configured 9. # Attempt to log in 10. $handle = Net::IMAP::Simple->new($ip, Timeout => 60); 11. if ($handle->login($test_account, $test_password) > 0) 12. { 13. # Connected 14. $status = $NORMAL; 15. $message = "Logged in successfully ($message)"; 16. } 17. else 18. { 19. # Failed to login 20. $status = $PROTO_ERROR; 21. $message = "Failed to log in"; 22. } 23. $ttrans = [gettimeofday]; 24. } 25. }
|
Ok, so the next section is where we put the custom logic for the protocol we wish to query. Remember that the code snippets are from the imap_check.pl script, and as such your scripts will obviously vary in this section. However there are a few points worth highlighting. Firstly, we check our current status, as set by the TCP connection test. Again for a UDP based protocol, $status may not have been set yet, so this check would be unrequired. The main point needing to be highlighted here are Lines 4 and 5. These lines access and store the config variables as passed to the script from console. These are the variables that you set up at the beginning of the script in the %infostruct hash ( remember?) Provided you have read the config variables into the %variables hash, as stated earlier with the line: %variables = &getVariables($filename);
All of your config variables can be accessed in this way. For those of you that would like a worded explanation (warning: This may confuse you more). Line 4 can be literally interpreted as: my $test_account = %variables->{test_account}->{value}; * Grab the value of the "value" key of the "test_account" hash in the "%variables" hash and store it in the $test_account local variable * Don't say I didn't warn you. However if you were to replace, "test_account" with the name of the variable you are after you will have access to it in a local variable of the same name: See the differences between lines 4 and 5. 4. my $test_account = %variables->{test_account}->{value}; 5. my $test_password = %variables->{test_password}->{value};
Because these variables weren't marked as being required, the next step is test that they were actually provided to us. This is done on line 6 with the use of the is_string() function. From here this snippet is specific to the IMAP tests that are required for this script. What you put in this section depends on the protocol you are checking and how you want to test it. Remember to update the $status and $message variables as required. The final point to note is line 23: 23. $ttrans = [gettimeofday];
Just like the response time, we are taking the current time of day to calculate the total transaction time. Returning the results What use would the script be if we didn't return our results back to console? So the final few lines of our Check sub, handle this for us. 1. %resultstruct = ('metric'=> [ 2. { 'name' => 'status', 'value' => $status, }, 3. { 'name' => 'resptime', 'value' => tv_interval($tstart, $tresp), }, 4. { 'name' => 'transtime', 'value' => tv_interval($tstart, $ttrans), }, 5 . { 'name' => 'message', 'value' => $message, }, 6. ]); 7. print &generateXml($xmlroot, \%resultstruct);
|
Because we communicate our results back to Lithium via XML, we create the structure here (lines 1 through 6). As you can see it is basic XML that resembles. Provided that you have stored your status and message, in the $status and $message variables, and the three time readings are stored in $tstart, $tresp and $ttrans, then there is no need to make any changes here. And the final line of the scripts checking sub is to print out the results (stored in %resultstruct) to stdout. Standard functions. These are the functions (subroutines) that each script needs to perform it's basic tasks. There is no need to make any changes to this section of your script. You would have come across these subs in your script already, these are: - generateConfig: Used to provide the XML structure for config variables
- getVariables: Used to get the config variables out of the file (generated by Lithium) and stores them as a hash
- getAction: Used to extract action information from the command line
Additional Resources:
|