pve/lib/pve/cli/base.rb

240 lines
8.0 KiB
Ruby

require 'pmap'
class PVE::Cli
using IPAddress::ToSWithNetmaskForNetworks
def cli_base
cli.cmd :list, "List CT/VM-IDs", aliases: ['ls'], &lambda {|target=nil|
connect
nodes = Proxmox::Node.all
nodes.
flat_map {|n| [ n.method(:lxc), n.method(:qemu) ] }.
flat_pmap {|m| m.call.map {|c| c.vmid.to_i } }.
sort.
each {|c| puts c }
}
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)", 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 val2str v
case v
when true then 1
when false then 0
else v
end
end
def prepare_show_config cnf
r = {}
cnf.each do |k,v|
case k
when :network
v.each do |net|
s =
net.
reject {|k, v| :card == k }.
sort_by {|k, v| :name == k ? :AAAAAAAAA : k }.
map {|k, v| "#{k}=#{val2str v}" }
r[net[:card].to_sym] = s.join ','
end
when :sshkeys
r[k] = CGI.unescape( v).gsub( /^/, " "*14).gsub /\A {14}|\n\z/, ''
when :features
r[k] = v.map {|k, v| "#{k}=#{val2str v}" }.join ','
else
r[k] = val2str( v).to_s.gsub( /$^/, " "*14).gsub /\n\z/, ''
end
end
r
end
def show_config cnf, old = nil
cnf = prepare_show_config cnf
l = cnf.keys.map( &:length).max
if old
old = prepare_show_config old
(cnf.keys+old.keys).uniq.sort.each do |k|
v, o = cnf[k], old[k]
if v == o
puts "#{k}:#{' ' * (l-k.length)} #{v}"
else
puts "\e[31m#{k}:#{' ' * (l-k.length)} #{o}\e[0m" unless o.nil?
puts "\e[32m#{k}:#{' ' * (l-k.length)} #{v}\e[0m" unless v.nil?
end
end
else
cnf.sort_by {|k,_| k }.each do |k,v|
puts "#{k}:#{' ' * (l-k.length)} #{v}"
end
end
end
cli.sub :config, "CT/VM Configuration", min: 2, aliases: %w[cnf] do |ccli|
ccli.cmd :help, '', aliases: [nil, '-h', '--help'], &lambda {|*args| help ccli, *args }
ccli.cmd :set, "Set Configs for CT/VM", min: 3, &lambda {|name_or_id, *args|
if %w[-h --help].include? name_or_id
STDERR.puts "Usage: set -h|--help # Show help"
STDERR.puts " set ct|vm --CNF1=VAL1 --CNF2=VAL2 ... # Set config-value. Empty value clears field."
exit 1
end
opts = {}
until args.empty?
case arg = args.shift
when /\A--no-(\w+)\z/
opts[$1.to_sym] = false
when /\A--(\w+)=(.*)\z/
opts[$1.to_sym] = $2
when /\A--(\w+)!\z/, /\A--del-(\w+)\z/
opts[$1.to_sym] = nil
when /\A--(\w+)\z/
n = $1.to_sym
opts[n] =
if args.empty? or /\A--/ == args.first
true
else opts[n] = args.shift
end
else
raise UsageError, "Expection option to set. What do you mean with: #{arg}"
end
end
opts.each do |k, v|
opts[k] =
case v
when '' then nil
else v
end
end
%i[migrate_downtime].each do |k|
next unless opts.has_key? k
opts[k] =
case v = opts[k]
when nil, '', 'nil' then nil
else v.to_f
end
end
%i[memory background_delay balloon cores cpulimit cpuunits migrate_speed shares smp sockets vcpus swap tty].each do |k|
next unless opts.has_key? k
opts[k] =
case v = opts[k]
when nil, '', 'nil' then nil
else v.to_i
end
end
%i[unprivileged debug onboot protection template].each do |k|
next unless opts.has_key? k
opts[k] =
case v = opts[k]
when true, *%w[1 T TRUE t true True Y YES y yes Yes] then true
when false, *%w[0 F FALSE f false False N NO n no No] then false
when '', 'nil', nil then nil
else raise UsageError, "Boolean expected, given: #{v.inspect}"
end
end
connect
th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
raise UsageError, "Container or Node not found: #{name_or_id}" unless th
old = th.config
opts[:digest] ||= old[:digest]
th.cnfset opts
show_config th.config, old
}
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)
raise UsageError, "Container or Node not found: #{name_or_id}" unless th
show_config th.config
}
end
cli.cmd( :enter, "Enter Console of CT/Node", &lambda {|name_or_id|
connect
th = Proxmox::LXC.find( name_or_id) || Proxmox::Node.find_by_name( name_or_id)
raise UsageError, "Container or Node not found: #{name_or_id}" unless th
STDERR.puts "! #{$?.exitstatus}" unless th.enter
}).
completion do |*pre, arg|
completion_helper *pre, arg do |f|
complete_lxc( f) + complete_node( f)
end
end
cli.cmd( :run, "Starts CT/VM", aliases: %w[start star], &lambda {|*names_or_ids|
connect
raise UsageError, "Nothing to start?" if names_or_ids.empty?
per_argument names_or_ids, print: "\e[34;1mRunning CT/VM\e[0m %s:\n" do |name_or_id|
th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
raise UsageError, "Container or Node not found: #{name_or_id}" unless th
start th
end
}).
completion do |*pre, arg|
completion_helper *pre, arg do |f|
complete_lxc( f) + complete_qemu( f)
end
end
cli.cmd( :migrate, "Migrates (halted) CTs/VMs to an other host", min: 2, &lambda {|target, *names_or_ids, fire:, timeout:, secs:|
connect
node = Proxmox::Node.find_by_name! target
per_argument names_or_ids, print: "\e[1;34mCT/VM(s)\e[0m %s:\n" do |name_or_id|
th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
raise UsageError, "CT/VM not found: #{name_or_id}" unless th
unless th.stopped?
raise UsageError, "VM #{name_or_id} is running. Shutdown or for VM-online-migration see `help qm migrate`" if Proxmox::Qemu === th
raise UsageError, "CT #{name_or_id} is running. You have to shutdown it."
end
task = th.migrate node
wait task, text: "Migrating", timeout: timeout unless fire
end
}).tap {|c| opts_wait c }
#cli.cmd :reboot, "Reboot CT/VM (not implemented, yet)", min: 6, &lambda {|name_or_id|
# connect
# th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
# raise UsageError, "Container or Node not found: #{name_or_id}" unless th
# reboot th
#}
cli.cmd( :stop, "Stops CT/VM", min: 4, &lambda {|*names_or_ids|
connect
per_argument names_or_ids, print: "\e[34;1mStopping CT/VM\e[0m %s:\n" do |name_or_id|
th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
raise UsageError, "Container or Node not found: #{name_or_id}" unless th
stop th
end
}).
completion do |*pre, arg|
completion_helper *pre, arg do |f|
complete_lxc( f) + complete_qemu( f)
end
end
cli.cmd( :help, '', aliases: ['-h', '--help'], &lambda {|*args, full:|
if full
cli.help_full *args, output: STDERR
else
cli.help *args, output: STDERR
end
}).
opt( :full, '-f', '--[no-]full', 'Includes all commands of all subcommands.', default: false)
cli.cmd :cli, 'Opens interactive console', min: 3, aliases: [nil], &lambda {
@interactive = true
cli.interactive( File.basename($0,'.rb')).run
}
end
end