Class: PVE::Cli
- Inherits:
-
Object
- Object
- PVE::Cli
- Defined in:
- lib/pve/cli/ct.rb,
lib/pve/cli/ha.rb,
lib/pve/cli/qm.rb,
lib/pve/cli/base.rb,
lib/pve/cli/node.rb,
lib/pve/cli/task.rb,
lib/pve/cli/storage.rb,
lib/pve/cli.rb
Instance Attribute Summary collapse
-
#cfg ⇒ Object
readonly
Returns the value of attribute cfg.
-
#cli ⇒ Object
readonly
Returns the value of attribute cli.
Instance Method Summary collapse
- #appliances(node, regexp, system, applications) ⇒ Object
- #call(*argv) ⇒ Object
- #cli_base ⇒ Object
- #cli_ct ⇒ Object
- #cli_ha ⇒ Object
- #cli_node ⇒ Object
- #cli_qm ⇒ Object
- #cli_storage ⇒ Object
- #cli_task ⇒ Object
- #complete_lxc(f) ⇒ Object
- #complete_node(f) ⇒ Object
- #complete_qemu(f) ⇒ Object
- #completion_helper(*pre, arg, &exe) ⇒ Object
- #connect ⇒ Object
- #create(klass, template, timeout: nil, fire: nil, secs: nil, start: nil, **options) ⇒ Object
- #destroy(ct, timeout: nil, fire: nil, secs: nil) ⇒ Object
- #enter(host, *args) ⇒ Object
- #help(cl, *args) ⇒ Object
-
#initialize(config_file = nil) ⇒ Cli
constructor
A new instance of Cli.
- #interactive? ⇒ Boolean
- #opts_ha(cl) ⇒ Object
- #opts_wait(cl) ⇒ Object
- #prepare ⇒ Object
- #prepare_show_config(cnf) ⇒ Object
- #show_config(cnf, old = nil) ⇒ Object
- #start(host, node: nil, timeout: nil, fire: nil, secs: nil) ⇒ Object
- #stop(host, timeout: nil, fire: nil, secs: nil) ⇒ Object
- #task_log(task, logn, limit = 1024) ⇒ Object
- #wait(task, secs: nil, text: nil) ⇒ Object
- #wait_state(host, state, timeout: nil, lock: nil) ⇒ Object
Constructor Details
#initialize(config_file = nil) ⇒ Cli
Returns a new instance of Cli.
39 40 41 42 43 44 45 46 47 |
# File 'lib/pve/cli.rb', line 39 def initialize config_file = nil config_file ||= '/etc/pve/pvecli.yml' @cfg = YAML.safe_load File.read( config_file), [], [], false, config_file, symbolize_names: true @cli = DenCli.new File.basename($0), "PVE CLI" @interactive = false prepare end |
Instance Attribute Details
#cfg ⇒ Object (readonly)
Returns the value of attribute cfg.
29 30 31 |
# File 'lib/pve/cli.rb', line 29 def cfg @cfg end |
#cli ⇒ Object (readonly)
Returns the value of attribute cli.
29 30 31 |
# File 'lib/pve/cli.rb', line 29 def cli @cli end |
Instance Method Details
#appliances(node, regexp, system, applications) ⇒ Object
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/pve/cli.rb', line 217 def appliances node, regexp, system, applications system = applications = true if system.nil? and applications.nil? node = node ? Proxmox::Node.find_by_name!( node) : Proxmox::Node.all.first to = TablizedOutput.new %w<Section Package Version OS Template Description>, format: %w[> > > > > <] node.aplinfo. select {|a| 'system' == a.section ? system : applications}. each do |apl| to.push [ apl.section, apl.package, apl.version, apl.os, apl.template, apl.description, ] end to.print order: [1,2] end |
#call(*argv) ⇒ Object
208 209 210 211 212 213 214 215 |
# File 'lib/pve/cli.rb', line 208 def call *argv cli.call *argv rescue RestClient::ExceptionWithResponse STDERR.puts "#$! - #{$!.response} (#{$!.class})" #, $!.backtrace.map {|b|" #{b}"} rescue UsageError, DenCli::UsageError STDERR.puts $! exit 1 end |
#cli_base ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/pve/cli/base.rb', line 7 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 = 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)) } }). 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 :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--(\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 } ccli.cmd :show, "Show Config of CT/VM", aliases: %w[s], &lambda {|name_or_id| connect th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id) 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 {|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 }). completion do |*pre, arg| completion_helper *pre, arg do |f| complete_lxc( f) + complete_qemu( f) end end #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 }). 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 |
#cli_ct ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/pve/cli/ct.rb', line 2 def cli_ct cli.sub :ct, "Containers", aliases: %w[lx lxc] do |ct_cli| ct_cli.cmd :list, "List CT-IDs", aliases: ['ls'], &lambda {|node=nil| connect nodes = node ? Proxmox::Node.find_by_name( name) : Proxmox::Node.all nodes.flat_map do |n| n.lxc.map {|c| c.vmid.to_i } 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)) } }). 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") ct_cli.cmd :enter, "Enter Console of CT", &lambda {|name_or_id| connect STDERR.puts "! #{$?.exitstatus}" unless Proxmox::LXC.find!( name_or_id).enter } ct_cli.cmd :exec, "Executes Command in CT", min: 4, &lambda {|name_or_id, *command| connect 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:| connect ct = Proxmox::LXC.find! name_or_id start ct, node: node, fire: fire, timeout: timeout, secs: secs }). opt( :node, "-nNODE", "--node=NODE", "On NODE (default, as is, so without migration)"). tap {|c| opts_wait c } ct_cli.cmd( :stop, "Stops CT", min: 3, &lambda {|name_or_id, fire: nil, timeout:, secs:| connect ct = Proxmox::LXC.find! name_or_id stop ct, fire: fire, timeout: timeout, secs: secs }).tap {|c| opts_wait c } ct_cli.cmd( :wait, "Wait till CT is in state", &lambda {|name_or_id, state, timeout: nil, secs: nil| connect ct = Proxmox::LXC.find! name_or_id wait ct, state, timeout: timeout, secs: secs }). 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, *| #, 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" STDERR.puts " ct create -l # Listing available templates" exit 1 elsif %w[-l --list].include? template STDERR.puts PVE::CTTemplate.constants.reject {|c|:Base==c}.map {|c|c.to_s.titlecase.dasherize.downcase} exit 0 end ctopts = {} OptionParser.new do |opts| ctt = PVE::CTTemplate.const_get template.classify opts. = <<EOU Usage: ct create #{template} [options] #{ctt.help} Options: (*=Required) EOU opts.on '-h', '--help', " Help!" do STDERR.puts opts exit 1 unless interactive? return end opts.on( '-r', '--[no-]-start', " Start container after creation") {|v| ctopts[:start] = v } opts.on( '-f', '--[no-]-fire', " Do not wait till running") {|v| ctopts[:start] = v } opts.on( '-t', '--timeout=TIMEOUT', " Wait for max TIMEOUT seconds (default: endless)") {|v| ctopts[:timeout] = v } opts.on( '-s', '--seconds=SECONDS', " Check every SECONDS for state (default: 0.2)") {|v| ctopts[:seconds] = v } ctt.requirements.each do |name, (type, req, desc, *args)| req = req ? "*" : " " case type when :boolean opts.on( "--[no-]#{name}", "#{req}#{desc}") {|v| ctopts[name] = v } when :string, :numeric opts.on( "--#{name}=#{type.upcase}", "#{req}#{desc}") {|v| ctopts[name] = v } when :enum opts.on( "--#{name}=#{type.upcase}", "#{req}#{desc} (#{args.first.join ', '})") do |v| ctopts[name] = v end end end end.parse! connect create Proxmox::LXC, template, **ctopts }) ct_cli.cmd( :config, 'Shows current config', aliases: %w[cnf], &lambda {|name_or_id| connect ct = Proxmox::LXC.find! name_or_id STDOUT.puts JSON.dump( ct.config) }) ct_cli.cmd( :resize, 'Resize a disk', &lambda {|name_or_id, disk, size| connect ct = Proxmox::LXC.find! name_or_id task = ct.resize disk, size wait task, text: "Resizing #{ct.sid} #{disk} to #{size}" }) ct_cli.cmd( :destroy, '', min: 7, &lambda {|name_or_id, fire:, secs:, timeout:, i_really_want_to_destroy:| raise UsageError, "Name/ID is not what you want to destroy" unless name_or_id == i_really_want_to_destroy connect ct = Proxmox::LXC.find! name_or_id raise UsageError, "Container is not stopped" unless ct.stopped? destroy ct, fire: fire, timeout: timeout, secs: secs }).tap {|c| opts_wait c }. opt( :i_really_want_to_destroy, "--i-really-want-to-destroy=NAMEORID", "Repeat the name/ID") ct_cli.cmd( :help, '', aliases: ['-h', '--help']) {|*args| help ct_cli, *args } end end |
#cli_ha ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/pve/cli/ha.rb', line 11 def cli_ha cli.sub :ha, "Inspect High-Availability" 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) raise UsageError, "Container or VirtualMachine not found: #{name_or_id}" unless th ha = th.ha raise UsageError, "#{th.sid} is already High-Available" if ha.active? 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| 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 ha = th.ha raise UsageError, "#{th.sid} is not High-Available" unless ha.active? ha.delete } hacli.cmd( :active, "CT/VM should be high-available. Options are only for defaults, if not activated, yet.", &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) raise UsageError, "Container or VirtualMachine not found: #{name_or_id}" unless th ha = th.ha ha.create group: group, comment: comment, max_relocate: max_relocate, max_restart: max_restart if ha.active? }).tap {|cl| opts_ha cl } hacli.cmd :deactive, "CT/VM should NOT be high-available.", &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 ha = th.ha ha.delete unless ha.active? } hacli.cmd( :started, "CT/VM should be in state started. By stopping CT/VM via pct/e state will be changed in HA, too.", &lambda {|name_or_id, force: nil| 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 ha = th.ha ha = ha.create unless ha.active? ha.disabled! if force and ha.error? ha.started! }).opt( :force, "-f", "--force", "If CT/VM is in error-state, first reset HA, than try to start.") hacli.cmd :stopped, "CT/VM should be in state stopped. By starting CT/VM via pct/e state will be changed in HA, too.", min: 3, &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 ha = th.ha ha = ha.create unless ha.active? 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| 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 ha = th.ha raise UsageError, "#{th.sid} is not High-Available" if ha.active? ha.state = :disabled } hacli.cmd 'help', '', aliases: ['-h', '--help'], &lambda {|*args| help hacli, *args } end end |
#cli_node ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/pve/cli/node.rb', line 2 def cli_node cli.sub :node, "Nodes" do |nod_cli| nod_cli.cmd :status, "Lists nodes with status", aliases: [nil], &lambda {|node=nil| connect to = TablizedOutput.new %w[Status Node Uptime CPU Mem/MiB Disk/MiB] nodes = Proxmox::Node.all nodes = nodes.select {|n| node == n.name } if node nodes.each do |n| to.push [ n.status, n.node, Measured.seconds( n.uptime), "%.02f/%d" % [n.cpu, n.maxcpu], "#{Measured.bytes( n.mem)}/#{Measured.bytes( n.maxmem)}", "#{Measured.bytes( n.disk)}/#{Measured.bytes( n.maxdisk)}", ] end to.print order: [1] } nod_cli.cmd :exec, "Executes command on node", min: 4 do |name, *args| connect STDERR.puts "! #{$?.exitstatus}" unless Proxmox::Node.find_by_name!( name).exec *args end nod_cli.cmd :enter, "Enter Console of node" do |name, *args| connect STDERR.puts "! #{$?.exitstatus}" unless Proxmox::Node.find_by_name!( name).enter *args end nod_cli.sub :task, "Inspect tasks" do |tcli| tcli.cmd :list, "List done tasks", aliases: [nil, 'ls'], &lambda {|node| connect Proxmox::Node.find_by_name!( node). tasks. map( &:upid). sort. each {|upid| puts upid } } end nod_cli.cmd( 'help', '', aliases: ['-h', '--help']) {|*args| help nod_cli, *args } end end |
#cli_qm ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/pve/cli/qm.rb', line 2 def cli_qm cli.sub :qm, "Virtual Machines", aliases: %w[v vm qemu], &lambda {|qm| qm.cmd :list, "List VM-IDs", aliases: ['ls'], &lambda {|node=nil| connect nodes = Proxmox::Node.all nodes = nodes.select {|n| node == n.name } if node nodes.flat_map do |n| n.qemu.map {|c| c.vmid.to_i } 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)) } }). 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") qm.cmd :exec, "Executes Command in VM via qemu-guest-agent", min: 4, &lambda {|name_or_id, *command| connect STDERR.puts "! #{$?.exitstatus}" unless Proxmox::Qemu.find!( name_or_id).exec *command } qm.cmd( :resize, 'Resize a disk', &lambda {|name_or_id, disk, size| connect qm = Proxmox::Qemu.find! name_or_id task = qm.resize disk, size wait task, text: "Resizing #{qm.sid} #{disk} to #{size}" }) qm.cmd 'help', '', aliases: ['-h', '--help'], &lambda {|*args| help qm, *args } } end |
#cli_storage ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/pve/cli/storage.rb', line 2 def cli_storage cli.sub :storage, "Storages", min: 3 do |cli_sm| cli_sm.cmd :list, "List Storages", aliases: ['ls'], &lambda {|node=nil| connect nodes = node ? [Proxmox::Node.find_by_name!( node)] : Proxmox::Node.all nodes.flat_map do |n| n.lxc.map {|c| c.vmid.to_i } end.sort.each {|c| puts c } } cli_sm.cmd :status, "List Storages with status", aliases: [nil], &lambda {|node=nil| connect to = TablizedOutput.new %w[A E S Storage Host Type] nodes = node ? [Proxmox::Node.find_by_name!( node)] : Proxmox::Node.all nodes.each do |n| n.storage.each do |v| to.push [ case v.active when 1 then ColoredString.new 'Y', "32" when 0 then ColoredString.new 'n', "31" else v.active.to_s end, case v.enabled when 1 then ColoredString.new 'Y', "32" when 0 then ColoredString.new 'n', "31" else v.enabled.to_s end, 1 == v.shared ? 's' : 'l', v.storage, v.node.node, v.type ] end end to.print order: [4,5] } cli_sm.sub :content, "Content of Storage", aliases: ['cnt'] do |cli_cnt| cli_cnt.cmd :list, "List Content", aliases: ['ls'], &lambda {|node=nil, storage| connect node = node ? Proxmox::Node.find_by_name!( node) : Proxmox::Node.all.first storage = node.storage.select {|sm| storage == sm.storage }.first storage.content.each {|c| puts c.to_s } } cli_cnt.cmd( :help, '', aliases: ['-h', '--help']) {|*args| help cli_cnt, *args } end cli_sm.cmd( :help, '', aliases: ['-h', '--help']) {|*args| help cli_sm, *args } #cli_sm.provide_help end cli.sub :apl, "Appliances - Downloadable container images", min: 3 do |cli_apl| cli_apl.cmd( :content, "Table of all provided appliances", aliases: [nil], &lambda {|node:, regexp:, system:, applications:| 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) cli_apl.cmd( :system, "Table of provided systems", aliases: [nil], &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) cli_apl.cmd( :applications, "Table of provided applications", aliases: [nil], &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) cli_apl.cmd( :list, "List provided appliances", aliases: ['ls'], &lambda {|node=nil, regexp:| connect node = node ? Proxmox::Node.find_by_name!( node) : Proxmox::Node.all.first node.aplinfo.each do |apl| puts apl.template end }). opt( :regexp, '-r=REGEXP', '--regexp', 'Filters by name', default: nil) cli_apl.cmd :download, "Download appliance", aliases: ['dl'], min: 2, &lambda {|template, node, storage=nil| storage ||= 'local' connect node = Proxmox::Node.find_by_name! node apl = node.aplinfo.find {|apl| apl.template == template } raise UsageError, "Appliance not found" unless apl task = apl.download storage wait task, text: "Download #{apl.template} on #{node.node} to #{storage}" } cli_apl.cmd( :help, '', aliases: ['-h', '--help']) {|*args| help cli_apl, *args } end end |
#cli_task ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/pve/cli/task.rb', line 2 def cli_task cli.sub :task, "Inspect tasks" do |tcli| tcli.cmd :list, "List done tasks", &lambda {|node=nil| connect nodes = Proxmox::Node.all nodes = nodes.select {|n| node == n.name } if node nodes.flat_map do |n| n.tasks.map &:upid end.sort.each {|upid| puts upid } } tcli.cmd :get, "Inspect a task", &lambda {|upid| connect Proxmox::Node.all.each do |n| n.tasks.each do |t| next unless t.upid == upid puts t.upid t.log( start: 0, limit: 1024).each {|l| puts l[:t] } return end end } end end |
#complete_lxc(f) ⇒ Object
168 169 170 171 172 |
# File 'lib/pve/cli.rb', line 168 def complete_lxc f Proxmox::LXC.all. flat_map {|x| [x.name, x.vmid.to_s] }. select {|x| f =~ x } end |
#complete_node(f) ⇒ Object
180 181 182 183 184 |
# File 'lib/pve/cli.rb', line 180 def complete_node f Proxmox::Qemu.all. map {|x| x.name }. select {|x| f =~ x } end |
#complete_qemu(f) ⇒ Object
174 175 176 177 178 |
# File 'lib/pve/cli.rb', line 174 def complete_qemu f Proxmox::Qemu.all. flat_map {|x| [x.name, x.vmid.to_s] }. select {|x| f =~ x } end |
#completion_helper(*pre, arg, &exe) ⇒ Object
186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/pve/cli.rb', line 186 def completion_helper *pre, arg, &exe if pre.empty? connect xs = yield /\A#{Regexp.quote arg}/ STDOUT.print "\a" if xs.empty? xs else STDOUT.print "\a" [] end end |
#connect ⇒ Object
31 32 33 34 35 36 37 |
# File 'lib/pve/cli.rb', line 31 def connect @conn ||= Proxmox.connect cfg[:auth][:username], cfg[:auth][:password], realm: cfg[:auth][:realm], **cfg[:connect] #RestClient.log = STDERR @conn end |
#create(klass, template, timeout: nil, fire: nil, secs: nil, start: nil, **options) ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/pve/cli.rb', line 136 def create klass, template, timeout: nil, fire: nil, secs: nil, start: nil, ** [:start] = fire && start task = klass.create template, ** return if fire status = wait task, text: "Creating" if status.successfull? host = task.host.refresh! start host, timeout: timeout, secs: secs if start elsif not interactive? exit 1 end end |
#destroy(ct, timeout: nil, fire: nil, secs: nil) ⇒ Object
149 150 151 152 153 154 |
# File 'lib/pve/cli.rb', line 149 def destroy ct, timeout: nil, fire: nil, secs: nil task = ct.destroy unless fire wait task, text: "Destroying" end end |
#enter(host, *args) ⇒ Object
53 54 55 56 |
# File 'lib/pve/cli.rb', line 53 def enter host, *args r = host.enter *args STDERR.puts "! #{$?.exitstatus}" unless r end |
#help(cl, *args) ⇒ Object
156 157 158 159 |
# File 'lib/pve/cli.rb', line 156 def help cl, *args STDERR.puts cl.help( *args) exit 1 unless interactive? end |
#interactive? ⇒ Boolean
49 50 51 |
# File 'lib/pve/cli.rb', line 49 def interactive? @interactive end |
#opts_ha(cl) ⇒ Object
2 3 4 5 6 7 8 9 |
# File 'lib/pve/cli/ha.rb', line 2 def opts_ha cl cl. opt( :group, "-gGROUP", "--group=GROUP", "Put host in GROUP", default: 'all'). opt( :comment, "-cCOMMENT", "--comment=COMMENT", "Set comment"). opt( :max_relocate, "-lCOUNT", "--max-relocate=COUNT", "How often host can be relocate before givingup?", default: 2). opt( :max_restart, "-rCOUNT", "--max-restart=COUNT", "How often host can be restarted before givingup?", default: 2). opt( :state, "-sSTATE", "--state=STATE", "Host should have STATE. If you start/stop be `pct/qm/e start/stop` STATE will be set before action.", default: "started") end |
#opts_wait(cl) ⇒ Object
161 162 163 164 165 166 |
# File 'lib/pve/cli.rb', line 161 def opts_wait cl cl. opt( :timeout, "-t", "--timeout=TIMEOUT", "Wait for max TIMEOUT seconds (default: endless)", default: nil). opt( :secs, "-s", "--seconds=SECONDS", "Check every SECONDS for state (default: 0.2)", default: 0.2). opt( :fire, "-f", "--[no-]fire", "Do not wait till running", default: false) end |
#prepare ⇒ Object
198 199 200 201 202 203 204 205 206 |
# File 'lib/pve/cli.rb', line 198 def prepare cli_node cli_ct cli_qm cli_task cli_ha cli_base cli_storage end |
#prepare_show_config(cnf) ⇒ Object
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/pve/cli/base.rb', line 39 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 |
#show_config(cnf, old = nil) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/pve/cli/base.rb', line 66 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 |
#start(host, node: nil, timeout: nil, fire: nil, secs: nil) ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/pve/cli.rb', line 110 def start host, node: nil, timeout: nil, fire: nil, secs: nil timeout ||= 30 if node task = host.migrate Proxmox::Node.find_by_name!( node) wait task, text: "Migrating" end if host.running? STDERR.puts "Already running." return end task = host.start wait task, text: "Starting" unless fire end |
#stop(host, timeout: nil, fire: nil, secs: nil) ⇒ Object
124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/pve/cli.rb', line 124 def stop host, timeout: nil, fire: nil, secs: nil timeout ||= 30 if host.stopped? STDERR.puts "Already stopped." return end task = host.stop unless fire wait task, text: "Stopping" end end |
#task_log(task, logn, limit = 1024) ⇒ Object
70 71 72 73 74 75 76 77 78 79 |
# File 'lib/pve/cli.rb', line 70 def task_log task, logn, limit = 1024 log = task.log start: logn, limit: limit log = [] if [{n: 1, t: 'no content'}] == log unless log.empty? STDERR.printf "\r\e[J" log.each {|l| puts l[:t] } logn = log.last[:n] end logn end |
#wait(task, secs: nil, text: nil) ⇒ Object
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/pve/cli.rb', line 81 def wait task, secs: nil, text: nil secs ||= 0.1 spinners, spin, logn = "▖▘▝▗", 0, 0 STDERR.puts task.upid host = task.host&.name loop do s = task.status logn = self.task_log task, logn if s.finished? loop do r = self.task_log task, logn break if 0 == logn - r logn = r end STDERR.printf "\r[%s] %s %s %s\e[J\n", host || s.id, s.successfull? ? "\e[32;1m✓\e[0m" : "\e[31;1m✗\e[0m", text && "#{text}:", s.stopped? ? :finished : s.status return s end STDERR.printf "\r[%s] \e[33;1m%s\e[0m %s...\e[J", host || s[:id], spinners[spin = (spin + 1) % 4], text || "Working" sleep secs end end |
#wait_state(host, state, timeout: nil, lock: nil) ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/pve/cli.rb', line 58 def wait_state host, state, timeout: nil, lock: nil spinners = %w[- / | \\] spin = 0 r = host.wait state, lock: lock, timeout: timeout, secs: 0.2 do |state, lock| lock = " (locked: #{lock})" if lock STDERR.printf "\r[%s] %s Still %s%s...\e[J\n", host.name, spinners[spin = (spin + 1) % 4], state, lock end STDERR.printf "\r\e[J" exit 1 unless interactive? and r end |