diff --git a/lib/pve/cli.rb b/lib/pve/cli.rb index 0f999e3..b2bf2cc 100644 --- a/lib/pve/cli.rb +++ b/lib/pve/cli.rb @@ -153,6 +153,48 @@ class PVE::Cli end end + def node_opt node = nil + node &&= /\A#{node}\z/ + node ? Proxmox::Node.find_by_name( name) : Proxmox::Node.all + end + + def target_opt target = nil, &exe + if target + target = /\A#{target}\z/ + lambda {|n| exe.call n if n === target } + else + exe + end + end + + def hosting_table target:, status:, sort: + connect + to = TablizedOutput.new %w[Status HA ID Name Host Uptime CPU/% Mem/MiB Mem/% Disk/MiB Disk/%], format: '<<<<<>>>>>>' + target &&= /\A#{target}\z/i + status = + case status + when /\Asta(r(t(ed?)?)?)?\z/i, /\Aon(l(i(ne?)?)?)?\z/i, /\Ar(u(n(n(i(ng?)?)?)?)?)?\z/i, '1' + %i[started online running] + when /\Asto(p(p(ed?)?)?)?\z/i, /\Aof(f(l(i(ne?)?)?)?)?\z/i, '0' + %i[stopped offline] + when nil, '', /\Aa(ll?)?\z/i then nil + else + raise DenKn::UsageError, "Unknown state #{status}" + end + push = + if target and status + lambda {|n| to.virt n if n === target and status.include?( n.state) } + elsif target + lambda {|n| to.virt n if n === target } + elsif status + lambda {|n| to.virt n if status.include? n.state } + else + to.method :virt + end + yield push + to.print order: sort.each_char.map {|c| (2*c.ord[5]-1) * (' sainhucmd'.index( c.downcase)) } + end + def help cl, *args STDERR.puts cl.help( *args) exit 1 unless interactive? diff --git a/lib/pve/cli/base.rb b/lib/pve/cli/base.rb index 8000d5d..8c031bc 100644 --- a/lib/pve/cli/base.rb +++ b/lib/pve/cli/base.rb @@ -15,26 +15,17 @@ def cli_base each {|c| puts c } } - cli.cmd( :status, "Lists Nodes/VMs/CTs with status", &lambda {|target=nil, sort: 'n', node: nil| - connect - node &&= /\A#{node}\z/ - to = TablizedOutput.new %w[Status HA ID Name Host Uptime CPU/% Mem/MiB Mem/% Disk/MiB Disk/%] - push = - if target - target = /\A#{target}\z/ - lambda {|n| to.virt n if n === target } - else - lambda {|n| to.virt n } - end - nodes = node ? Proxmox::Node.find_by_name( name) : Proxmox::Node.all - nodes.each &push - nodes. - flat_map {|n| [ Thread.new( n, &:lxc), Thread.new( n, &:qemu) ] }. - each {|n| n.value.each &push } - to.print order: sort.each_char.map {|c| (2*c.ord[5]-1) * (' sainhucmd'.index( c.downcase)) } + cli.cmd( :status, "Lists Nodes/VMs/CTs with status", &lambda {|target=nil, sort:, node: nil, status: nil| + hosting_table target: target, status: status, sort: sort do |push| + node_opt( node). + each( &push).lazy. + flat_map {|n| [ Thread.new( n, &:lxc), Thread.new( n, &:qemu) ] }. + each {|n| n.value.each &push } + end }). - opt( :sort, '-s', '--sort=COLUMNS', "Sort by COLUMNs eg hn for host and name ([s]tatus, h[a], [i]d, [n]ame (default), [h]ost, [u]ptime, [c]pu, [m]em, [d]isk)"). - opt( :node, '-n', '--node=NODE', "List only hosted by this NODE") + opt( :sort, '-s', '--sort=COLUMNS', "Sort by COLUMNs eg hn for host and name ([s]tatus, h[a], [i]d, [n]ame (default), [h]ost, [u]ptime, [c]pu, [m]em, [d]isk)", default: 'n'). + opt( :node, '-n', '--node=NODE', "List only hosted by this NODE"). + opt( :status, '-S', '--status=STATUS', "Filter for status (running, stopped, ...) (default: no filter)") def prepare_show_config cnf r = {} @@ -144,7 +135,7 @@ def cli_base show_config th.config, old } - ccli.cmd :show, "Show Config of CT/VM", aliases: %w[s], &lambda {|name_or_id| + ccli.cmd :show, "Show Config of CT/VM", &lambda {|name_or_id| connect th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id) show_config th.config diff --git a/lib/pve/cli/ct.rb b/lib/pve/cli/ct.rb index a50387d..a1fb79b 100644 --- a/lib/pve/cli/ct.rb +++ b/lib/pve/cli/ct.rb @@ -9,25 +9,17 @@ def cli_ct end.sort.each {|c| puts c } } - ct_cli.cmd( :status, "Lists CTs with status", aliases: [nil], &lambda {|target=nil, sort: 'n', node: nil| - connect - node &&= /\A#{node}\z/ - to = TablizedOutput.new %w[Status HA ID Name Host Uptime CPU/% Mem/MiB Mem/% Disk/MiB Disk/%] - push = - if target - target = /\A#{target}\z/ - lambda {|n| to.virt n if n === target } - else - lambda {|n| to.virt n } - end - nodes = node ? Proxmox::Node.find_by_name( name) : Proxmox::Node.all - nodes. - map {|n| Thread.new( n, &:lxc) }. - each {|n| n.value.each &push } - to.print order: sort.each_char.map {|c| (2*c.ord[5]-1) * (' sainhucmd'.index( c.downcase)) } + ct_cli.cmd( :status, "Lists CTs with status", aliases: [nil], &lambda {|target=nil, sort: nil, node: nil, status: nil| + hosting_table target: target, status: status, sort: sort do |push| + node_opt( node). + each( &push).lazy. + map {|n| Thread.new n, &:lxc }. + each {|n| n.value.each &push } + end }). - opt( :sort, '-s', '--sort=COLUMNS', "Sort by COLUMNs eg hn for host and name ([s]tatus, h[a], [i]d, [n]ame (default), [h]ost, [u]ptime, [c]pu, [m]em, [d]isk)"). - opt( :node, '-n', '--node=NODE', "List only hosted by this NODE") + opt( :sort, '-s', '--sort=COLUMNS', "Sort by COLUMNs eg hn for host and name ([s]tatus, h[a], [i]d, [n]ame (default), [h]ost, [u]ptime, [c]pu, [m]em, [d]isk)", default: 'n'). + opt( :node, '-n', '--node=NODE', "List only hosted by this NODE"). + opt( :status, '-S', '--status=STATUS', "Filter for status (running, stopped, ...) (default: no filter)") ct_cli.cmd :enter, "Enter Console of CT", &lambda {|name_or_id| connect @@ -39,7 +31,7 @@ def cli_ct STDERR.puts "! #{$?.exitstatus}" unless Proxmox::LXC.find!( name_or_id).exec *command } - ct_cli.cmd( :start, "Starts CT", min: 3, &lambda {|name_or_id, node: nil, fire:, timeout:, secs:| + ct_cli.cmd( :start, "Starts CT", min: 4, &lambda {|name_or_id, node: nil, fire:, timeout:, secs:| connect ct = Proxmox::LXC.find! name_or_id start ct, node: node, fire: fire, timeout: timeout, secs: secs @@ -61,7 +53,7 @@ def cli_ct opt( :timeout, "-tTIMEOUT", "--timeout=TIMEOUT", "Wait for max TIMEOUT seconds (default: endless)", default: nil). opt( :secs, "-sSECONDS", "--seconds=SECONDS", "Check every SECONDS for state (default: 0.2)", default: 0.2) - ct_cli.cmd( :create, "Creates a new container", &lambda {|template, *options| #, fire:, timeout:, secs:, start:| + ct_cli.cmd( :create, "Creates a new container", min: 2, &lambda {|template, *options| #, fire:, timeout:, secs:, start:| if %w[-h --help].include? template STDERR.puts "Usage: ct create TEMPLATE -h # Shows template-related options" STDERR.puts " ct create TEMPLATE [OPTIONS] # Creates a container" @@ -107,7 +99,7 @@ EOU create Proxmox::LXC, template, **ctopts }) - ct_cli.cmd( :config, 'Shows current config', aliases: %w[cnf], &lambda {|name_or_id| + ct_cli.cmd( :config, 'Shows current config', aliases: %w[cnf], min: 2, &lambda {|name_or_id| connect ct = Proxmox::LXC.find! name_or_id STDOUT.puts JSON.dump( ct.config) diff --git a/lib/pve/cli/ha.rb b/lib/pve/cli/ha.rb index 04b3b7f..8f37708 100644 --- a/lib/pve/cli/ha.rb +++ b/lib/pve/cli/ha.rb @@ -9,7 +9,7 @@ def opts_ha cl end def cli_ha - cli.sub :ha, "Inspect High-Availability" do |hacli| + cli.sub :ha, "Inspect High-Availability", min: 2 do |hacli| hacli.cmd( :create, "Create HA for CT/VM", &lambda {|name_or_id, group:, comment: nil, max_relocate:, max_restart:, state:| connect th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id) @@ -19,7 +19,7 @@ def cli_ha ha.create group: group, comment: comment, max_relocate: max_relocate, max_restart: max_restart }).tap {|cl| opts_ha cl } - hacli.cmd :remove, "Remove CT/VM from HA", &lambda {|name_or_id| + hacli.cmd :remove, "Remove CT/VM from HA", min: 5, &lambda {|name_or_id| connect th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id) raise UsageError, "Container or VirtualMachine not found: #{name_or_id}" unless th @@ -63,7 +63,7 @@ def cli_ha ha.stopped! } - hacli.cmd :reset, "If state of CT/VM is failed, Proxmox will not start/stop it anyway. You have to reset state (state=disabled), first", &lambda {|name_or_id| + hacli.cmd :reset, "If state of CT/VM is failed, Proxmox will not start/stop it anyway. You have to reset state (state=disabled), first", min: 3, aliases: [:rst], &lambda {|name_or_id| connect th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id) raise UsageError, "Container or VirtualMachine not found: #{name_or_id}" unless th diff --git a/lib/pve/cli/node.rb b/lib/pve/cli/node.rb index 99dee17..f87b6bb 100644 --- a/lib/pve/cli/node.rb +++ b/lib/pve/cli/node.rb @@ -30,7 +30,7 @@ def cli_node end nod_cli.sub :task, "Inspect tasks" do |tcli| - tcli.cmd :list, "List done tasks", aliases: [nil, 'ls'], &lambda {|node| + tcli.cmd :list, "List done tasks", aliases: [:ls], &lambda {|node| connect Proxmox::Node.find_by_name!( node). tasks. diff --git a/lib/pve/cli/qm.rb b/lib/pve/cli/qm.rb index 40944fe..58dfb95 100644 --- a/lib/pve/cli/qm.rb +++ b/lib/pve/cli/qm.rb @@ -10,25 +10,17 @@ def cli_qm end.sort.each {|c| puts c } } - qm.cmd( :status, "Lists CTs with status", aliases: [nil], &lambda {|target=nil, sort: 'n', node: nil| - connect - node &&= /\A#{node}\z/ - to = TablizedOutput.new %w[Status HA ID Name Host Uptime CPU/% Mem/MiB Mem/% Disk/MiB Disk/%] - push = - if target - target = /\A#{target}\z/ - lambda {|n| to.virt n if n === target } - else - lambda {|n| to.virt n } - end - nodes = node ? Proxmox::Node.find_by_name( name) : Proxmox::Node.all - nodes. - map {|n| Thread.new( n, &:qemu) }. - each {|n| n.value.each &push } - to.print order: sort.each_char.map {|c| (2*c.ord[5]-1) * (' sainhucmd'.index( c.downcase)) } + qm.cmd( :status, "Lists CTs with status", aliases: [nil], &lambda {|target=nil, sort: nil, node: nil, status: nil| + hosting_table target: target, state: state, sort: sort do |push| + node_opt( node). + each( &push).lazy. + flat_map {|n| [ Thread.new( n, &:lxc), Thread.new( n, &:qemu) ] }. + each {|n| n.value.each &push } + end }). - opt( :sort, '-s', '--sort=COLUMNS', "Sort by COLUMNs eg hn for host and name ([s]tatus, h[a], [i]d, [n]ame (default), [h]ost, [u]ptime, [c]pu, [m]em, [d]isk)"). - opt( :node, '-n', '--node=NODE', "List only hosted by this NODE") + opt( :sort, '-s', '--sort=COLUMNS', "Sort by COLUMNs eg hn for host and name ([s]tatus, h[a], [i]d, [n]ame (default), [h]ost, [u]ptime, [c]pu, [m]em, [d]isk)", default: 'n'). + opt( :node, '-n', '--node=NODE', "List only hosted by this NODE"). + opt( :status, '-S', '--status=STATUS', "Filter for status (running, stopped, ...) (default: no filter)") qm.cmd :exec, "Executes Command in VM via qemu-guest-agent", min: 4, &lambda {|name_or_id, *command| connect diff --git a/lib/pve/cli/storage.rb b/lib/pve/cli/storage.rb index 26b06ae..5503ac5 100644 --- a/lib/pve/cli/storage.rb +++ b/lib/pve/cli/storage.rb @@ -53,24 +53,24 @@ def cli_storage connect appliances node, regexp, system, applications }). - opt( :node, '-n=NODE', '--node', 'Ask this node for appliances (any node should list the same)', default: nil). - opt( :regexp, '-r=REGEXP', '--regexp', 'Filter by template', default: nil). - opt( :system, '-s', '--system', 'Only system templates', default: nil). - opt( :applications, '-a', '--applications', 'Only applications (non system) templates', default: nil) + opt( :node, '-n=NODE', '--node', 'Ask this node for appliances (any node should list the same)', default: nil). + opt( :regexp, '-r=REGEXP', '--regexp', 'Filter by template', default: nil). + opt( :system, '-s', '--system', 'Only system templates', default: nil). + opt( :applications, '-a', '--applications', 'Only applications (non system) templates', default: nil) - cli_apl.cmd( :system, "Table of provided systems", aliases: [nil], &lambda {|node:, regexp:| + cli_apl.cmd( :system, "Table of provided systems", &lambda {|node:, regexp:| connect appliances node, regexp, true, nil }). - opt( :node, '-n=NODE', '--node', 'Ask this node for appliances (any node should list the same)', default: nil). - opt( :regexp, '-r=REGEXP', '--regexp', 'Filter by template', default: nil) + opt( :node, '-n=NODE', '--node', 'Ask this node for appliances (any node should list the same)', default: nil). + opt( :regexp, '-r=REGEXP', '--regexp', 'Filter by template', default: nil) - cli_apl.cmd( :applications, "Table of provided applications", aliases: [nil], &lambda {|node:, regexp:| + cli_apl.cmd( :applications, "Table of provided applications", &lambda {|node:, regexp:| connect appliances node, regexp, nil, true }). - opt( :node, '-n=NODE', '--node', 'Ask this node for appliances (any node should list the same)', default: nil). - opt( :regexp, '-r=REGEXP', '--regexp', 'Filter by template', default: nil) + opt( :node, '-n=NODE', '--node', 'Ask this node for appliances (any node should list the same)', default: nil). + opt( :regexp, '-r=REGEXP', '--regexp', 'Filter by template', default: nil) cli_apl.cmd( :list, "List provided appliances", aliases: ['ls'], &lambda {|node=nil, regexp:| connect diff --git a/lib/pve/helper.rb b/lib/pve/helper.rb index 2aaa9a5..3c92065 100644 --- a/lib/pve/helper.rb +++ b/lib/pve/helper.rb @@ -94,7 +94,6 @@ class ColoredString end - class TablizedOutput def initialize header, stdout: nil, format: nil @header = header.map &:to_s @@ -125,10 +124,14 @@ class TablizedOutput def inspect() "#" end def to_s - y = (v*w).round - x = (100*v).round - r = "%*s" % [w, 0==x ? '·' : x] - "\e[1;4;#{0.75>v ? 32 : 31}m#{r[0...y]}\e[0m#{r[y..-1]}" + vw = v*w + percent = (100*v).round + vwi = vw.to_i + rounded = (vw+0.5).to_i + s = "%*s" % [w, 0==percent ? '·' : percent] + pre = "\e[1;4;#{0.75>v ? 32 : 31}m" + mid = (vw % 1) > 0.5 ? "\e[2m" : "\e[0m" + "#{pre}#{s[0...vwi]}#{mid}#{s[vwi]}\e[0m#{s[(vwi+1)..-1]}" end end @@ -161,8 +164,8 @@ class TablizedOutput @stdout.puts \ @header.each_with_index.map {|s, i| "#{' ' * (@maxs[i] - s.length)}#{s}" - }.join( ' ') - ls.each_with_index do |l| + }.join( ' | ') + ls.each_with_index do |l, i| @stdout.puts \ l.each_with_index.map {|s, i| pad = ' ' * (@maxs[i] - s.length) @@ -172,7 +175,7 @@ class TablizedOutput else "#{pad}#{s}" end - }.join( ' ') + }.join( "\e[3#{i.even? ? 6 : 3}m | \e[0m") end end