pve 0.1.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/LICENSE.txt +661 -0
- data/README.adoc +12 -0
- data/Rakefile +2 -0
- data/lib/pve/cli/base.rb +44 -20
- data/lib/pve/cli/ct.rb +34 -14
- data/lib/pve/cli/ha.rb +3 -3
- data/lib/pve/cli/qm.rb +25 -9
- data/lib/pve/cli/storage.rb +97 -0
- data/lib/pve/cli/task.rb +1 -1
- data/lib/pve/cli.rb +101 -25
- data/lib/pve/helper.rb +63 -24
- data/lib/pve/proxmox.rb +135 -33
- data/lib/pve/templates.rb +57 -15
- data/lib/pve/version.rb +1 -1
- data/pve.gemspec +1 -1
- metadata +7 -6
- data/Gemfile.lock +0 -70
- data/lib/pve/qm.rb +0 -32
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
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
|
-
|
30
|
-
|
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
|
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
|
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
|
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, "
|
12
|
+
ct_cli.cmd( :status, "Lists CTs with status", aliases: [nil], &lambda {|target=nil, sort: 'n', node: nil|
|
14
13
|
connect
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
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"
|
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"
|
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
|
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, "
|
13
|
+
qm.cmd( :status, "Lists CTs with status", aliases: [nil], &lambda {|target=nil, sort: 'n', node: nil|
|
14
14
|
connect
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
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
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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] =
|
137
|
+
options[:start] = fire && start
|
116
138
|
task = klass.create template, **options
|
117
139
|
return if fire
|
118
|
-
wait task, text: "Creating"
|
119
|
-
|
120
|
-
|
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})"
|
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
|