pve 0.1.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.adoc CHANGED
@@ -14,6 +14,18 @@ You need to provide a config-file `/etc/pve/pvecli.yml`:
14
14
  connect:
15
15
  verify_tls: no if you do not use known CA-signed X509-Certificates
16
16
 
17
+ Featurs
18
+ =======
19
+
20
+ The library provides an interface to interact with PVE-servers.
21
+ Provided abstractions for:
22
+
23
+ * Node
24
+ * LXC (create, delete, modify, use)
25
+ * Qemu (create, delete, modify, use)
26
+ * Storages (list, list content)
27
+ * Appliances (list, download to storage)
28
+
17
29
  pvecli
18
30
  ======
19
31
 
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/lib/pve/cli/base.rb CHANGED
@@ -2,6 +2,8 @@ require 'pmap'
2
2
 
3
3
  class PVE::Cli
4
4
 
5
+ using IPAddress::ToSWithNetmaskForNetworks
6
+
5
7
  def cli_base
6
8
  cli.cmd :list, "List CT/VM-IDs", aliases: ['ls'], &lambda {|target=nil|
7
9
  connect
@@ -24,11 +26,11 @@ def cli_base
24
26
  else
25
27
  lambda {|n| to.virt n }
26
28
  end
27
- nodes = Proxmox::Node.all
29
+ nodes = node ? Proxmox::Node.find_by_name( name) : Proxmox::Node.all
30
+ nodes.each &push
28
31
  nodes.
29
- select {|n| not node or n === node }.
30
- flat_map {|n| [ n.method(:lxc), n.method(:qemu) ] }.
31
- each {|m| m.call.each &push }
32
+ flat_map {|n| [ Thread.new( n, &:lxc), Thread.new( n, &:qemu) ] }.
33
+ each {|n| n.value.each &push }
32
34
  to.print order: sort.each_char.map {|c| (2*c.ord[5]-1) * (' sainhucmd'.index( c.downcase)) }
33
35
  }).
34
36
  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)").
@@ -82,15 +84,9 @@ def cli_base
82
84
  end
83
85
 
84
86
  cli.sub :config, "CT/VM Configuration", min: 2, aliases: %w[cnf] do |ccli|
85
- ccli.cmd 'help', '', aliases: [nil, '-h', '--help'], &lambda {|*args| help ccli, *args }
86
-
87
- ccli.cmd :show, "Show Config of CT/VM", &lambda {|name_or_id|
88
- connect
89
- th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
90
- show_config th.config
91
- }
87
+ ccli.cmd :help, '', aliases: [nil, '-h', '--help'], &lambda {|*args| help ccli, *args }
92
88
 
93
- ccli.cmd :set, "Set Configs for CT/VM", &lambda {|name_or_id, *args|
89
+ ccli.cmd :set, "Set Configs for CT/VM", min: 3, &lambda {|name_or_id, *args|
94
90
  if %w[-h --help].include? name_or_id
95
91
  STDERR.puts "Usage: set -h|--help # Show help"
96
92
  STDERR.puts " set ct|vm --CNF1=VAL1 --CNF2=VAL2 ... # Set config-value. Empty value clears field."
@@ -147,21 +143,37 @@ def cli_base
147
143
  th.cnfset opts
148
144
  show_config th.config, old
149
145
  }
146
+
147
+ ccli.cmd :show, "Show Config of CT/VM", aliases: %w[s], &lambda {|name_or_id|
148
+ connect
149
+ th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
150
+ show_config th.config
151
+ }
150
152
  end
151
153
 
152
- cli.cmd :enter, "Enter Console of CT/Node", &lambda {|name_or_id|
154
+ cli.cmd( :enter, "Enter Console of CT/Node", &lambda {|name_or_id|
153
155
  connect
154
156
  th = Proxmox::LXC.find( name_or_id) || Proxmox::Node.find_by_name( name_or_id)
155
157
  raise UsageError, "Container or Node not found: #{name_or_id}" unless th
156
158
  STDERR.puts "! #{$?.exitstatus}" unless th.enter
157
- }
159
+ }).
160
+ completion do |*pre, arg|
161
+ completion_helper *pre, arg do |f|
162
+ complete_lxc( f) + complete_node( f)
163
+ end
164
+ end
158
165
 
159
- cli.cmd :run, "Starts CT/VM", aliases: %w[start star], &lambda {|name_or_id|
166
+ cli.cmd( :run, "Starts CT/VM", aliases: %w[start star], &lambda {|name_or_id|
160
167
  connect
161
168
  th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
162
169
  raise UsageError, "Container or Node not found: #{name_or_id}" unless th
163
170
  start th
164
- }
171
+ }).
172
+ completion do |*pre, arg|
173
+ completion_helper *pre, arg do |f|
174
+ complete_lxc( f) + complete_qemu( f)
175
+ end
176
+ end
165
177
 
166
178
  #cli.cmd :reboot, "Reboot CT/VM (not implemented, yet)", min: 6, &lambda {|name_or_id|
167
179
  # connect
@@ -170,16 +182,28 @@ def cli_base
170
182
  # reboot th
171
183
  #}
172
184
 
173
- cli.cmd :stop, "Stops CT/VM", min: 4, &lambda {|name_or_id|
185
+ cli.cmd( :stop, "Stops CT/VM", min: 4, &lambda {|name_or_id|
174
186
  connect
175
187
  th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
176
188
  raise UsageError, "Container or Node not found: #{name_or_id}" unless th
177
189
  stop th
178
- }
190
+ }).
191
+ completion do |*pre, arg|
192
+ completion_helper *pre, arg do |f|
193
+ complete_lxc( f) + complete_qemu( f)
194
+ end
195
+ end
179
196
 
180
- cli.cmd 'help', '', aliases: ['-h', '--help'], &lambda {|*args| help cli, *args }
197
+ cli.cmd( :help, '', aliases: ['-h', '--help'], &lambda {|*args, full:|
198
+ if full
199
+ cli.help_full *args, output: STDERR
200
+ else
201
+ cli.help *args, output: STDERR
202
+ end
203
+ }).
204
+ opt( :full, '-f', '--[no-]full', 'Includes all commands of all subcommands.', default: false)
181
205
 
182
- cli.cmd 'cli', 'Opens interactive console', min: 3, aliases: [nil], &lambda {
206
+ cli.cmd :cli, 'Opens interactive console', min: 3, aliases: [nil], &lambda {
183
207
  @interactive = true
184
208
  cli.interactive( File.basename($0,'.rb')).run
185
209
  }
data/lib/pve/cli/ct.rb CHANGED
@@ -3,23 +3,31 @@ def cli_ct
3
3
  cli.sub :ct, "Containers", aliases: %w[lx lxc] do |ct_cli|
4
4
  ct_cli.cmd :list, "List CT-IDs", aliases: ['ls'], &lambda {|node=nil|
5
5
  connect
6
- nodes = Proxmox::Node.all
7
- nodes = nodes.select {|n| node == n.name } if node
6
+ nodes = node ? Proxmox::Node.find_by_name( name) : Proxmox::Node.all
8
7
  nodes.flat_map do |n|
9
8
  n.lxc.map {|c| c.vmid.to_i }
10
9
  end.sort.each {|c| puts c }
11
10
  }
12
11
 
13
- ct_cli.cmd :status, "List CTs with status", aliases: [nil], &lambda {|node=nil|
12
+ ct_cli.cmd( :status, "Lists CTs with status", aliases: [nil], &lambda {|target=nil, sort: 'n', node: nil|
14
13
  connect
15
- to = TablizedOutput.new %w[Status HA ID Name Host Uptime CPU Mem/MiB Disk/MiB]
16
- nodes = Proxmox::Node.all
17
- nodes = nodes.select {|n| node == n.name } if node
18
- nodes.each do |n|
19
- n.lxc.each &to.method( :virt)
20
- end
21
- to.print order: [3]
22
- }
14
+ node &&= /\A#{node}\z/
15
+ to = TablizedOutput.new %w[Status HA ID Name Host Uptime CPU/% Mem/MiB Mem/% Disk/MiB Disk/%]
16
+ push =
17
+ if target
18
+ target = /\A#{target}\z/
19
+ lambda {|n| to.virt n if n === target }
20
+ else
21
+ lambda {|n| to.virt n }
22
+ end
23
+ nodes = node ? Proxmox::Node.find_by_name( name) : Proxmox::Node.all
24
+ nodes.
25
+ map {|n| Thread.new( n, &:lxc) }.
26
+ each {|n| n.value.each &push }
27
+ to.print order: sort.each_char.map {|c| (2*c.ord[5]-1) * (' sainhucmd'.index( c.downcase)) }
28
+ }).
29
+ 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)").
30
+ opt( :node, '-n', '--node=NODE', "List only hosted by this NODE")
23
31
 
24
32
  ct_cli.cmd :enter, "Enter Console of CT", &lambda {|name_or_id|
25
33
  connect
@@ -57,13 +65,19 @@ def cli_ct
57
65
  if %w[-h --help].include? template
58
66
  STDERR.puts "Usage: ct create TEMPLATE -h # Shows template-related options"
59
67
  STDERR.puts " ct create TEMPLATE [OPTIONS] # Creates a container"
68
+ STDERR.puts " ct create -l # Listing available templates"
60
69
  exit 1
70
+ elsif %w[-l --list].include? template
71
+ STDERR.puts PVE::CTTemplate.constants.reject {|c|:Base==c}.map {|c|c.to_s.titlecase.dasherize.downcase}
72
+ exit 0
61
73
  end
62
74
  ctopts = {}
63
75
  OptionParser.new do |opts|
76
+ ctt = PVE::CTTemplate.const_get template.classify
64
77
  opts.banner = <<EOU
65
78
  Usage: ct create #{template} [options]
66
79
 
80
+ #{ctt.help}
67
81
  Options: (*=Required)
68
82
  EOU
69
83
  opts.on '-h', '--help', " Help!" do
@@ -75,7 +89,6 @@ EOU
75
89
  opts.on( '-f', '--[no-]-fire', " Do not wait till running") {|v| ctopts[:start] = v }
76
90
  opts.on( '-t', '--timeout=TIMEOUT', " Wait for max TIMEOUT seconds (default: endless)") {|v| ctopts[:timeout] = v }
77
91
  opts.on( '-s', '--seconds=SECONDS', " Check every SECONDS for state (default: 0.2)") {|v| ctopts[:seconds] = v }
78
- ctt = PVE::CTTemplate.const_get template.classify
79
92
  ctt.requirements.each do |name, (type, req, desc, *args)|
80
93
  req = req ? "*" : " "
81
94
  case type
@@ -94,10 +107,17 @@ EOU
94
107
  create Proxmox::LXC, template, **ctopts
95
108
  })
96
109
 
97
- ct_cli.cmd( :config, '', &lambda {|name_or_id|
110
+ ct_cli.cmd( :config, 'Shows current config', aliases: %w[cnf], &lambda {|name_or_id|
111
+ connect
112
+ ct = Proxmox::LXC.find! name_or_id
113
+ STDOUT.puts JSON.dump( ct.config)
114
+ })
115
+
116
+ ct_cli.cmd( :resize, 'Resize a disk', &lambda {|name_or_id, disk, size|
98
117
  connect
99
118
  ct = Proxmox::LXC.find! name_or_id
100
- STDOUT.puts ct.config.to_json
119
+ task = ct.resize disk, size
120
+ wait task, text: "Resizing #{ct.sid} #{disk} to #{size}"
101
121
  })
102
122
 
103
123
  ct_cli.cmd( :destroy, '', min: 7, &lambda {|name_or_id, fire:, secs:, timeout:, i_really_want_to_destroy:|
data/lib/pve/cli/ha.rb CHANGED
@@ -15,7 +15,7 @@ def cli_ha
15
15
  th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
16
16
  raise UsageError, "Container or VirtualMachine not found: #{name_or_id}" unless th
17
17
  ha = th.ha
18
- raise UsageError, "#{th.sid} is already High-Available" unless ha.active?
18
+ raise UsageError, "#{th.sid} is already High-Available" if ha.active?
19
19
  ha.create group: group, comment: comment, max_relocate: max_relocate, max_restart: max_restart
20
20
  }).tap {|cl| opts_ha cl }
21
21
 
@@ -24,7 +24,7 @@ def cli_ha
24
24
  th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
25
25
  raise UsageError, "Container or VirtualMachine not found: #{name_or_id}" unless th
26
26
  ha = th.ha
27
- raise UsageError, "#{th.sid} is not High-Available" if ha.active?
27
+ raise UsageError, "#{th.sid} is not High-Available" unless ha.active?
28
28
  ha.delete
29
29
  }
30
30
 
@@ -52,7 +52,7 @@ def cli_ha
52
52
  ha = ha.create unless ha.active?
53
53
  ha.disabled! if force and ha.error?
54
54
  ha.started!
55
- }).opt( :force, "-f", "--force", "If CT/VM is in error-state, first disable HA, than try to start.")
55
+ }).opt( :force, "-f", "--force", "If CT/VM is in error-state, first reset HA, than try to start.")
56
56
 
57
57
  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|
58
58
  connect
data/lib/pve/cli/qm.rb CHANGED
@@ -10,22 +10,38 @@ def cli_qm
10
10
  end.sort.each {|c| puts c }
11
11
  }
12
12
 
13
- qm.cmd :status, "List VMs with status", aliases: [nil], &lambda {|node=nil|
13
+ qm.cmd( :status, "Lists CTs with status", aliases: [nil], &lambda {|target=nil, sort: 'n', node: nil|
14
14
  connect
15
- to = TablizedOutput.new %w[Status HA ID Name Host Uptime CPU Mem/MiB Disk/MiB]
16
- nodes = Proxmox::Node.all
17
- nodes = nodes.select {|n| node == n.name } if node
18
- nodes.each do |n|
19
- n.qemu.each &to.method( :virt)
20
- end
21
- to.print order: [3]
22
- }
15
+ node &&= /\A#{node}\z/
16
+ to = TablizedOutput.new %w[Status HA ID Name Host Uptime CPU/% Mem/MiB Mem/% Disk/MiB Disk/%]
17
+ push =
18
+ if target
19
+ target = /\A#{target}\z/
20
+ lambda {|n| to.virt n if n === target }
21
+ else
22
+ lambda {|n| to.virt n }
23
+ end
24
+ nodes = node ? Proxmox::Node.find_by_name( name) : Proxmox::Node.all
25
+ nodes.
26
+ map {|n| Thread.new( n, &:qemu) }.
27
+ each {|n| n.value.each &push }
28
+ to.print order: sort.each_char.map {|c| (2*c.ord[5]-1) * (' sainhucmd'.index( c.downcase)) }
29
+ }).
30
+ 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)").
31
+ opt( :node, '-n', '--node=NODE', "List only hosted by this NODE")
23
32
 
24
33
  qm.cmd :exec, "Executes Command in VM via qemu-guest-agent", min: 4, &lambda {|name_or_id, *command|
25
34
  connect
26
35
  STDERR.puts "! #{$?.exitstatus}" unless Proxmox::Qemu.find!( name_or_id).exec *command
27
36
  }
28
37
 
38
+ qm.cmd( :resize, 'Resize a disk', &lambda {|name_or_id, disk, size|
39
+ connect
40
+ qm = Proxmox::Qemu.find! name_or_id
41
+ task = qm.resize disk, size
42
+ wait task, text: "Resizing #{qm.sid} #{disk} to #{size}"
43
+ })
44
+
29
45
  qm.cmd 'help', '', aliases: ['-h', '--help'], &lambda {|*args| help qm, *args }
30
46
  }
31
47
  end
@@ -0,0 +1,97 @@
1
+ class PVE::Cli
2
+ def cli_storage
3
+ cli.sub :storage, "Storages", min: 3 do |cli_sm|
4
+ cli_sm.cmd :list, "List Storages", aliases: ['ls'], &lambda {|node=nil|
5
+ connect
6
+ nodes = node ? [Proxmox::Node.find_by_name!( node)] : Proxmox::Node.all
7
+ nodes.flat_map do |n|
8
+ n.lxc.map {|c| c.vmid.to_i }
9
+ end.sort.each {|c| puts c }
10
+ }
11
+
12
+ cli_sm.cmd :status, "List Storages with status", aliases: [nil], &lambda {|node=nil|
13
+ connect
14
+ to = TablizedOutput.new %w[A E S Storage Host Type]
15
+ nodes = node ? [Proxmox::Node.find_by_name!( node)] : Proxmox::Node.all
16
+ nodes.each do |n|
17
+ n.storage.each do |v|
18
+ to.push [
19
+ case v.active
20
+ when 1 then ColoredString.new 'Y', "32"
21
+ when 0 then ColoredString.new 'n', "31"
22
+ else v.active.to_s
23
+ end,
24
+ case v.enabled
25
+ when 1 then ColoredString.new 'Y', "32"
26
+ when 0 then ColoredString.new 'n', "31"
27
+ else v.enabled.to_s
28
+ end,
29
+ 1 == v.shared ? 's' : 'l', v.storage, v.node.node, v.type
30
+ ]
31
+ end
32
+ end
33
+ to.print order: [4,5]
34
+ }
35
+
36
+ cli_sm.sub :content, "Content of Storage", aliases: ['cnt'] do |cli_cnt|
37
+ cli_cnt.cmd :list, "List Content", aliases: ['ls'], &lambda {|node=nil, storage|
38
+ connect
39
+ node = node ? Proxmox::Node.find_by_name!( node) : Proxmox::Node.all.first
40
+ storage = node.storage.select {|sm| storage == sm.storage }.first
41
+ storage.content.each {|c| puts c.to_s }
42
+ }
43
+ cli_cnt.cmd( :help, '', aliases: ['-h', '--help']) {|*args| help cli_cnt, *args }
44
+ end
45
+
46
+ cli_sm.cmd( :help, '', aliases: ['-h', '--help']) {|*args| help cli_sm, *args }
47
+ #cli_sm.provide_help
48
+ end
49
+
50
+ cli.sub :apl, "Appliances - Downloadable container images", min: 3 do |cli_apl|
51
+
52
+ cli_apl.cmd( :content, "Table of all provided appliances", aliases: [nil], &lambda {|node:, regexp:, system:, applications:|
53
+ connect
54
+ appliances node, regexp, system, applications
55
+ }).
56
+ opt( :node, '-n=NODE', '--node', 'Ask this node for appliances (any node should list the same)', default: nil).
57
+ opt( :regexp, '-r=REGEXP', '--regexp', 'Filter by template', default: nil).
58
+ opt( :system, '-s', '--system', 'Only system templates', default: nil).
59
+ opt( :applications, '-a', '--applications', 'Only applications (non system) templates', default: nil)
60
+
61
+ cli_apl.cmd( :system, "Table of provided systems", aliases: [nil], &lambda {|node:, regexp:|
62
+ connect
63
+ appliances node, regexp, true, nil
64
+ }).
65
+ opt( :node, '-n=NODE', '--node', 'Ask this node for appliances (any node should list the same)', default: nil).
66
+ opt( :regexp, '-r=REGEXP', '--regexp', 'Filter by template', default: nil)
67
+
68
+ cli_apl.cmd( :applications, "Table of provided applications", aliases: [nil], &lambda {|node:, regexp:|
69
+ connect
70
+ appliances node, regexp, nil, true
71
+ }).
72
+ opt( :node, '-n=NODE', '--node', 'Ask this node for appliances (any node should list the same)', default: nil).
73
+ opt( :regexp, '-r=REGEXP', '--regexp', 'Filter by template', default: nil)
74
+
75
+ cli_apl.cmd( :list, "List provided appliances", aliases: ['ls'], &lambda {|node=nil, regexp:|
76
+ connect
77
+ node = node ? Proxmox::Node.find_by_name!( node) : Proxmox::Node.all.first
78
+ node.aplinfo.each do |apl|
79
+ puts apl.template
80
+ end
81
+ }).
82
+ opt( :regexp, '-r=REGEXP', '--regexp', 'Filters by name', default: nil)
83
+
84
+ cli_apl.cmd :download, "Download appliance", aliases: ['dl'], min: 2, &lambda {|template, node, storage=nil|
85
+ storage ||= 'local'
86
+ connect
87
+ node = Proxmox::Node.find_by_name! node
88
+ apl = node.aplinfo.find {|apl| apl.template == template }
89
+ raise UsageError, "Appliance not found" unless apl
90
+ task = apl.download storage
91
+ wait task, text: "Download #{apl.template} on #{node.node} to #{storage}"
92
+ }
93
+
94
+ cli_apl.cmd( :help, '', aliases: ['-h', '--help']) {|*args| help cli_apl, *args }
95
+ end
96
+ end
97
+ end
data/lib/pve/cli/task.rb CHANGED
@@ -16,7 +16,7 @@ def cli_task
16
16
  n.tasks.each do |t|
17
17
  next unless t.upid == upid
18
18
  puts t.upid
19
- t.log.each {|l| puts l[:l] }
19
+ t.log( start: 0, limit: 1024).each {|l| puts l[:t] }
20
20
  return
21
21
  end
22
22
  end
data/lib/pve/cli.rb CHANGED
@@ -9,10 +9,22 @@ require_relative 'cli/ha'
9
9
  require_relative 'cli/task'
10
10
  require_relative 'cli/qm'
11
11
  require_relative 'cli/node'
12
+ require_relative 'cli/storage'
12
13
 
13
14
  class UsageError <RuntimeError
14
15
  end
15
16
 
17
+ class DenCli::Sub
18
+ def provide_help name: nil, aliases: nil, min: nil
19
+ base = self
20
+ name = :help if name.nil?
21
+ aliases = %w[-h --help] if aliases.nil?
22
+ min = 1 if min.nil?
23
+ #p name: name, aliases: aliases, min: min
24
+ cmd( name, '', aliases: aliases, min: min) {|*args| help *args }
25
+ end
26
+ end
27
+
16
28
  class PVE::Cli
17
29
  attr_reader :cfg, :cli
18
30
 
@@ -55,30 +67,42 @@ class PVE::Cli
55
67
  exit 1 unless interactive? and r
56
68
  end
57
69
 
70
+ def task_log task, logn, limit = 1024
71
+ log = task.log start: logn, limit: limit
72
+ log = [] if [{n: 1, t: 'no content'}] == log
73
+ unless log.empty?
74
+ STDERR.printf "\r\e[J"
75
+ log.each {|l| puts l[:t] }
76
+ logn = log.last[:n]
77
+ end
78
+ logn
79
+ end
80
+
58
81
  def wait task, secs: nil, text: nil
59
82
  secs ||= 0.1
60
83
  spinners, spin, logn = "▖▘▝▗", 0, 0
61
84
  STDERR.puts task.upid
85
+ host = task.host&.name
62
86
  loop do
63
87
  s = task.status
64
- if s[:exitstatus]
65
- STDERR.printf "\r[%s] %s %s\e[J\n",
66
- task.host ? task.host.name : s[:id],
67
- case s[:exitstatus]
68
- when "OK" then "\e[32;1m✓\e[0m"
69
- else "\e[31;1m✗\e[0m"
70
- end,
71
- s[:status]
72
- return task
88
+ logn = self.task_log task, logn
89
+ if s.finished?
90
+ loop do
91
+ r = self.task_log task, logn
92
+ break if 0 == logn - r
93
+ logn = r
94
+ end
95
+ STDERR.printf "\r[%s] %s %s %s\e[J\n",
96
+ host || s.id,
97
+ s.successfull? ? "\e[32;1m✓\e[0m" : "\e[31;1m✗\e[0m",
98
+ text && "#{text}:",
99
+ s.stopped? ? :finished : s.status
100
+ return s
73
101
  end
74
- log = task.log start: logn
75
- log = [] if [{n: 1, t: 'no content'}] == log
76
- unless log.empty?
77
- STDERR.printf "\r\e[J"
78
- log.each {|l| puts l[:t] }
79
- logn = log.last[:n]
80
- end
81
- STDERR.printf "\r[%s] \e[33;1m%s\e[0m %s...\e[J", task.host ? task.host.name : s[:id], spinners[spin = (spin + 1) % 4], text || "Working"
102
+ STDERR.printf "\r[%s] \e[33;1m%s\e[0m %s...\e[J",
103
+ host || s[:id],
104
+ spinners[spin = (spin + 1) % 4],
105
+ text || "Working"
82
106
  sleep secs
83
107
  end
84
108
  end
@@ -94,9 +118,7 @@ class PVE::Cli
94
118
  return
95
119
  end
96
120
  task = host.start
97
- unless fire
98
- wait task, text: "Starting"
99
- end
121
+ wait task, text: "Starting" unless fire
100
122
  end
101
123
 
102
124
  def stop host, timeout: nil, fire: nil, secs: nil
@@ -112,12 +134,16 @@ class PVE::Cli
112
134
  end
113
135
 
114
136
  def create klass, template, timeout: nil, fire: nil, secs: nil, start: nil, **options
115
- options[:start] = true if fire and start
137
+ options[:start] = fire && start
116
138
  task = klass.create template, **options
117
139
  return if fire
118
- wait task, text: "Creating"
119
- host = task.host.refresh!
120
- start host, timeout: timeout, secs: secs
140
+ status = wait task, text: "Creating"
141
+ if status.successfull?
142
+ host = task.host.refresh!
143
+ start host, timeout: timeout, secs: secs if start
144
+ elsif not interactive?
145
+ exit 1
146
+ end
121
147
  end
122
148
 
123
149
  def destroy ct, timeout: nil, fire: nil, secs: nil
@@ -139,6 +165,36 @@ class PVE::Cli
139
165
  opt( :fire, "-f", "--[no-]fire", "Do not wait till running", default: false)
140
166
  end
141
167
 
168
+ def complete_lxc f
169
+ Proxmox::LXC.all.
170
+ flat_map {|x| [x.name, x.vmid.to_s] }.
171
+ select {|x| f =~ x }
172
+ end
173
+
174
+ def complete_qemu f
175
+ Proxmox::Qemu.all.
176
+ flat_map {|x| [x.name, x.vmid.to_s] }.
177
+ select {|x| f =~ x }
178
+ end
179
+
180
+ def complete_node f
181
+ Proxmox::Qemu.all.
182
+ map {|x| x.name }.
183
+ select {|x| f =~ x }
184
+ end
185
+
186
+ def completion_helper *pre, arg, &exe
187
+ if pre.empty?
188
+ connect
189
+ xs = yield /\A#{Regexp.quote arg}/
190
+ STDOUT.print "\a" if xs.empty?
191
+ xs
192
+ else
193
+ STDOUT.print "\a"
194
+ []
195
+ end
196
+ end
197
+
142
198
  def prepare
143
199
  cli_node
144
200
  cli_ct
@@ -146,14 +202,34 @@ class PVE::Cli
146
202
  cli_task
147
203
  cli_ha
148
204
  cli_base
205
+ cli_storage
149
206
  end
150
207
 
151
208
  def call *argv
152
209
  cli.call *argv
153
210
  rescue RestClient::ExceptionWithResponse
154
- STDERR.puts "#$! - #{$!.response} (#{$!.class})", $!.backtrace.map {|b|" #{b}"}
211
+ STDERR.puts "#$! - #{$!.response} (#{$!.class})" #, $!.backtrace.map {|b|" #{b}"}
155
212
  rescue UsageError, DenCli::UsageError
156
213
  STDERR.puts $!
157
214
  exit 1
158
215
  end
216
+
217
+ def appliances node, regexp, system, applications
218
+ system = applications = true if system.nil? and applications.nil?
219
+ node = node ? Proxmox::Node.find_by_name!( node) : Proxmox::Node.all.first
220
+ to = TablizedOutput.new %w<Section Package Version OS Template Description>, format: %w[> > > > > <]
221
+ node.aplinfo.
222
+ select {|a| 'system' == a.section ? system : applications}.
223
+ each do |apl|
224
+ to.push [
225
+ apl.section,
226
+ apl.package,
227
+ apl.version,
228
+ apl.os,
229
+ apl.template,
230
+ apl.description,
231
+ ]
232
+ end
233
+ to.print order: [1,2]
234
+ end
159
235
  end