{} as record separators?

B

Bob Norfolk

I'm working on a ruby script to read Nagios's status.dat and output it's
data.

The data format looks like this:

service {
host_name=www.bob.com
service_description=bob-website
modified_attributes=0
check_command=check-bob-website
event_handler=
has_been_checked=1
should_be_scheduled=1
check_execution_time=0.155
check_latency=0.250
check_type=0
current_state=0
last_hard_state=0
current_attempt=1
max_attempts=3
state_type=1
last_state_change=1155243779
last_hard_state_change=1155243779
last_time_ok=1155507506
last_time_warning=1154915720
last_time_unknown=0
last_time_critical=1155243478
plugin_output=HTTP OK HTTP/1.1 200 OK - 0.117 second response
time
performance_data=time=0.116677s;;;0.000000 size=3998B;;;0
last_check=1155507506
next_check=1155507806
current_notification_number=0
last_notification=0
next_notification=0
no_more_notifications=0
notifications_enabled=1
active_checks_enabled=1
passive_checks_enabled=0
event_handler_enabled=0
problem_has_been_acknowledged=0
acknowledgement_type=0
flap_detection_enabled=0
failure_prediction_enabled=1
process_performance_data=1
obsess_over_service=1
last_update=1155507620
is_flapping=0
percent_state_change=0.00
scheduled_downtime_depth=0
}


What I've written works, but it only works for one block. I'm not quite
sure how to make it work for the whole block?

Any advice would be greatly appreciated.

Here's the code I've written:

Class server

def read_nagiosstatus(filename)
nagios_status = {}
for line in IO.readlines(filename):
line.strip! # Remove all extraneous
whitespace
line.sub!(/#.*$/, "") # Remove comments
next unless line.length > 0 # check for end of file
var, value = line.split(/\s*=\s*/, 2)
nagios_status[var.intern] = value
end
return nagios_status
end

nagiosstatus = read_nagiosstatus("status.dat")
puts "Host name is #{nagiosstatus[:host_name]}"
puts "Service description is #{nagiosstatus[:service_description]}"
 
C

ctyu

** first try **


data = {}
File.foreach( "nagios.dat" ) do |e|
next if e =~ /service \{|\}/
k,v = e.strip.split("=")
data[k] = v
end


it didnt work because of line below has more than one "="
performance_data=time=0.116677s;;;0.000000 size=3998B;;;0


** second try **

data = {}
File.foreach( "nagios.dat") do |e|
next if e =~ /service \{|\}/
if e.strip =~ /(.*?)=(.*)/
data[$1]=$2
end
end
 
S

Scott

Wow, this can't be "The Ruby Way", but it works:

def nagios_data(data)
blocks = data.strip.split(/.*\}\n(?=\w+\s+\{)/)
blocks.map do |nagios_data|
data_points = nagios_data.split("\n")
# you can possibly use this as a hash key if they will be unique
# block_name = data_points[0][/\w+/]
data_points[1..-1].inject({}) do |values, data_point|
unless data_point =~ /\s+\}/
key, value = data_point.strip.sub(/#.*$/, "").split(/\s*=\s*/, 2)
values[key.intern] = value
end
values
end
end
end

require 'pp'

pp nagios_data(File.read("status.dat"))
 
B

Bob Norfolk

Scott said:
Wow, this can't be "The Ruby Way", but it works:

def nagios_data(data)
blocks = data.strip.split(/.*\}\n(?=\w+\s+\{)/)
blocks.map do |nagios_data|
data_points = nagios_data.split("\n")
# you can possibly use this as a hash key if they will be unique
# block_name = data_points[0][/\w+/]
data_points[1..-1].inject({}) do |values, data_point|
unless data_point =~ /\s+\}/
key, value = data_point.strip.sub(/#.*$/, "").split(/\s*=\s*/, 2)
values[key.intern] = value
end
values
end
end
end

require 'pp'

pp nagios_data(File.read("status.dat"))


When I try running this code, I get:

nagios_test2.rb:10:in `nagios_data': undefined method `intern' for
nil:NilClass (NoMethodError)
from nagios_test2.rb:7:in `inject'
from nagios_test2.rb:7:in `each'
from nagios_test2.rb:7:in `inject'
from nagios_test2.rb:7:in `nagios_data'
from nagios_test2.rb:3:in `map'
from nagios_test2.rb:3:in `nagios_data'
from nagios_test2.rb:19


Where does the intern method come from?
 
B

Bob Norfolk

Here's the code I've written:
Class server

def read_nagiosstatus(filename)
nagios_status = {}
for line in IO.readlines(filename):
line.strip! # Remove all extraneous
whitespace
line.sub!(/#.*$/, "") # Remove comments
next unless line.length > 0 # check for end of file
var, value = line.split(/\s*=\s*/, 2)
nagios_status[var.intern] = value
end
return nagios_status
end

nagiosstatus = read_nagiosstatus("status.dat")
puts "Host name is #{nagiosstatus[:host_name]}"
puts "Service description is #{nagiosstatus[:service_description]}"

So this code actually works. It reads through my key=value pairs just
fine. But of course, there's no code here to separate between multiple
instances of {}.

I understand now how to split on {} thanks to the examples you've all
posted.

What I don't understand though, is how would I address or list these?
I've got two problems to solve.

The first is that I'd be happy if I could just get an object that was
unique based on the host_name and I could call
nagios_status[:)host_name,:plugin_output)] for each host in the
status.dat file.

The second is that each host_name has multiple services. My unique key
needs to be based on the host_name variable. And I think I need to end
up so I have an object that's like host_name.service_name.variables.




def read_nagiosstatus(filename)
nagios_status = {}
for line in IO.readlines(filename):
line.strip! # Remove all extraneous
whitespace
line.sub!(/#.*$/, "") # Remove comments
next unless line.length > 0 # check for end of file
var, value = line.split(/\s*=\s*/, 2)
nagios_status[var.intern] = value
end
return nagios_status
end

nagiosstatus = read_nagiosstatus("status.dat")
puts #{nagios_status[:host_name]}
 
J

John Johnson

I'm working on a ruby script to read Nagios's status.dat and output it= 's
data.
Bob,

See if this works for you.

Regards,
JJ

#
# Copyright 2006, by John Johnson, All Rights Reserved
# Released under the NewBSD license.
#

require 'ostruct'
require 'pp'

class Services < Hash

SERVER_RE =3D /^\s*service\s*\{(.+?)\n\s*\}\s*$/m
VALUE_RE =3D /^\s*([^=3D]+)\s*=3D\s*([^\n=3D]*?)$/
INTEGER_RE =3D /^([-+]?\d+)$/
FLOAT_RE =3D /^([-+]?\d*\.\d*[eE]?\d*)$/
BOOLEAN_TRUE_RE =3D /^[1tTyY]+/ # e.g. 1, true, True, yes, Yes

# Types of declarations possible for field names.
:FIELD_NAMES
:FIELD_REGULAR_EXPRESSIONS

#
# Conversion for field values.
#
NAME_TYPES =3D 0
NAMES =3D 1
CONVERSION_BLOCK =3D 2
CONVERSIONS =3D [

# The following fields have time values that are the number
# of seconds past the epoch.
[
# Types of names listed.
[ :FIELD_NAMES ],
# Field name declarations.
['last_state_change', 'last_hard_state_change',
'last_time_ok', 'last_time_warning', 'last_time_unknown',
'last_time_critical', 'last_check', 'next_check',
'last_notification', 'next_notification', 'last_update'],
# Code to convert from a String to whatever this is.
lambda { |seconds_string|
return Time.at(seconds_string.to_i)
}
],

# The following appear to be boolean fields.
[
[ :FIELD_NAMES, :FIELD_REGULAR_EXPRESSIONS ],
['modified_attributes', 'has_been_checked', 'should_be_scheduled'=
,
'no_more_notifications',
/\w+_enabled$/, # all field names ending in _enabled
'problem_has_been_acknowledged', 'process_performance_data',
'obsess_over_service', 'is_flapping'],
lambda { |boolean_string|
if boolean_string =3D~ BOOLEAN_TRUE_RE
true
else
false
end
}
]
]

HOST_NAME_PROPERTY =3D 'host_name'

def convert_field(name, value)
CONVERSIONS.each { |conv_set|
name_types =3D conv_set[NAME_TYPES]
names =3D conv_set[NAMES]
conversion_block =3D conv_set[CONVERSION_BLOCK]

if name_types.include?:)FIELD_REGULAR_EXPRESSIONS)
names.each { |name_dec|
case
when name_dec.class=3D=3DString
if name =3D=3D name_dec
return conversion_block.call(value)
end
when name_dec.class=3D=3DRegexp
if name =3D~ name_dec
return conversion_block.call(value)
end
else
fail "Don't know what a #{name_dec.class} field name is"
end
}
end
if name_types.include?:)FIELD_NAMES)
if names.include?(name)
return conversion_block.call(value)
end
end
}

# Try implicit conversions.
case value
when INTEGER_RE
return Integer(value)
when FLOAT_RE
return Float(value)
end

return String(value)
end

def initialize(filename)
super
contents =3D File.open(filename).read
contents.scan(SERVER_RE) { |value_ary|
values =3D value_ary[0]
host_name =3D ""
service =3D OpenStruct.new
values.scan(VALUE_RE) { |name, value|
converted =3D convert_field(name, value)
dumped =3D Marshal.dump(converted)
service.instance_eval(
"self.#{name} =3D Marshal.load('#{dumped}')")
host_name=3Dvalue if name =3D=3D HOST_NAME_PROPERTY
}
self[host_name] =3D service
}
end
end

services =3D Services.new('nagio.txt')
services.each_pair{ |service, properties|
puts "Host: #{service}"
puts "\t has_been_checked: #{properties.has_been_checked}"
puts "\t next_check: #{properties.next_check}"
puts "\tnotifications_enabled: #{properties.notifications_enabled}"
}

if services['www.bob.com'].obsess_over_service
puts "Someone is obsessing over Bob!"
else
puts "No one is obsessing over Bob."
end

if services['www.test.com'].obsess_over_service
puts "Someone is obsessing over Test!"
else
puts "No one is obsessing over Test."
end


-- =

Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
 
B

Bob Norfolk

John said:
So, did it work?

Actually, I was just working on debugging this error that the script
above gives :


nagiostest3.rb:111:in `initialize': (eval):1:in `initialize': compile
error (SyntaxError)
(eval):1: unterminated string meets end of file
(eval):1: parse error, unexpected $, expecting ')'
íelf.last_time_critical = Marshal.load(u: Time
^
from nagiostest3.rb:108:in `initialize'
from nagiostest3.rb:104:in `initialize'
from nagiostest3.rb:119
 
J

John Johnson

Actually, I was just working on debugging this error that the script
above gives :


nagiostest3.rb:111:in `initialize': (eval):1:in `initialize': compile
error (SyntaxError)
(eval):1: unterminated string meets end of file
(eval):1: parse error, unexpected $, expecting ')'
íelf.last_time_critical = Marshal.load(u: Time
^
from nagiostest3.rb:108:in `initialize'
from nagiostest3.rb:104:in `initialize'
from nagiostest3.rb:119

Hm, probably a ' in the dumped data.
Try this instead:

def initialize(filename)
super
contents = File.open(filename).read
contents.scan(SERVER_RE) { |value_ary|
values = value_ary[0]
host_name = ""
service = OpenStruct.new
values.scan(VALUE_RE) { |name, value|
converted = convert_field(name, value)
dumped = Marshal.dump(converted)
dumped = dumped.unpack("H*")
service.instance_eval(
"self.#{name} =
Marshal.load(['#{dumped}'].pack('H*'))")
host_name=value if name == HOST_NAME_PROPERTY
}
self[host_name] = service
}
end
end

It converts the dumped data to a hex string, then back, eliminating the
need for escaping characters, etc.

Regards,
JJ
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top