diff options
author | Steve Kemp <steve@steve.org.uk> | 2012-11-13 16:16:18 +0000 |
---|---|---|
committer | Steve Kemp <steve@steve.org.uk> | 2012-11-13 16:16:18 +0000 |
commit | 723f2d471679d4f2081b8cca82346f93f906fc26 (patch) | |
tree | e978d5ac404e5686d22bdc8717ba24de4a95ed7b | |
parent | affe98261e29ec044066c36cf625772eb0f48154 (diff) |
added test-case to cover basic file-parsing.
-rwxr-xr-x | parser/parser.rb | 360 | ||||
-rwxr-xr-x | t/test-parser.rb | 107 |
2 files changed, 292 insertions, 175 deletions
diff --git a/parser/parser.rb b/parser/parser.rb index a3f7246..4612001 100755 --- a/parser/parser.rb +++ b/parser/parser.rb @@ -29,15 +29,13 @@ require 'json' # # TODO: # -# 1. Break parse_file down into repeated calls to parse_line, to allow test -# cases to be written. +# 1. Explicitly abort and panic on malformed lines. # -# 2. Explicitly abort and panic on malformed lines. +# 2. Implement HTTP-fetching for macro-bodies. # -# 3. Implement HTTP-fetching for macro-bodies. # # Steve -# -- +# -- # class MonitorConfig @@ -65,8 +63,7 @@ class MonitorConfig @file = filename if ( @file.nil? || ( ! File.exists?( @file) ) ) - puts "Missing configuration file" - exit( 0 ) + raise ArgumentError, "Missing configuration file!" end end @@ -112,6 +109,10 @@ class MonitorConfig + def macros + @MACROS + end + # # Is the given string of text a macro? # @@ -129,208 +130,217 @@ class MonitorConfig end - - - # - # Parse the configuration file, named in our constructor. + # Parse a single line from the configuration file. # - def parse_file() + def parse_line( line ) + # - # Parse the configuration file on the command line + # A blank line, or a comment may be skipped. # - File.open( @file, "r").each_line do |line| - - # - # A blank line, or a comment may be skipped. - # - next if ( ( line =~ /^#/ ) || ( line.length < 1 ) ) + return if ( ( line =~ /^#/ ) || ( line.length < 1 ) ) - # specification of mauve-server to which we should raise our alerts to. - next if ( line =~ /Mauve\s+server(.*)source/ ) - - - # - # Look for macro definitions, inline - # - if ( line =~ /^([A-Z]_+)\s+are\s+fetched\s+from\s+([^\s]+)\.?/ ) - define_macro( line ) + # + # The specification of mauve-server to which we should raise our alerts to. + # + return if ( line =~ /Mauve\s+server(.*)source/ ) - elsif ( line =~ /^([0-9A-Z_]+)\s+(is|are)\s+/ ) - define_macro( line ) - elsif ( line =~ /\s+must\s+ping/ ) + # + # Look for macro definitions, inline + # + if ( line =~ /^([A-Z]_+)\s+are\s+fetched\s+from\s+([^\s]+)\.?/ ) + define_macro( line ) - # - # Target - # - targets = Array.new + elsif ( line =~ /^([0-9A-Z_]+)\s+(is|are)\s+/ ) + define_macro( line ) - # - # Fallback target is the first token on the line - # - target = line.split( /\s+/)[0] + elsif ( line =~ /\s+must\s+ping/ ) + # + # Target + # + targets = Array.new - # - # If the target is a macro - # - if ( is_macro?( target ) ) - targets = get_macro_targets(target) + # + # Fallback target is the first token on the line + # + target = line.split( /\s+/)[0] + + + # + # If the target is a macro + # + if ( is_macro?( target ) ) + targets = get_macro_targets(target) + else + targets.push( target ) + end + + # + # The alert-failure message + # + alert = "Ping failed" + if ( line =~ /otherwise '([^']+)'/ ) + alert=$1.dup + end + + # + # Store the test(s) + # + targets.each do |host| + test = { + :target_host => host, + :test_type => "ping", + :test_alert => alert + } + + if ( !ENV['DUMP'].nil? ) + puts ( test.to_json ) else - targets.push( target ) + @queue.put( test.to_json ) end - - # - # The alert-failure message - # - alert = "Ping failed" - if ( line =~ /otherwise '([^']+)'/ ) - alert=$1.dup - end - + end + + elsif ( line =~ /\s+must\s+run\s+([^\s]+)\s+/i ) + + # + # Get the service we're testing, and remove any trailing "." + # + # This handles the case of: + # + # LINN_HOSTS must run ssh. + # + service = $1.dup + service.chomp!(".") + + # + # Target of the service-test. + # + targets = Array.new + target = line.split( /\s+/)[0] + + # + # If the target is a macro + # + if ( is_macro?( target ) ) + targets = get_macro_targets( target ) + else + targets.push( target ) + end + + # + # Alert text + # + alert = "#{service} failed" + if ( line =~ /otherwise '([^']+)'/ ) + alert=$1.dup + end + + # + # All our service tests require a port - we setup the defaults here, + # but the configuration file will allow users to specify an alternative + # via " on XXX ". + # + case service + when /ssh/ then + port=22 + when /jabber/ then + port=5222 + when /ldap/ then + port=389 + when /^https$/ then + port=443 + when /^http$/ then + port=80 + when /rsync/i then + port=873 + when /ftp/i then + port=21 + when /telnet/i then + port=23 + when /smtp/i then + port=25 + end + + # + # But allow that to be changed + # + # e.g. + # + # must run ssh on 33 otherwise .. + # must run ftp on 44 otherwise .. + # must run http on 8000 otherwise .. + # + if ( line =~ /\s+on\s+([0-9]+)/ ) + port = $1.dup + end + + targets.each do |host| + + test = { + :target_host => host, + :test_type => service, + :test_port => port, + :test_alert => alert + } + # - # Store the test(s) + # HTTP-tests will include the expected result in one of two + # forms: # - targets.each do |host| - test = { - :target_host => host, - :test_type => "ping", - :test_alert => alert - } - - if ( !ENV['DUMP'].nil? ) - puts ( test.to_json ) - else - @queue.put( test.to_json ) - end - end - - elsif ( line =~ /\s+must\s+run\s+([^\s]+)\s+/i ) - + # must run http with status 200 # - # Get the service we're testing, and remove any trailing "." + # must run http with content 'text' # - # This handles the case of: + # If those are sepcified then include them here. # - # LINN_HOSTS must run ssh. + # Note we're deliberately fast and loose here - which allows both to be specified # - service = $1.dup - service.chomp!(".") - + # http://example.vm/ must run http with status 200 and content 'OK' otherwise 'boo!'. # - # Target of the service-test. # - targets = Array.new - target = line.split( /\s+/)[0] + if ( line =~ /\s+with\s+status\s+([0-9]+)\s+/ ) + test[:http_status]=$1.dup + end + if ( line =~ /\s+with\s+content\s+'([^']+)'/ ) + test[:http_text]=$1.dup + end # - # If the target is a macro + # We've parsed(!) the line. Either output the JSON to the console + # or add to the queue. # - if ( is_macro?( target ) ) - targets = get_macro_targets( target ) + if ( !ENV['DUMP'].nil? ) + puts ( test.to_json ) else - targets.push( target ) + @queue.put( test.to_json ) end + end + else + puts "Unknown line: #{line}" if ( line.length > 2 ) + end + end - # - # Alert text - # - alert = "#{service} failed" - if ( line =~ /otherwise '([^']+)'/ ) - alert=$1.dup - end - # - # All our service tests require a port - we setup the defaults here, - # but the configuration file will allow users to specify an alternative - # via " on XXX ". - # - case service - when /ssh/ then - port=22 - when /jabber/ then - port=5222 - when /ldap/ then - port=389 - when /^https$/ then - port=443 - when /^http$/ then - port=80 - when /rsync/i then - port=873 - when /ftp/i then - port=21 - when /telnet/i then - port=23 - when /smtp/i then - port=25 - end + # + # Parse the configuration file, named in our constructor. + # + def parse_file() + # + # Parse the configuration file on the command line + # + File.open( @file, "r").each_line do |line| + parse_line( line) + end + end - # - # But allow that to be changed - # - # e.g. - # - # must run ssh on 33 otherwise .. - # must run ftp on 44 otherwise .. - # must run http on 8000 otherwise .. - # - if ( line =~ /\s+on\s+([0-9]+)/ ) - port = $1.dup - end - targets.each do |host| - - test = { - :target_host => host, - :test_type => service, - :test_port => port, - :test_alert => alert - } - - # - # HTTP-tests will include the expected result in one of two - # forms: - # - # must run http with status 200 - # - # must run http with content 'text' - # - # If those are sepcified then include them here. - # - # Note we're deliberately fast and loose here - which allows both to be specified - # - # http://example.vm/ must run http with status 200 and content 'OK' otherwise 'boo!'. - # - # - if ( line =~ /\s+with\s+status\s+([0-9]+)\s+/ ) - test[:http_status]=$1.dup - end - if ( line =~ /\s+with\s+content\s+'([^']+)'/ ) - test[:http_text]=$1.dup - end - - # - # We've parsed(!) the line. Either output the JSON to the console - # or add to the queue. - # - if ( !ENV['DUMP'].nil? ) - puts ( test.to_json ) - else - @queue.put( test.to_json ) - end - end +end - else - puts "Unknown line: #{line}" if ( line.length > 2 ) - end - end - end -end # # Entry-point to our code. diff --git a/t/test-parser.rb b/t/test-parser.rb new file mode 100755 index 0000000..4c1076a --- /dev/null +++ b/t/test-parser.rb @@ -0,0 +1,107 @@ +#!/usr/bin/ruby -I../parser/ -I./parser/ + + +require 'test/unit' +require 'parser' + + + +# +# Unit test for our parser. +# +class TestParser < Test::Unit::TestCase + + # + # Create the test suite environment: NOP. + # + def setup + end + + # + # Destroy the test suite environment: NOP. + # + def teardown + end + + + # + # Test we can create a new parser object - specifically + # that it throws exceptions if it is not given a filename + # that exists. + # + def test_init + + # + # Missing filename -> Exception + # + assert_raise ArgumentError do + MonitorConfig.new() + end + + # + # Filename points to file that doesn't exist -> Exception + # + assert_raise ArgumentError do + MonitorConfig.new("/file/doesn't/exist") + end + + # + # File that exists -> No Exception. + # + assert_nothing_raised do + MonitorConfig.new("/dev/null" ) + end + + end + + + # + # Test that we can define macros. + # + def test_macros + + parser = MonitorConfig.new("/dev/null" ) + + # + # With nothing loaded we should have zero macros - so the + # count of our macros hash should be zero + # + macros = parser.macros + assert( macros.empty? ) + assert( macros.size() == 0 ) + + + # + # Define a macro: + # + # FOO => "kvm1.vm.bytemark.co.uk", "kvm2.vm.bytemark.co.uk". + # + # Before defining it double-check it doesn't exist + # + assert( !(parser.is_macro?( "FOO" )) ) + + parser.define_macro( "FOO is kvm1.vm.bytemark.co.uk and kvm2.vm.bytemark.co.uk." ) + + + # + # OK we should now have a single macro defined. + # + macros = parser.macros + assert( macros.size() == 1 ) + + # + # The macro name "FOO" should exist + # + assert( parser.is_macro?( "FOO" ) ) + + # + # The contents of the FOO macro should have the value we expect + # + val = parser.get_macro_targets( "FOO" ) + assert( val.size() == 2 ) + assert( val.include?( "kvm1.vm.bytemark.co.uk" ) ) + assert( val.include?( "kvm2.vm.bytemark.co.uk" ) ) + end + + +end |