class Measured class < v %w[KiB MiBy GiByt TiByte ExiByte PetiByte].each_with_index do |m| v /= 1024 #return "%.1f %s" % [v, m] if 10 > v #return "%d %s" % [v, m] if 512 > v return "%.1f %s" % [v, m] if 512 > v end "%d PetiByte" % v end def bytes2 v r = (v.to_i / 1024 / 1024).to_s return '·' if 0 == r r. reverse. each_char. each_slice( 3). to_a. reverse. map {|a| a.reverse.join }. join " " end alias bytes bytes2 def seconds i i = i.to_i return '·' if 0 == i return "%d s" % i if 90 > i i /= 60 return "%d mi" % i if 90 > i i /= 60 return "%d hou" % i if 36 > i i /= 24 return "%d days" % i if 14 > i j = i / 7 return "%d weeks" % j if 25 > j i /= 365 return "%.1f years" if 550 > i "%dy" % i end end end class ColoredString attr_reader :string, :color_codes def initialize string, color_codes @string, @color_codes = string, color_codes end def inspect "#" end def length() @string.length end alias size length #def to_str() self end def to_s() "\e[#{@color_codes}m#{@string}\e[0m" end alias to_str to_s #alias inspect to_str include Comparable def <=>(o) @string <=> o.string end end class TablizedOutput def initialize header, stdout: nil @header = header.map &:to_s @columnc = header.size @maxs = header.map &:length @stdout ||= STDOUT @lines = [] end class B include Comparable def <=>(o) @v <=> o.v end end class V < B attr_reader :v, :s def initialize( v, s=nil) @v, @s = v, s || "#{v}" end def to_s() @s end def length() @s.length end def inspect() "#" end end class Percentage < B attr_reader :v, :w def initialize( v, w=nil) @v, @w = v, w || 10 end def length() @w end def inspect() "#" end def to_s y = w - (v*w).round x = (100*v).round r = "%*s" % [w, 0==x ? '·' : x] "\e[0m#{r[0...y]}\e[1;4;#{0.75>v ? 32 : 31}m#{r[y..-1]}\e[0m" end end def push fields fields = fields.map do |x| case x when String, ColoredString, B then x else V.new x end end @maxs = @columnc.times.map {|i| [@maxs[i], fields[i].length].max } @lines.push fields end def pushs lines lines.each &method( :push) end def print order: nil format = "#{(["\e[%%sm%% %ds\e[0m"] * @columnc).join( ' ') % @maxs}\n" ls = @lines if order eval <<-EOC, binding, __FILE__, 1+__LINE__ ls = ls.sort {|a,b| [#{order.map {|i| 0 < i ? "a[#{i-1}]" : "b[#{-i-1}]" }.join ', '}] <=> [#{order.map {|i| 0 < i ? "b[#{i-1}]" : "a[#{-i-1}]" }.join ', '}] } EOC end #ls = ls.sort_by {|e| p e; order.map &e.method(:[]) } if order @stdout.printf format, *@header.flat_map {|s|['',s]} ls.each {|l| @stdout.printf format, *l.flat_map {|s| s.is_a?(ColoredString) ? [s.color_codes, s.string] : ["", s.to_s] } } end def virt v ha = v.respond_to?( :ha) ? v.ha : nil unknown = V.new 0, '-' push [ case v.status when "running", "online" then ColoredString.new v.status, "32" when "stopped" then ColoredString.new v.status, "31" else v.status end, ha&.state || '·', case v.t when "nd" then ColoredString.new v.sid, "33" when "qm" then ColoredString.new v.sid, "35" when "ct" then ColoredString.new v.sid, "36" else v.sid end, v.name, v.node.is_a?(String) ? v.node : v.node.node, v.respond_to?(:uptime) ? V.new( v.uptime, Measured.seconds( v.uptime)) : unknown, v.respond_to?(:cpu) ? Percentage.new( v.cpu) : unknown, v.respond_to?(:mem) ? V.new( v.mem, Measured.bytes( v.mem)) : unknown, v.respond_to?(:maxmem) ? Percentage.new( v.mem/v.maxmem.to_f) : unknown, v.respond_to?(:disk) ? V.new( v.disk.to_i, Measured.bytes( v.disk.to_i)) : unknown, if v.respond_to?(:maxdisk) and 0 < v.maxdisk.to_i Percentage.new( v.disk.to_f/v.maxdisk.to_f) else unknown end, ] end end