mirror of
https://github.com/OpenNebula/one.git
synced 2025-01-11 05:17:41 +03:00
feature #661: Add new CLI libraries
This commit is contained in:
parent
7551c47553
commit
876e36ddf9
197
src/cli/cli_helper.rb
Normal file
197
src/cli/cli_helper.rb
Normal file
@ -0,0 +1,197 @@
|
||||
module CLIHelper
|
||||
LIST = {
|
||||
:name => "list",
|
||||
:short => "-l x,y,z",
|
||||
:large => "--list x,y,z",
|
||||
:format => Array,
|
||||
:description => "Selects columns to display with list command"
|
||||
}
|
||||
|
||||
#ORDER = {
|
||||
# :name => "order",
|
||||
# :short => "-o x,y,z",
|
||||
# :large => "--order x,y,z",
|
||||
# :format => Array,
|
||||
# :description => "Order by these columns, column starting with - means decreasing order"
|
||||
#}
|
||||
#
|
||||
#FILTER = {
|
||||
# :name => "filter",
|
||||
# :short => "-f x,y,z",
|
||||
# :large => "--filter x,y,z",
|
||||
# :format => Array,
|
||||
# :description => "Filter data. An array is specified with column=value pairs."
|
||||
#}
|
||||
#
|
||||
#HEADER = {
|
||||
# :name => "header",
|
||||
# :short => "-H",
|
||||
# :large => "--header",
|
||||
# :description => "Shows the header of the table"
|
||||
#}
|
||||
|
||||
DELAY = {
|
||||
:name => "delay",
|
||||
:short => "-d x",
|
||||
:large => "--delay x",
|
||||
:format => Integer,
|
||||
:description => "Sets the delay in seconds for top command"
|
||||
}
|
||||
|
||||
#OPTIONS = [LIST, ORDER, FILTER, HEADER, DELAY]
|
||||
OPTIONS = [LIST, DELAY]
|
||||
|
||||
# Sets bold font
|
||||
def CLIHelper.scr_bold
|
||||
print "\33[1m"
|
||||
end
|
||||
|
||||
# Sets underline
|
||||
def CLIHelper.scr_underline
|
||||
print "\33[4m"
|
||||
end
|
||||
|
||||
# Restore normal font
|
||||
def CLIHelper.scr_restore
|
||||
print "\33[0m"
|
||||
end
|
||||
|
||||
# Clears screen
|
||||
def CLIHelper.scr_cls
|
||||
print "\33[2J\33[H"
|
||||
end
|
||||
|
||||
# Moves the cursor
|
||||
def CLIHelper.scr_move(x,y)
|
||||
print "\33[#{x};#{y}H"
|
||||
end
|
||||
|
||||
# Print header
|
||||
def CLIHelper.print_header(str, underline=true)
|
||||
scr_bold
|
||||
scr_underline if underline
|
||||
print str
|
||||
scr_restore
|
||||
puts
|
||||
end
|
||||
|
||||
class ShowTable
|
||||
include CLIHelper
|
||||
|
||||
def initialize(conf=nil, ext=nil, &block)
|
||||
# merge del conf con la table
|
||||
@columns = Hash.new
|
||||
@default_columns = Array.new
|
||||
|
||||
@ext = ext
|
||||
|
||||
instance_eval(&block)
|
||||
end
|
||||
|
||||
def column(name, desc, *conf, &block)
|
||||
column = Hash.new
|
||||
column[:desc] = desc
|
||||
column[:size] = 5
|
||||
conf.each{|c|
|
||||
if c.instance_of?(Symbol)
|
||||
column[c]=true
|
||||
elsif c.instance_of?(Hash)
|
||||
c.each{|key,value|
|
||||
column[key]=value
|
||||
}
|
||||
end
|
||||
}
|
||||
column[:proc] = block
|
||||
@columns[name.to_sym] = column
|
||||
@default_columns<<name
|
||||
end
|
||||
|
||||
def default(*args)
|
||||
@default_columns=args
|
||||
end
|
||||
|
||||
def show(data, options={})
|
||||
update_columns(options)
|
||||
print_table(data, options)
|
||||
end
|
||||
|
||||
def top(data, options={})
|
||||
update_columns(options)
|
||||
delay=options[:delay] ? options[:delay] : 1
|
||||
|
||||
begin
|
||||
while true
|
||||
scr_cls
|
||||
scr_move(0,0)
|
||||
show(data, options)
|
||||
sleep delay
|
||||
end
|
||||
rescue Exception
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def print_table(data, options)
|
||||
CLIHelper.print_header(header_str)
|
||||
print_data(data, options)
|
||||
end
|
||||
|
||||
def print_data(data, options)
|
||||
ncolumns=@default_columns.length
|
||||
res_data=data_array(data, options)
|
||||
print res_data.collect{|l|
|
||||
(0..ncolumns-1).collect{ |i|
|
||||
dat=l[i]
|
||||
col=@default_columns[i]
|
||||
|
||||
if @columns[col] && @columns[col][:humanize]
|
||||
dat = @columns[col][:humanize].call(dat)
|
||||
end
|
||||
|
||||
format_str(col, dat)
|
||||
}.join(' ')
|
||||
}.join("\n")
|
||||
puts
|
||||
end
|
||||
|
||||
def data_array(data, options)
|
||||
res_data=data.collect {|d|
|
||||
@default_columns.collect {|c|
|
||||
@columns[c][:proc].call(d,@ext).to_s if @columns[c]
|
||||
}
|
||||
}
|
||||
|
||||
if options
|
||||
filter_data!(res_data, options[:filter]) if options[:filter]
|
||||
sort_data!(res_data, options[:order]) if options[:order]
|
||||
end
|
||||
|
||||
res_data
|
||||
end
|
||||
|
||||
def format_str(field, data)
|
||||
minus=( @columns[field][:left] ? "-" : "" )
|
||||
size=@columns[field][:size]
|
||||
"%#{minus}#{size}.#{size}s" % [ data.to_s ]
|
||||
end
|
||||
|
||||
def update_columns(options)
|
||||
@default_columns = options[:list].collect{|o| o.to_sym} if options[:list]
|
||||
end
|
||||
|
||||
def header_str
|
||||
@default_columns.collect {|c|
|
||||
if @columns[c]
|
||||
format_str(c, c.to_s)
|
||||
else
|
||||
""
|
||||
end
|
||||
}.compact.join(' ')
|
||||
end
|
||||
|
||||
# TBD def filter_data!
|
||||
|
||||
# TBD def sort_data!
|
||||
end
|
||||
end
|
333
src/cli/command_parser.rb
Executable file
333
src/cli/command_parser.rb
Executable file
@ -0,0 +1,333 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'optparse'
|
||||
require 'pp'
|
||||
|
||||
module CommandParser
|
||||
OPTIONS = [
|
||||
VERBOSE={
|
||||
:name => "verbose",
|
||||
:short => "-v",
|
||||
:large => "--verbose",
|
||||
:description => "Verbose mode"
|
||||
},
|
||||
HELP={
|
||||
:name => "help",
|
||||
:short => "-h",
|
||||
:large => "--help",
|
||||
:description => "Show this message"
|
||||
}
|
||||
]
|
||||
|
||||
class CmdParser
|
||||
attr_reader :options, :args
|
||||
|
||||
def initialize(args=[], &block)
|
||||
@opts = Array.new
|
||||
@commands = Hash.new
|
||||
@formats = Hash.new
|
||||
@script = nil
|
||||
@usage = ""
|
||||
|
||||
@args = args
|
||||
@options = Hash.new
|
||||
|
||||
set :format, :file, "" do |arg| format_file(arg) ; end
|
||||
set :format, :range, "" do |arg| format_range(arg) ; end
|
||||
set :format, :text, "" do |arg| format_text(arg) ; end
|
||||
|
||||
instance_eval(&block)
|
||||
|
||||
self.run
|
||||
end
|
||||
|
||||
def usage(str)
|
||||
@usage = "Usage: #{str}"
|
||||
end
|
||||
|
||||
def set(e, *args, &block)
|
||||
case e
|
||||
when :option
|
||||
add_option(args[0])
|
||||
when :format
|
||||
add_format(args[0], args[1], block)
|
||||
end
|
||||
end
|
||||
|
||||
def command(name, desc, *args_format, &block)
|
||||
cmd = Hash.new
|
||||
cmd[:desc] = desc
|
||||
cmd[:arity] = 0
|
||||
cmd[:options] = []
|
||||
cmd[:args_format] = Array.new
|
||||
args_format.each {|args|
|
||||
if args.instance_of?(Array)
|
||||
cmd[:arity]+=1 unless args.include?(nil)
|
||||
cmd[:args_format] << args
|
||||
elsif args.instance_of?(Hash) && args[:options]
|
||||
cmd[:options] << args[:options]
|
||||
else
|
||||
cmd[:arity]+=1
|
||||
cmd[:args_format] << [args]
|
||||
end
|
||||
}
|
||||
cmd[:proc] = block
|
||||
@commands[name] = cmd
|
||||
end
|
||||
|
||||
def script(*args_format, &block)
|
||||
@script=Hash.new
|
||||
@script[:args_format] = Array.new
|
||||
args_format.collect {|args|
|
||||
if args.instance_of?(Array)
|
||||
@script[:arity]+=1 unless args.include?(nil)
|
||||
@script[:args_format] << args
|
||||
elsif args.instance_of?(Hash) && args[:options]
|
||||
@opts << args[:options]
|
||||
else
|
||||
@script[:arity]+=1
|
||||
@script[:args_format] << [args]
|
||||
end
|
||||
}
|
||||
|
||||
@script[:proc] = block
|
||||
end
|
||||
|
||||
def run
|
||||
comm_name=""
|
||||
if @script
|
||||
comm=@script
|
||||
elsif
|
||||
if @args[0] && !@args[0].match(/^-/)
|
||||
comm_name=@args.shift.to_sym
|
||||
comm=@commands[comm_name]
|
||||
end
|
||||
end
|
||||
|
||||
if comm.nil?
|
||||
help
|
||||
exit -1
|
||||
end
|
||||
|
||||
extra_options = comm[:options] if comm
|
||||
parse(extra_options)
|
||||
if comm
|
||||
check_args!(comm_name, comm[:arity], comm[:args_format])
|
||||
|
||||
begin
|
||||
rc = comm[:proc].call
|
||||
rescue Exception =>e
|
||||
puts e.message
|
||||
exit -1
|
||||
end
|
||||
|
||||
if rc.instance_of?(Array)
|
||||
puts rc[1]
|
||||
exit rc.first
|
||||
else
|
||||
exit rc
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def help
|
||||
puts @usage
|
||||
puts
|
||||
print_options
|
||||
puts
|
||||
print_commands
|
||||
puts
|
||||
print_formatters
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def print_options
|
||||
puts "Options:"
|
||||
|
||||
shown_opts = Array.new
|
||||
opt_format = "#{' '*5}%-25s %s"
|
||||
@commands.each{ |key,value|
|
||||
value[:options].flatten.each { |o|
|
||||
if shown_opts.include?(o[:name])
|
||||
next
|
||||
else
|
||||
shown_opts << o[:name]
|
||||
short = o[:short].split(' ').first
|
||||
printf opt_format, "#{short}, #{o[:large]}", o[:description]
|
||||
puts
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
@opts.each{ |o|
|
||||
printf opt_format, "#{o[:short]}, #{o[:large]}", o[:description]
|
||||
puts
|
||||
}
|
||||
end
|
||||
|
||||
def print_commands
|
||||
puts "Commands:"
|
||||
|
||||
cmd_format5 = "#{' '*5}%s"
|
||||
cmd_format10 = "#{' '*10}%s"
|
||||
@commands.each{ |key,value|
|
||||
printf cmd_format5, "* #{key}"
|
||||
puts
|
||||
|
||||
args_str=value[:args_format].collect{ |a|
|
||||
if a.include?(nil)
|
||||
"[#{a.compact.join("|")}]"
|
||||
else
|
||||
a.join("|")
|
||||
end
|
||||
}.join(' ')
|
||||
printf cmd_format10, "arguments: #{args_str}"
|
||||
puts
|
||||
|
||||
value[:desc].split("\n").each { |l|
|
||||
printf cmd_format10, l
|
||||
puts
|
||||
}
|
||||
|
||||
unless value[:options].empty?
|
||||
opts_str=value[:options].flatten.collect{|o|
|
||||
o[:name]
|
||||
}.join(', ')
|
||||
printf cmd_format10, "options: #{opts_str}"
|
||||
puts
|
||||
end
|
||||
puts
|
||||
}
|
||||
end
|
||||
|
||||
def print_formatters
|
||||
puts "argument formats:"
|
||||
|
||||
cmd_format5 = "#{' '*5}%s"
|
||||
cmd_format10 = "#{' '*10}%s"
|
||||
@formats.each{ |key,value|
|
||||
printf cmd_format5, "* #{key}"
|
||||
puts
|
||||
|
||||
value[:desc].split("\n").each { |l|
|
||||
printf cmd_format10, l
|
||||
puts
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def add_option(option)
|
||||
if option.instance_of?(Array)
|
||||
option.each { |o| @opts << o }
|
||||
elsif option.instance_of?(Hash)
|
||||
@opts << option
|
||||
end
|
||||
end
|
||||
|
||||
def add_format(format, description, block)
|
||||
@formats[format] = {
|
||||
:desc => description,
|
||||
:proc => block
|
||||
}
|
||||
end
|
||||
|
||||
def parse(extra_options)
|
||||
@cmdparse=OptionParser.new do |opts|
|
||||
merge = @opts
|
||||
merge = @opts + extra_options if extra_options
|
||||
merge.flatten.each do |e|
|
||||
opts.on(e[:short],e[:large], e[:format],e[:description]) do |o|
|
||||
if e[:proc]
|
||||
e[:proc].call
|
||||
elsif e[:name]=="help"
|
||||
help
|
||||
#puts opts
|
||||
exit
|
||||
else
|
||||
@options[e[:name].to_sym]=o
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
@cmdparse.parse!(@args)
|
||||
rescue => e
|
||||
puts e.message
|
||||
exit -1
|
||||
end
|
||||
end
|
||||
|
||||
def check_args!(name, arity, args_format)
|
||||
if @args.length < arity
|
||||
print "Command #{name} requires "
|
||||
if arity>1
|
||||
puts "#{args_format.length} parameters to run."
|
||||
else
|
||||
puts "one parameter to run"
|
||||
end
|
||||
exit -1
|
||||
else
|
||||
id=0
|
||||
@args.collect!{|arg|
|
||||
format = args_format[id]
|
||||
argument = nil
|
||||
error_msg = nil
|
||||
format.each { |f|
|
||||
rc = @formats[f][:proc].call(arg) if @formats[f]
|
||||
if rc[0]==0
|
||||
argument=rc[1]
|
||||
break
|
||||
else
|
||||
error_msg=rc[1]
|
||||
next
|
||||
end
|
||||
}
|
||||
|
||||
unless argument
|
||||
puts error_msg if error_msg
|
||||
puts "command #{name}: argument #{id} must be one of #{format.join(', ')}"
|
||||
exit -1
|
||||
end
|
||||
|
||||
id+=1
|
||||
argument
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
########################################################################
|
||||
# Formatters for arguments
|
||||
########################################################################
|
||||
def format_text(arg)
|
||||
arg.instance_of?(String) ? [0,arg] : [-1]
|
||||
end
|
||||
|
||||
def format_file(arg)
|
||||
File.exists?(arg) ? [0,arg] : [-1]
|
||||
end
|
||||
|
||||
REG_RANGE=/^(?:(?:\d+\.\.\d+|\d+),)*(?:\d+\.\.\d+|\d+)$/
|
||||
|
||||
def format_range(arg)
|
||||
arg_s = arg.gsub(" ","").to_s
|
||||
return [-1] unless arg_s.match(REG_RANGE)
|
||||
|
||||
ids = Array.new
|
||||
arg_s.split(',').each { |e|
|
||||
if e.match(/^\d+$/)
|
||||
ids << e.to_i
|
||||
elsif m = e.match(/^(\d+)\.\.(\d+)$/)
|
||||
ids += (m[1].to_i..m[2].to_i).to_a
|
||||
else
|
||||
return [-1]
|
||||
end
|
||||
}
|
||||
|
||||
return 0,ids.uniq
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user