require 'pmap' class PVE::Cli 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: '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 = Proxmox::Node.all nodes. select {|n| not node or n === node }. flat_map {|n| [ n.method(:lxc), n.method(:qemu) ] }. each {|m| m.call.each &push } to.print order: sort.each_char.map {|c| (2*c.ord[5]-1) * (' sainhucmd'.index( c.downcase)) } }). 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") 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| case v when true then [k,1] when false then [k,0] else [k,v] end }. map {|k, v| "#{k}=#{v}" } r[net[:card].to_sym] = s.join(",") end when :sshkeys r[k] = CGI.unescape(v).gsub( /^/, " "*14).gsub /\A {14}|\n\z/, '' else case v when true then v = 1 when false then v = 0 end r[k] = v.to_s.gsub( /$^/, " "*14).gsub /\n\z/, '' end end r end def show_config cnf, old = nil cnf = prepare_show_config cnf 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}:#{' ' * (12-k.length)} #{v}" else puts "\e[31m#{k}:#{' ' * (12-k.length)} #{o}\e[0m" unless o.nil? puts "\e[32m#{k}:#{' ' * (12-k.length)} #{v}\e[0m" unless v.nil? end end else cnf.sort_by{|k,v|k}.each do |k,v| puts "#{k}:#{' ' * (12-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 :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 } ccli.cmd :set, "Set Configs for CT/VM", &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--(\w+)=(.*)\z/ opts[$1.to_sym] = $2 when /\A--(\w+)\z/ opts[$1.to_sym] = args.shift else raise UsageError, "Expection option to set. What do you mean with: #{arg}" end end opts.each do |k, v| opts[k] = case v = opts[k] 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 *%w[1 T TRUE t true True Y YES y yes Yes] then true when *%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) old = th.config opts[:digest] ||= old[:digest] th.cnfset opts show_config th.config, old } 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 } cli.cmd :run, "Starts CT/VM", aliases: %w[start star], &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 start th } #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 {|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 stop th } cli.cmd 'help', '', aliases: ['-h', '--help'], &lambda {|*args| help cli, *args } cli.cmd 'cli', 'Opens interactive console', min: 3, aliases: [nil], &lambda { @interactive = true cli.interactive( File.basename($0,'.rb')).run } end end