bin/crowbar_node_state
#!/usr/bin/env ruby
#
# Copyright 2011-2013, Dell
# Copyright 2013-2014, SUSE LINUX Products GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require "rubygems"
require "net/http"
require "net/http/digest_auth"
require "uri"
require "inifile"
require "json"
require "getoptlong"
@debug = false
@hostname = ENV["CROWBAR_IP"]
@hostname = "127.0.0.1" unless @hostname
@port = 80
@headers = {
"Accept" => "application/json",
"Content-Type" => "application/json"
}
@data = ""
@allow_zero_args = false
@timeout = 500
@barclamp="machines"
@noready = false
#
# Parsing options can be added by adding to this list before calling opt_parse
#
@options = [
[["--help", "-h", GetoptLong::NO_ARGUMENT], "--help or -h - help"],
[["--username", "-U", GetoptLong::REQUIRED_ARGUMENT], "--username <username> or -U <username> - specifies the username to use"],
[["--password", "-P", GetoptLong::REQUIRED_ARGUMENT], "--password <password> or -P <password> - specifies the password to use"],
[["--hostname", "-n", GetoptLong::REQUIRED_ARGUMENT], "--hostname <name or ip> or -n <name or ip> - specifies the destination server"],
[["--port", "-p", GetoptLong::REQUIRED_ARGUMENT], "--port <port> or -p <port> - specifies the destination server port"],
[["--debug", "-d", GetoptLong::NO_ARGUMENT], "--debug or -d - turns on debugging information"],
[["--timeout", GetoptLong::REQUIRED_ARGUMENT], "--timeout <seconds> - timeout in seconds for read http reads"],
[["--no-ready", GetoptLong::NO_ARGUMENT], "--no-ready - ignore ready nodes"]
]
@commands = {
"status" => ["status", "status - show status of nodes"]
}
def print_commands(cmds, spacer = " ")
cmds.each do |key, command|
puts "#{spacer}#{command[1]}"
print_commands(command[2], " #{spacer}") if command[0] =~ /run_sub_command\(/
end
end
def usage (rc)
puts "Usage: crowbar node_state [options] <subcommands>"
@options.each do |options|
puts " #{options[1]}"
end
print_commands(@commands.sort)
exit rc
end
def help
usage 0
end
def debug(msg)
puts msg if @debug
end
def authenticate(req, uri, data = nil, limit = 10)
uri.user = @username
uri.password = @password
h = Net::HTTP.new uri.host, uri.port
if uri.instance_of? URI::HTTPS
h.verify_mode = OpenSSL::SSL::VERIFY_NONE
h.use_ssl = true
end
h.read_timeout = @timeout
r = req.new uri.request_uri, @headers
r.body = data if data
res = h.request r
debug "(r) hostname: #{uri.host}:#{uri.port}"
debug "(r) request: #{uri.path}"
debug "(r) method: #{req::METHOD}"
debug "(r) return code: #{res.code}"
debug "(r) return body: #{res.body}"
res.each_header do |h, v|
debug "(r) return #{h}: #{v}"
end
if res.header["location"]
uri = URI.parse(res.header["location"])
return authenticate(req, uri, data, limit)
end
if res["www-authenticate"]
digest = Net::HTTP::DigestAuth.new
auth = digest.auth_header uri, res["www-authenticate"], req::METHOD
r = req.new uri.request_uri, @headers
r.body = data if data
r.add_field "Authorization", auth
res = h.request r
debug "(a) hostname: #{uri.host}:#{uri.port}"
debug "(a) request: #{uri.path}"
debug "(a) method: #{req::METHOD}"
debug "(a) return code: #{res.code}"
debug "(a) return body: #{res.body}"
res.each_header do |h, v|
debug "(a) return #{h}: #{v}"
end
end
res
end
def get_json(path)
uri = URI.parse("http://#{@hostname}:#{@port}/nodes#{path}")
res = authenticate(Net::HTTP::Get,uri)
puts "DEBUG: (g) hostname: #{uri.host}:#{uri.port}" if @debug
puts "DEBUG: (g) request: #{uri.path}" if @debug
puts "DEBUG: (g) return code: #{res.code}" if @debug
puts "DEBUG: (g) return body: #{res.body}" if @debug
return [res.body, res.code.to_i] if res.code.to_i != 200
struct = JSON.parse(res.body)
puts "DEBUG: (g) JSON parse structure = #{struct.inspect}" if @debug
return [struct, 200]
end
def status()
struct = get_json("/status")
if struct[1] == 200
string = ""
struct[0]["nodes"].keys.sort.each do |n|
status = struct[0]["nodes"][n]["status"]
string += "#{n} #{status}\n" unless @noready and status == "Ready"
end
["#{string}", 0]
elsif struct[1] == 404
["No current configuration for status", 1]
else
["Failed to talk to service show: #{struct[1]}: #{struct[0]}", 1]
end
end
def get_user_password
@username = ENV["CROWBAR_USERNAME"]
@password = ENV["CROWBAR_PASSWORD"]
return unless @username.nil?
[
File.join(ENV["HOME"], ".crowbarrc"),
File.join("/etc", "crowbarrc")
].each do |file|
next unless File.exist?(file)
begin
site = ENV["CROWBAR_ALIAS"] || "default"
config = IniFile.load(file).to_h[site]
@username = config["username"]
@password = config["password"]
rescue IniFile::Error
STDERR.puts "Could not parse config file \"#{file}\""
end
end
end
def opt_parse()
get_user_password
sub_options = @options.map { |x| x[0] }
lsub_options = @options.map { |x| [x[0][0], x[2]] }
opts = GetoptLong.new(*sub_options)
opts.each do |opt, arg|
case opt
when "--help"
usage 0
when "--debug"
@debug = true
when "--no-ready"
@noready = true
when "--hostname"
@hostname = arg
when "--username"
@username = arg
when "--password"
@password = arg
when "--port"
@port = arg.to_i
when "--data"
@data = arg
when "--timeout"
@timeout = arg
when "--file"
data = File.read(arg)
else
found = false
lsub_options.each do |x|
next if x[0] != opt
eval x[1]
found = true
end
usage -1 unless found
end
end
if ARGV.length == 0 and !@allow_zero_args
usage -1
end
if @username.nil? or @password.nil?
STDERR.puts "Incomplete credentials, will not be able to authenticate!"
STDERR.puts "Please create a crowbarrc configuration file, " \
"set CROWBAR_USERNAME and CROWBAR_PASSWORD, or use -U and -P"
exit 1
end
end
def run_sub_command(cmds, subcmd)
cmd = cmds[subcmd]
usage -2 if cmd.nil?
eval cmd[0]
end
def run_command()
run_sub_command(@commands, ARGV.shift)
end
def main()
opt_parse
res = run_command
puts res[0]
exit res[1]
end
main