I'm trying to make nagios check for BBU (Battery Backup Unit), I have this ruby script called check_bbu
#!/usr/bin/ruby
require 'json'
output = %x{/usr/sbin/storcli /c0/bbu show j}
begin
j = JSON.parse(output)
result = j["Controllers"][0]["Command Status"]["Status"]
status = j["Controllers"][0]["Response Data"]["BBU_Info"][0]["State"]
rescue Exception => e
puts "CRITICAL: error reading BBU status: #{e}"
exit 2
end
if result != 'Success'
puts "CRITICAL: command not successful, result: #{result}"
exit 2
end
if status != 'Optimal'
puts "CRITICAL: BBU not optimal, status is #{status}"
exit 2
end
puts "OK: BBU is optimal"
But when I run this plugin I'm getting following error,
$ ./check_nrpe -u -t 30 -H foo.example.com -c check_bbu
CRITICAL: error reading BBU status: undefined method `[]' for nil:NilClass
What am I doing wrong in this ?
Seems your code break at
j = JSON.parse(output)
result = j["Controllers"][0]["Command Status"]["Status"]
status = j["Controllers"][0]["Response Data"]["BBU_Info"][0]["State"]
you should check which line
j == nil, and your want j["Controllers"]
result = j["Controllers"][0]["Command Status"]["Status"]
status = j["Controllers"][0]["Response Data"]["BBU_Info"][0]["State"]
cause undefined method '[]' for nil:NilClass by display j value first, and make sure json format match your code.
j = JSON.parse(output)
p j # display value
update:
your json format should match your code, otherwise it will raise error.
for example:
{
"Controllers": [
{
"Commandddd Status": { # format not match
"Status": "success"
}
}
]
}
# code and json format not match
j["Controllers"][0]["Command Status"] # nil
j["Controllers"][0]["Command Status"]["Status"]
=> nil["Status"] # boom!
For example to my comment, lets imagine that we have correct JSON:
j = {"Controllers" => [{"Comand Status" => {"status" => 'ok'}}]}
=> {"Controllers"=>[{"Comand Status"=>{"status"=>"ok"}}]}
so by typing your first line, it's should return correct result:
> j["Controllers"][0]["Comand Status"]["status"]
=> "ok"
But also you can get wrong JSON, for example:
j_e = {"Controllers" => []}
so now it's return error:
>j_e["Controllers"][0]["Comand Status"]["status"]
NoMethodError: undefined method `[]' for nil:NilClass
to solve it, you can use something like:
def try data, keys # there we take arguments: data == j, and keys == array with keys
return data if data.nil? # there we check: if data, that we send == nil, for example j = nil, array = ['first', etc..] we should stop this method and return nil as result
value = data[keys.shift] # on this line we try to get some data from j ##keys.shift will delete first element from array that we send to this method and return as `key` for `data`, for example data['Controllers'] so keys now will looks like: [0, 'Comand Status', 'status']
unless keys.empty? #on this line we check if on the pred line we was used the last key and keys now looks like: [] we: *return value and if it's not empty we just **call this method 1 more time
try(value, keys) #**value = [{"Comand Status" => {"status" => 'ok'}}] and keys = [0, 'Comand Status', 'status']
else
value #*nil or value
end
end
j = {"Controllers"=>[{"Comand Status"=>{"status"=>"ok"}}]}
try(j, ['Controllers', 0, 'Comand Status', 'status'])
>'ok'
try j, ['Controllers', 1, 'Comand Status', 'status']
> nil
in you code this should looks like:
require 'json'
def try data, keys
return data if data.nil?
value = data[keys.shift]
unless keys.empty?
try(value, keys)
else
value
end
end
output = %x{/usr/sbin/storcli /c0/bbu show j}
begin
j = JSON.parse(output)
result = try(j, ["Controllers", 0, "Command Status", "Status"])
status = try(j, ["Controllers", 0, "Response Data", "BBU_Info", 0, "State"])
rescue Exception => e
puts "CRITICAL: error reading BBU status: #{e}"
exit 2
end
if result != 'Success'
puts "CRITICAL: command not successful, result: #{result}"
exit 2
end
if status != 'Optimal'
puts "CRITICAL: BBU not optimal, status is #{status}"
exit 2
end
puts "OK: BBU is optimal"
Also, for Ruby 2.3.0+
it's much easier, just:
j.dig("Controllers", 0, "Comand Status", "status")
Related
I am making a Ruby REPL to be used inside an application. I made code:
a = 1
b = 2
currentScope = []
Kernel.local_variables.each do |var|
currentScope << [var,Kernel.eval(var.to_s)]
end
launchREPL(currentScope)
Inside the REPL, I can execute the following code:
#a #=>1
#a+#b #=>3
Ideally I wouldn't have to write the four lines of code before I launch the REPL, and instead I would like to run them inside the launchREPL function. However this would require access to the previous scope from inside the launchREPL function.
Test1
Most notably I tried:
launchREPL(Kernel)
When I do the following:
def launchREPL(scope)
F = 0
puts scope.local_variables # => [:F]
end
it is apparent that this method is not valid.
Test2
launchREPL(Kernel.binding)
def launchREPL(scope)
Kernel.binding.local_variables #= Error: private method 'local_variables' called for #<Binding>
end
Is there any way to do what I'm trying to do?
Edit: P.S. This is currently the code inside launchREPL:
def launchREPL(scope=nil,winName="Ruby REPL")
# ICM RB file Begin:
puts "\"Starting REPL...\""
__b = binding #Evaluating in a binding, keeps track of local variables
__s = ""
###############################################################################
# SEND INSTANCE VARIABLES TO REPL
###############################################################################
#
#How to prepare scope
# currentScope = []
# Kernel.local_variables.each do |var|
# currentScope << [var,Kernel.eval(var.to_s)]
# end
# launchREPL(currentScope)
if scope != nil
scope.each do |varDef|
__b.instance_variable_set "##{varDef[0].to_s}" , varDef[1]
__b.eval("##{varDef[0].to_s} = __b.instance_variable_get(:##{varDef[0].to_s})")
end
end
# to get instance variables: __b.instance_variable_get(__b.instance_variables[0])
# or better: __b.instance_variable_get(:#pipe1)
#
###############################################################################
bStartup = true
while bStartup || __s != ""
# If startup required skip evaluation step
if !bStartup
#Evaluate command
begin
__ret = __s + "\n>" + __b.eval(__s).to_s
rescue
__ret = __s + "\n> Error: " + $!.to_s
end
puts __ret
else
#REPL is already running
bStartup = false
end
#Read user input & print previous output
__s = WSApplication.input_box(__ret,winName,"")
__s == nil ? __s = "" : nil
end
end
Although what you are trying to achieve is unclear and there are definitely many ways to do it properly, every ruby method might be called with Object#send approach:
def launchREPL(scope)
scope.send :local_variables #⇒ here you go
end
a = 42
launchREPL(binding).include?(:a)
#⇒ true
Sidenote: this is how your “4 lines” are usually written in ruby:
local_variables.map { |var| [var, eval(var.to_s)] }
And this is how they should be written (note Binding#local_variable_get):
local_variables.map { |var| [var, binding.local_variable_get(var)] }
The summing up:
def launchREPL(scope)
vars = scope.send(:local_variables).map do |var|
[var, scope.local_variable_get(var)]
end
# some other code
end
a = 42
launchREPL(binding).to_h[:a]
#⇒ 42
This won’t fit the comment, so I would post it as an answer.
def launchREPL(scope = nil, winName = "Ruby REPL")
puts '"Starting REPL..."'
scope.eval('local_variables').each do |var|
instance_variable_set "##{var}", scope.eval(var.to_s)
end if scope
s = ""
loop do
ret = begin
"#{s}\n> #{eval(s)}"
rescue => e
"#{s}\n> Error: #{e.message}"
end
puts ret
# s = WSApplication.input_box(ret, winName, "")
# break if s.empty?
s = "100 * #a" # remove this line and uncomment 2 above
end
end
a = 42
launchREPL(binding)
This is how your function should be written (I have just make it looking as ruby code.) The above works (currently it has no break at all, but you can see as it’s calculating 4200 infinitely.)
I have a function which gets an array of element, then it iterates over the array, when expected element is found it breaks and return.
The function is this:
def get_expected_element(id:, name:)
# I am sure there are 3 elements got
elem_array = get_all_elements(id)
element = nil
elem_array.each { |elem|
# I see this log
puts "elem = #{elem}"
if elem == name
element = elem
# I see this log too
puts "Found element"
break
end
}
# I see this log too, and program is hanging
puts "=== return ==="
element
end
When I invoke the function, the program is hanging after puts "=== return ===":
service = MyService.new
element_got = service.get_expected_element(id:3, name:"apple")
# I don't see the below log
puts "#{element_got}, I don't see this, why?"
The log in console is this:
elem = orange
elem = apple
Found element
=== return ===
<it is hanging>
I cannot understand why the invoked function doesn't return?
Leaving out MyService I ran this:
def get_expected_element(id:, name:)
# I am sure there are 3 elements got
# elem_array = get_all_elements(id)
elem_array = ["elem1", "apple", "elem3"]
element = nil
elem_array.each { |elem|
# I see this log
puts "elem = #{elem}"
if elem == name
element = elem
# I see this log too
puts "Found element"
break
end
}
# I see this log too, and program is hanging
puts "=== return ==="
element
end
puts get_expected_element(id: 3, name: "apple")
and got this:
elem = elem1
elem = apple
Found element
=== return ===
apple
Your get_expected_element method seems fine.
I do not understand what is going on in the line
print buggy_logger << "\n" # <- This insertion is the bug.
Why is that the variable status changes when the above line is called?
I am following this website http://www.reactive.io/tips/2009/01/11/the-difference-between-ruby-symbols-and-strings/
status = "peace"
buggy_logger = status
print "Status: "
print buggy_logger << "\n" # <- This insertion is the bug.
def launch_nukes?(status)
unless status == 'peace'
return true
else
return false
end
end
print "Nukes Launched: #{launch_nukes?(status)}\n"
OUTPUT IS:
=> Status: peace
=> Nukes Launched: true
Your question is "why does the variable change?"
The answer is because buggy_logger holds a reference to status. Easily proven by inspecting the object_id.
irb(main):001:0> a = "hi"
=> "hi"
irb(main):002:0> a.object_id
=> 24088560
irb(main):003:0> b = a
=> "hi"
irb(main):004:0> b.object_id
=> 24088560
irb(main):005:0>
To create a copy use + or any non-mutating operator. Don't use <<.
irb(main):010:0> c = a + " guys"
=> "hi guys"
irb(main):011:0> c.object_id
=> 26523040
irb(main):012:0>
Since status = "peace" is a string, when buggy_logger << "\n" is ran, it's updating the string of buggy_logger (and subsequently, status) to be "peace\n"
Therefore, when the method is ran, it returns true because status != "peace" anymore.
Now, if in the beginning you used a symbol status = :peace, it would not be able to be altered with the "\n" appendage. Therefore, the method would return false because status == :peace
Symbol version:
status = :peace
buggy_logger = status
print "Status: "
#print buggy_logger << "\n" # This is no longer possible. It will cause an error
def launch_nukes?(status)
unless status == :peace
return true
else
return false
end
end
print "Nukes Launched: #{launch_nukes?(status)}\n" # <- Returns FALSE
I am having trouble with this one, I try to change the conditions and add other options but still I got the same error.. can someone help me? Thanks a lot!
this is my codes:
require 'watir-webdriver'
require 'minitest/autorun'
require "win32ole"
class Login < Minitest::Unit::TestCase
$number_of_fails = 0
$number_of_success = 0
$fields = ["first_name", "middle_name", "last_name", "sss_number", "tin_number"]
# $to_enter = ["Helfe", "padayao", "Marquez", "1234rer", "9387373"]
def test_in()
#browser =Watir::Browser.new :firefox
#browser.goto 'http://gw01.nextix.org/login'
accept_next_alert=true
#browser.driver.manage.window.maximize
excel= WIN32OLE::new("excel.Application")
wrkbook=excel.Workbooks.Open("C:\\testing\\inputs.xlsx")
wrksheet = wrkbook.worksheets(1)
wrksheet.select
rows = 2
while rows <= 5
$username = wrksheet.cells(rows, "A").value
$password = wrksheet.cells(rows, "B").value
#browser.text_field(:name, "username").set($username)
sleep 3
#browser.text_field(:name, "password").set($password)
sleep 3
#browser.button(:name => 'login').click
sleep 3
rows = rows + 1
end
$Dashboard = #browser.link(:text, "Dashboard")
$Dashboard.exists?
$Dashboard.click
#browser.link(:text, "Users").click
#browser.button(:value,"Add New User").click
rows = 8
while rows <= 13
$fname = wrksheet.cells(rows, "A").value
$mname = wrksheet.cells(rows, "B").value
$lname = wrksheet.cells(rows, "C").value
$sss = wrksheet.cells(rows, "D").value
$tin = wrksheet.cells(rows, "E").value
#browser.text_field(:id, $fields[0]).set($fname)
sleep 5
#browser.text_field(:id, $fields[1]).set($mname)
sleep 5
#browser.text_field(:id, $fields[2]).set($lname)
sleep 5
#browser.text_field(:id, $fields[3]).set($sss)
sleep 5
#browser.text_field(:id, $fields[4]).set($tin)
#browser.send_keys :tab
rows += 1
for i in 0..4
if #browser.text_field(:id => $fields[i], :aria_invalid => "false")
$number_of_success = $number_of_success + 1
else
$number_of_fails = $number_of_fails + 1
end
end
end
puts "Number of Success: #{$number_of_success}"
puts "Number of Failures: #{$number_of_fails}"
end
end
The result:
User1#DOCUMENTATIONS /c/testing
$ ruby revised_login.rb
Warning: you should require 'minitest/autorun' instead.
Warning: or add 'gem "minitest"' before 'require "minitest/autorun"'
From:
c:/Ruby193/lib/ruby/1.9.1/minitest/autorun.rb:14:in `<top (required)>'
revised_login.rb:2:in `<main>'
MiniTest::Unit.autorun is now Minitest.autorun. From c:/Ruby193/lib/ruby/1.9.1/m
initest/autorun.rb:18:in `<top (required)>'
MiniTest::Unit::TestCase is now Minitest::Test. From revised_login.rb:5:in `<mai
n>'
Run options: --seed 13926
# Running:
Number of Success: 30
Number of Failures: 0
.
Finished in 287.038568s, 0.0035 runs/s, 0.0000 assertions/s.
1 runs, 0 assertions, 0 failures, 0 errors, 0 skips
User1#DOCUMENTATIONS /c/testing
$
Try to add this methods in your condition..
exists? – Returns whether this element actually exists.
present? – Returns true if the element exists and is visible on the
page
visible? – If any parent element isn’t visible then we cannot write
to the element. The only reliable way to determine this is to iterate
up the DOM element tree checking every element to make sure it’s
visible.
The ruby IF statement expects you to follow it with something that will return True or False. If you do not do that, If is going to 'punt' and treat anything returned that is not null, zero, or 'false' as TRUE. The method you are invoking is going to return a handle to an object not true or false and thus IF is going to treat that as 'true'
to prove this, use irb to create a watir browser object, navigate to a web page, and then try the following (note that the page I am on has no text field that matched the selection criteria)
irb(main):009:0> puts b.text_field(:aria_invalid => 'false')
#<Watir::TextField:0x00000001f042a8>
=> nil
irb(main):010:0> puts b.text_field(:aria_invalid => 'false').exists?
false
irb(main):011:0> puts "true" if b.text_field(:aria_invalid => 'false')
true
=> nil
irb(main):012:0> puts "false" unless b.text_field(:aria_invalid => 'false').exists?
false
=> nil
irb(main):016:0> puts b.text_field(:aria_invalid => 'false').exists? ? "true" : "false"
false
=> nil
you can see that the IF treats the (effectively empty) object being returned as 'true' even when the object does not exist in the dom. You need to add the .exists? method, then we get back a true or a false, and you can see that now the conditional responds as we would expect given the existence or non-existence of the object.
I have a script that runs perfectly in the ChromeWebDriver but fails on PhantomJS. When I check if an element exists i get the following error:
[ERROR - 2014-01-07T19:31:55.878Z] WebElementLocator - _handleLocateCommand - El
ement(s) NOT Found: GAVE UP. Search Stop Time: 1389123115867
This doesn't really seem like an issue as the script continues. However, later on the script will fail unable to locate the following element:
question.div(:class => "choices")
This particular script visits a page that has test questions on it. They are in random order. The script decided what kind of question it is and chooses a random answer.
Thanks for any help. Here is the relevant code:
def QuestionType(question)
if question.div(:class => "questionPrompt").text_field.exists?
puts "FITB"
FITB(question)
#elsif question.div(:class => "choices").ul(:class =>"choices-list").li(:index => 1).checkbox.exists?
elsif question.checkbox.exists?
puts "Checkbox"
Checkbox(question)
else
puts "Radio"
Radio(question)
end
end
def FITB(question)
arn = Random.new.rand(0..10)
if arn == 0
answers.li(:index => arn).radio.set
else
idx = 0
begin
question.div(:class => "questionPrompt").text_field(:index => idx).set("Test #{idx}")
idx = idx + 1;
end while question.div(:class => "questionPrompt").text_field(:index => idx).exists?
end
puts "FITB Complete"
end
def Checkbox(question)
allAnswers = question.div(:class => "choices")
answers = allAnswers.ul
max = answers.lis.length - 1
arn = Random.new.rand(0..max)
if arn == 0
answers.li(:index => arn).radio.set
else
for i in 1..arn
answers.li(:index => i).checkbox.set
end
end
puts "Checkbox Complete"
end
def Radio(question)
allAnswers = question.div(:class => "choices")
answers = allAnswers.ul
max = answers.lis.length - 1
arn = Random.new.rand(0..max)
answers.li(:index => arn).radio.set
puts "Radio Complete"
end