0

I'm absolutely new to ruby and I'm trying to parse an XML structure and filter it for some attributes. The XML looks like this:

<systeminfo>
<machines>
<machine name="localhost">
<repository worker="localhost:8060" status="OK"/>
<dataengine worker="localhost:27042" status="OK"/>
<serverwebapplication worker="localhost:8000" status="OK"/>
<serverwebapplication worker="localhost:8001" status="OK"/>
<vizqlserver worker="localhost:9100" status="OK"/>
<vizqlserver worker="localhost:9101" status="OK"/>
<dataserver worker="localhost:9700" status="OK"/>
<dataserver worker="localhost:9701" status="OK"/>
<backgrounder worker="localhost:8250" status="OK"/>
<webserver worker="localhost:80" status="OK"/>
</machine>
</machines>
<service status="OK"/>
</systeminfo>

I want to check if the status attribute is OK. So far I have written this code:

#!/usr/bin/ruby -w

require 'rubygems'
require 'net/http'
require 'xmlsimple'

url = URI.parse("URL to XML")
req = Net::HTTP::Get.new(url.path)
res = Net::HTTP.start(url.host, url.port) {|http|
http.request(req)
}

sysinfodoc = XmlSimple.xml_in(res.body)


sysinfodoc["machines"][0]["machine"][0].each do |status|
p status[1][0]
p status[1][1]
end

Output:

{"worker"=>"localhost:8060", "status"=>"OK"}
nil
{"worker"=>"localhost:27042", "status"=>"OK"}
nil
{"worker"=>"localhost:9100", "status"=>"OK"}
{"worker"=>"localhost:9101", "status"=>"OK"}
{"worker"=>"localhost:8000", "status"=>"OK"}
{"worker"=>"localhost:8001", "status"=>"OK"}
{"worker"=>"localhost:8250", "status"=>"OK"}
nil
{"worker"=>"localhost:9700", "status"=>"OK"}
{"worker"=>"localhost:9701", "status"=>"OK"}
{"worker"=>"localhost:80", "status"=>"OK"}
nil
108
111

UPDATE The Output should be something like:

OK
OK
OK
OK
OK
OK
OK
OK
OK
OK

This script is supposed to be used with nagios. So rather than outputting the results I want to check if one of the status attributes conatins something which is not "OK" later. UPDATE

How do I get rid of the nils and the fixnums? Why are there fixnums anyway?

How can I filter this so I get just the status for every machine child? Or is this the wrong approach alltogether?

IMSoP
  • 89,526
  • 13
  • 117
  • 169
elcravo
  • 3
  • 2
  • Can you please show (rather than trying to explain) what exactly you expect as output? – Mathias Müller Feb 13 '15 at 09:33
  • I want the output to be OK OK OK OK OK OK OK OK OK OK since I want to check if there is a status which is not OK. This is supposed to be used with nagios. – elcravo Feb 13 '15 at 09:34
  • Please edit your question to include this information - and make sure your input XML is _representative_, if I understood correctly, there should be instances of machines that are "not OK". – Mathias Müller Feb 13 '15 at 09:36
  • I have updated my question. The XML is correct as ist is since it is created by the server software which should be monitored and the value of status is changing according to the service status. At the moment all services are up and running. – elcravo Feb 13 '15 at 09:42
  • You did not understand me I'm afraid. I asked you to present a _test case_ that includes all real-world cases you need to account for. – Mathias Müller Feb 13 '15 at 10:00

2 Answers2

2

How about using Nokogiri and XPath for this?

require 'nokogiri'
@doc = Nokogiri::XML(File.open("example.xml"))
@doc.xpath("//machine/*/@status").each { |x| puts x }

And the result will be

OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
=> 0
Mathias Müller
  • 22,203
  • 13
  • 58
  • 75
1

Disclaimer: Using nokogiri and XPath as suggested by Mathias is way more elegant and easy.


Once you have experienced the unexpected output, try to print out the status local variable itself:

sysinfodoc["machines"][0]["machine"][0].each do |status|
  # p status[1][0]
  p status
end

You will see the output is:

#⇒ ["name", "localhost"]
#⇒ ["repository", [{"worker"=>"localhost:8060", "status"=>"OK"}]]
#⇒ ["dataengine", [{"worker"=>"localhost:27042", "status"=>"OK"}]]
#⇒ ...

That said, to achieve what you want you should:

▶ sysinfodoc["machines"][0]["machine"][0].values.each do |status|
▷   next unless Array === status
▷   p status.last['status']
▷ end  
# "OK"
# "OK"
# "OK"
# ...

The check for status being array is necessary, since the presence of

# ["name", "localhost"]

Hope it helps.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160