pve 0.1.3 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +6 -6
- data/README.adoc +12 -0
- data/lib/pve/cli/base.rb +29 -15
- data/lib/pve/cli/ct.rb +3 -4
- data/lib/pve/cli/ha.rb +3 -3
- data/lib/pve/cli/qm.rb +7 -0
- data/lib/pve/cli/storage.rb +97 -0
- data/lib/pve/cli/task.rb +1 -1
- data/lib/pve/cli.rb +90 -21
- data/lib/pve/helper.rb +7 -4
- data/lib/pve/proxmox.rb +123 -23
- data/lib/pve/templates.rb +2 -1
- data/lib/pve/version.rb +1 -1
- data/pve.gemspec +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a44ba988b57b1f008d06b521e2f10821a36db9532bb4947ee60627a324cd4c23
|
4
|
+
data.tar.gz: 1c048a050ee35b3e479c23d527eca2d44d7d56246b25ee38f9843ececb2269e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e6c00aa82db977e9616f7fd29cb72c8b442881f5253446610551f964c7a11f9d4a49cc7b5e0830e54e88a563ec03a0f6d7c231d05aa660bea10126c2e026cf3
|
7
|
+
data.tar.gz: 95b41594c329cfbd03633a77bc1ed32100a3b35ef9f9082578b7e8550095a768daacf3ae41417d630136c6c07c1c21c2143c5f43a4fa539b50c4ed53408c70f0
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pve (0.1.
|
4
|
+
pve (0.1.3)
|
5
5
|
activesupport (>= 2)
|
6
6
|
dencli (~> 0.3.1)
|
7
7
|
ipaddress (~> 0.8.3)
|
@@ -11,26 +11,26 @@ PATH
|
|
11
11
|
GEM
|
12
12
|
remote: https://rubygems.org/
|
13
13
|
specs:
|
14
|
-
activesupport (6.1.
|
14
|
+
activesupport (6.1.4.1)
|
15
15
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
16
16
|
i18n (>= 1.6, < 2)
|
17
17
|
minitest (>= 5.1)
|
18
18
|
tzinfo (~> 2.0)
|
19
19
|
zeitwerk (~> 2.3)
|
20
|
-
concurrent-ruby (1.1.
|
20
|
+
concurrent-ruby (1.1.9)
|
21
21
|
dencli (0.3.1)
|
22
22
|
diff-lcs (1.4.4)
|
23
23
|
domain_name (0.5.20190701)
|
24
24
|
unf (>= 0.0.5, < 1.0.0)
|
25
25
|
http-accept (1.7.0)
|
26
|
-
http-cookie (1.0.
|
26
|
+
http-cookie (1.0.4)
|
27
27
|
domain_name (~> 0.5)
|
28
28
|
i18n (1.8.10)
|
29
29
|
concurrent-ruby (~> 1.0)
|
30
30
|
ipaddress (0.8.3)
|
31
31
|
mime-types (3.3.1)
|
32
32
|
mime-types-data (~> 3.2015)
|
33
|
-
mime-types-data (3.2021.
|
33
|
+
mime-types-data (3.2021.0901)
|
34
34
|
minitest (5.14.4)
|
35
35
|
netrc (0.11.0)
|
36
36
|
pmap (1.1.1)
|
@@ -56,7 +56,7 @@ GEM
|
|
56
56
|
concurrent-ruby (~> 1.0)
|
57
57
|
unf (0.1.4)
|
58
58
|
unf_ext
|
59
|
-
unf_ext (0.0.
|
59
|
+
unf_ext (0.0.8)
|
60
60
|
zeitwerk (2.4.2)
|
61
61
|
|
62
62
|
PLATFORMS
|
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/lib/pve/cli/base.rb
CHANGED
@@ -26,9 +26,8 @@ def cli_base
|
|
26
26
|
else
|
27
27
|
lambda {|n| to.virt n }
|
28
28
|
end
|
29
|
-
nodes = Proxmox::Node.all
|
29
|
+
nodes = node ? Proxmox::Node.find_by_name( name) : Proxmox::Node.all
|
30
30
|
nodes.
|
31
|
-
select {|n| not node or n === node }.
|
32
31
|
flat_map {|n| [ n.method(:lxc), n.method(:qemu) ] }.
|
33
32
|
each {|m| m.call.each &push }
|
34
33
|
to.print order: sort.each_char.map {|c| (2*c.ord[5]-1) * (' sainhucmd'.index( c.downcase)) }
|
@@ -86,13 +85,7 @@ def cli_base
|
|
86
85
|
cli.sub :config, "CT/VM Configuration", min: 2, aliases: %w[cnf] do |ccli|
|
87
86
|
ccli.cmd 'help', '', aliases: [nil, '-h', '--help'], &lambda {|*args| help ccli, *args }
|
88
87
|
|
89
|
-
ccli.cmd :
|
90
|
-
connect
|
91
|
-
th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
|
92
|
-
show_config th.config
|
93
|
-
}
|
94
|
-
|
95
|
-
ccli.cmd :set, "Set Configs for CT/VM", &lambda {|name_or_id, *args|
|
88
|
+
ccli.cmd :set, "Set Configs for CT/VM", min: 3, &lambda {|name_or_id, *args|
|
96
89
|
if %w[-h --help].include? name_or_id
|
97
90
|
STDERR.puts "Usage: set -h|--help # Show help"
|
98
91
|
STDERR.puts " set ct|vm --CNF1=VAL1 --CNF2=VAL2 ... # Set config-value. Empty value clears field."
|
@@ -149,21 +142,37 @@ def cli_base
|
|
149
142
|
th.cnfset opts
|
150
143
|
show_config th.config, old
|
151
144
|
}
|
145
|
+
|
146
|
+
ccli.cmd :show, "Show Config of CT/VM", aliases: %w[s], &lambda {|name_or_id|
|
147
|
+
connect
|
148
|
+
th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
|
149
|
+
show_config th.config
|
150
|
+
}
|
152
151
|
end
|
153
152
|
|
154
|
-
cli.cmd :enter, "Enter Console of CT/Node", &lambda {|name_or_id|
|
153
|
+
cli.cmd( :enter, "Enter Console of CT/Node", &lambda {|name_or_id|
|
155
154
|
connect
|
156
155
|
th = Proxmox::LXC.find( name_or_id) || Proxmox::Node.find_by_name( name_or_id)
|
157
156
|
raise UsageError, "Container or Node not found: #{name_or_id}" unless th
|
158
157
|
STDERR.puts "! #{$?.exitstatus}" unless th.enter
|
159
|
-
}
|
158
|
+
}).
|
159
|
+
completion do |*pre, arg|
|
160
|
+
completion_helper *pre, arg do |f|
|
161
|
+
complete_lxc( f) + complete_node( f)
|
162
|
+
end
|
163
|
+
end
|
160
164
|
|
161
|
-
cli.cmd :run, "Starts CT/VM", aliases: %w[start star], &lambda {|name_or_id|
|
165
|
+
cli.cmd( :run, "Starts CT/VM", aliases: %w[start star], &lambda {|name_or_id|
|
162
166
|
connect
|
163
167
|
th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
|
164
168
|
raise UsageError, "Container or Node not found: #{name_or_id}" unless th
|
165
169
|
start th
|
166
|
-
}
|
170
|
+
}).
|
171
|
+
completion do |*pre, arg|
|
172
|
+
completion_helper *pre, arg do |f|
|
173
|
+
complete_lxc( f) + complete_qemu( f)
|
174
|
+
end
|
175
|
+
end
|
167
176
|
|
168
177
|
#cli.cmd :reboot, "Reboot CT/VM (not implemented, yet)", min: 6, &lambda {|name_or_id|
|
169
178
|
# connect
|
@@ -172,12 +181,17 @@ def cli_base
|
|
172
181
|
# reboot th
|
173
182
|
#}
|
174
183
|
|
175
|
-
cli.cmd :stop, "Stops CT/VM", min: 4, &lambda {|name_or_id|
|
184
|
+
cli.cmd( :stop, "Stops CT/VM", min: 4, &lambda {|name_or_id|
|
176
185
|
connect
|
177
186
|
th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
|
178
187
|
raise UsageError, "Container or Node not found: #{name_or_id}" unless th
|
179
188
|
stop th
|
180
|
-
}
|
189
|
+
}).
|
190
|
+
completion do |*pre, arg|
|
191
|
+
completion_helper *pre, arg do |f|
|
192
|
+
complete_lxc( f) + complete_qemu( f)
|
193
|
+
end
|
194
|
+
end
|
181
195
|
|
182
196
|
cli.cmd 'help', '', aliases: ['-h', '--help'], &lambda {|*args| help cli, *args }
|
183
197
|
|
data/lib/pve/cli/ct.rb
CHANGED
@@ -3,8 +3,7 @@ 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 }
|
@@ -99,10 +98,10 @@ EOU
|
|
99
98
|
create Proxmox::LXC, template, **ctopts
|
100
99
|
})
|
101
100
|
|
102
|
-
ct_cli.cmd( :config, 'Shows current config', &lambda {|name_or_id|
|
101
|
+
ct_cli.cmd( :config, 'Shows current config', aliases: %w[cnf], &lambda {|name_or_id|
|
103
102
|
connect
|
104
103
|
ct = Proxmox::LXC.find! name_or_id
|
105
|
-
STDOUT.puts ct.config
|
104
|
+
STDOUT.puts JSON.dump( ct.config)
|
106
105
|
})
|
107
106
|
|
108
107
|
ct_cli.cmd( :resize, 'Resize a disk', &lambda {|name_or_id, disk, size|
|
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
@@ -26,6 +26,13 @@ def cli_qm
|
|
26
26
|
STDERR.puts "! #{$?.exitstatus}" unless Proxmox::Qemu.find!( name_or_id).exec *command
|
27
27
|
}
|
28
28
|
|
29
|
+
qm.cmd( :resize, 'Resize a disk', &lambda {|name_or_id, disk, size|
|
30
|
+
connect
|
31
|
+
qm = Proxmox::Qemu.find! name_or_id
|
32
|
+
task = qm.resize disk, size
|
33
|
+
wait task, text: "Resizing #{qm.sid} #{disk} to #{size}"
|
34
|
+
})
|
35
|
+
|
29
36
|
qm.cmd 'help', '', aliases: ['-h', '--help'], &lambda {|*args| help qm, *args }
|
30
37
|
}
|
31
38
|
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
@@ -13,6 +13,17 @@ require_relative 'cli/node'
|
|
13
13
|
class UsageError <RuntimeError
|
14
14
|
end
|
15
15
|
|
16
|
+
class DenCli::Sub
|
17
|
+
def provide_help name: nil, aliases: nil, min: nil
|
18
|
+
base = self
|
19
|
+
name = :help if name.nil?
|
20
|
+
aliases = %w[-h --help] if aliases.nil?
|
21
|
+
min = 1 if min.nil?
|
22
|
+
#p name: name, aliases: aliases, min: min
|
23
|
+
cmd( name, '', aliases: aliases, min: min) {|*args| help *args }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
16
27
|
class PVE::Cli
|
17
28
|
attr_reader :cfg, :cli
|
18
29
|
|
@@ -55,6 +66,17 @@ class PVE::Cli
|
|
55
66
|
exit 1 unless interactive? and r
|
56
67
|
end
|
57
68
|
|
69
|
+
def task_log task, logn, limit = 1024
|
70
|
+
log = task.log start: logn, limit: limit
|
71
|
+
log = [] if [{n: 1, t: 'no content'}] == log
|
72
|
+
unless log.empty?
|
73
|
+
STDERR.printf "\r\e[J"
|
74
|
+
log.each {|l| puts l[:t] }
|
75
|
+
logn = log.last[:n]
|
76
|
+
end
|
77
|
+
logn
|
78
|
+
end
|
79
|
+
|
58
80
|
def wait task, secs: nil, text: nil
|
59
81
|
secs ||= 0.1
|
60
82
|
spinners, spin, logn = "▖▘▝▗", 0, 0
|
@@ -62,23 +84,19 @@ class PVE::Cli
|
|
62
84
|
host = task.host&.name
|
63
85
|
loop do
|
64
86
|
s = task.status
|
65
|
-
|
87
|
+
logn = self.task_log task, logn
|
88
|
+
if s.finished?
|
89
|
+
loop do
|
90
|
+
r = self.task_log task, logn
|
91
|
+
break if 0 == logn - r
|
92
|
+
logn = r
|
93
|
+
end
|
66
94
|
STDERR.printf "\r[%s] %s %s %s\e[J\n",
|
67
|
-
host || s
|
68
|
-
|
69
|
-
when "OK" then "\e[32;1m✓\e[0m"
|
70
|
-
else "\e[31;1m✗\e[0m"
|
71
|
-
end,
|
95
|
+
host || s.id,
|
96
|
+
s.successfull? ? "\e[32;1m✓\e[0m" : "\e[31;1m✗\e[0m",
|
72
97
|
text && "#{text}:",
|
73
|
-
s
|
74
|
-
return
|
75
|
-
end
|
76
|
-
log = task.log start: logn
|
77
|
-
log = [] if [{n: 1, t: 'no content'}] == log
|
78
|
-
unless log.empty?
|
79
|
-
STDERR.printf "\r\e[J"
|
80
|
-
log.each {|l| puts l[:t] }
|
81
|
-
logn = log.last[:n]
|
98
|
+
s.stopped? ? :finished : s.status
|
99
|
+
return s
|
82
100
|
end
|
83
101
|
STDERR.printf "\r[%s] \e[33;1m%s\e[0m %s...\e[J",
|
84
102
|
host || s[:id],
|
@@ -99,9 +117,7 @@ class PVE::Cli
|
|
99
117
|
return
|
100
118
|
end
|
101
119
|
task = host.start
|
102
|
-
unless fire
|
103
|
-
wait task, text: "Starting"
|
104
|
-
end
|
120
|
+
wait task, text: "Starting" unless fire
|
105
121
|
end
|
106
122
|
|
107
123
|
def stop host, timeout: nil, fire: nil, secs: nil
|
@@ -120,9 +136,13 @@ class PVE::Cli
|
|
120
136
|
options[:start] = fire && start
|
121
137
|
task = klass.create template, **options
|
122
138
|
return if fire
|
123
|
-
wait task, text: "Creating"
|
124
|
-
|
125
|
-
|
139
|
+
status = wait task, text: "Creating"
|
140
|
+
if status.successfull?
|
141
|
+
host = task.host.refresh!
|
142
|
+
start host, timeout: timeout, secs: secs if start
|
143
|
+
elsif not interactive?
|
144
|
+
exit 1
|
145
|
+
end
|
126
146
|
end
|
127
147
|
|
128
148
|
def destroy ct, timeout: nil, fire: nil, secs: nil
|
@@ -144,6 +164,36 @@ class PVE::Cli
|
|
144
164
|
opt( :fire, "-f", "--[no-]fire", "Do not wait till running", default: false)
|
145
165
|
end
|
146
166
|
|
167
|
+
def complete_lxc f
|
168
|
+
Proxmox::LXC.all.
|
169
|
+
flat_map {|x| [x.name, x.vmid.to_s] }.
|
170
|
+
select {|x| f =~ x }
|
171
|
+
end
|
172
|
+
|
173
|
+
def complete_qemu f
|
174
|
+
Proxmox::Qemu.all.
|
175
|
+
flat_map {|x| [x.name, x.vmid.to_s] }.
|
176
|
+
select {|x| f =~ x }
|
177
|
+
end
|
178
|
+
|
179
|
+
def complete_node f
|
180
|
+
Proxmox::Qemu.all.
|
181
|
+
map {|x| x.name }.
|
182
|
+
select {|x| f =~ x }
|
183
|
+
end
|
184
|
+
|
185
|
+
def completion_helper *pre, arg, &exe
|
186
|
+
if pre.empty?
|
187
|
+
connect
|
188
|
+
xs = yield /\A#{Regexp.quote arg}/
|
189
|
+
STDOUT.print "\a" if xs.empty?
|
190
|
+
xs
|
191
|
+
else
|
192
|
+
STDOUT.print "\a"
|
193
|
+
[]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
147
197
|
def prepare
|
148
198
|
cli_node
|
149
199
|
cli_ct
|
@@ -161,4 +211,23 @@ class PVE::Cli
|
|
161
211
|
STDERR.puts $!
|
162
212
|
exit 1
|
163
213
|
end
|
214
|
+
|
215
|
+
def appliances node, regexp, system, applications
|
216
|
+
system = applications = true if system.nil? and applications.nil?
|
217
|
+
node = node ? Proxmox::Node.find_by_name!( node) : Proxmox::Node.all.first
|
218
|
+
to = TablizedOutput.new %w<Section Package Version OS Template Description>, format: %w[> > > > > <]
|
219
|
+
node.aplinfo.
|
220
|
+
select {|a| 'system' == a.section ? system : applications}.
|
221
|
+
each do |apl|
|
222
|
+
to.push [
|
223
|
+
apl.section,
|
224
|
+
apl.package,
|
225
|
+
apl.version,
|
226
|
+
apl.os,
|
227
|
+
apl.template,
|
228
|
+
apl.description,
|
229
|
+
]
|
230
|
+
end
|
231
|
+
to.print order: [1,2]
|
232
|
+
end
|
164
233
|
end
|
data/lib/pve/helper.rb
CHANGED
@@ -84,9 +84,10 @@ end
|
|
84
84
|
|
85
85
|
|
86
86
|
class TablizedOutput
|
87
|
-
def initialize header, stdout: nil
|
87
|
+
def initialize header, stdout: nil, format: nil
|
88
88
|
@header = header.map &:to_s
|
89
89
|
@columnc = header.size
|
90
|
+
@format = format || ['>']*@columnc
|
90
91
|
@maxs = header.map &:length
|
91
92
|
@stdout ||= STDOUT
|
92
93
|
@lines = []
|
@@ -112,10 +113,12 @@ class TablizedOutput
|
|
112
113
|
def inspect() "#<TO:Percentage #{@v}%>" end
|
113
114
|
|
114
115
|
def to_s
|
115
|
-
y = w - (v*w).round
|
116
|
+
#y = w - (v*w).round
|
117
|
+
y = (v*w).round
|
116
118
|
x = (100*v).round
|
117
119
|
r = "%*s" % [w, 0==x ? '·' : x]
|
118
|
-
"\e[0m#{r[0...y]}\e[1;4;#{0.75>v ? 32 : 31}m#{r[y..-1]}\e[0m"
|
120
|
+
#"\e[0m#{r[0...y]}\e[1;4;#{0.75>v ? 32 : 31}m#{r[y..-1]}\e[0m"
|
121
|
+
"\e[1;4;#{0.75>v ? 32 : 31}m#{r[0...y]}\e[0m#{r[y..-1]}"
|
119
122
|
end
|
120
123
|
end
|
121
124
|
|
@@ -136,7 +139,7 @@ class TablizedOutput
|
|
136
139
|
end
|
137
140
|
|
138
141
|
def print order: nil
|
139
|
-
format = "#{
|
142
|
+
format = "#{@format.map {|f| "\e[%%sm%%#{case f when '<' then '-' else ' ' end}%ds\e[0m"}.join( ' ') % @maxs}\n"
|
140
143
|
ls = @lines
|
141
144
|
if order
|
142
145
|
eval <<-EOC, binding, __FILE__, 1+__LINE__
|
data/lib/pve/proxmox.rb
CHANGED
@@ -123,10 +123,14 @@ module Proxmox
|
|
123
123
|
n.send :__update__, data
|
124
124
|
end
|
125
125
|
private :__new__
|
126
|
+
|
127
|
+
def fetch predata
|
128
|
+
__new__( predata).refresh!
|
129
|
+
end
|
126
130
|
end
|
127
131
|
|
128
|
-
def respond_to? method
|
129
|
-
|
132
|
+
def respond_to? method, also_private = false
|
133
|
+
instance_variable_defined?( "@#{method}") or super(method, also_private)
|
130
134
|
end
|
131
135
|
|
132
136
|
def method_missing method, *args, &exe
|
@@ -147,6 +151,7 @@ module Proxmox
|
|
147
151
|
initialize
|
148
152
|
self
|
149
153
|
end
|
154
|
+
private :__update__
|
150
155
|
|
151
156
|
def refresh!
|
152
157
|
__update__ rest_get( @rest_prefix)
|
@@ -176,9 +181,13 @@ module Proxmox
|
|
176
181
|
def === t
|
177
182
|
@name =~ t or @vmid.to_s =~ t or @sid =~ t
|
178
183
|
end
|
184
|
+
|
185
|
+
def rest_prefix
|
186
|
+
@rest_prefix ||= "/nodes/#{@node}"
|
187
|
+
end
|
179
188
|
|
180
189
|
def initialize
|
181
|
-
|
190
|
+
rest_prefix
|
182
191
|
@sid = "nd:#{@node}"
|
183
192
|
@name = @node
|
184
193
|
end
|
@@ -191,6 +200,20 @@ module Proxmox
|
|
191
200
|
rest_get( "#{@rest_prefix}/lxc").map {|d| LXC.send :__new__, d.merge( node: self, t: 'ct') }
|
192
201
|
end
|
193
202
|
|
203
|
+
def aplinfo
|
204
|
+
return [] if offline?
|
205
|
+
rest_get( "#{@rest_prefix}/aplinfo").map do |d|
|
206
|
+
AplInfo.send :__new__, d.merge( node: self, t: 'apl')
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def storage
|
211
|
+
return [] if offline?
|
212
|
+
rest_get( "#{@rest_prefix}/storage").map do |d|
|
213
|
+
Storage.send :__new__, d.merge( node: self, t: 'sm')
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
194
217
|
def qemu
|
195
218
|
return [] if offline?
|
196
219
|
rest_get( "#{@rest_prefix}/qemu").map {|d| Qemu.send :__new__, d.merge( node: self, t: 'qm') }
|
@@ -211,21 +234,51 @@ module Proxmox
|
|
211
234
|
end
|
212
235
|
|
213
236
|
class Task < Base
|
214
|
-
|
237
|
+
class Status < Base
|
238
|
+
def rest_prefix
|
239
|
+
@rest_prefix ||= '/nodes/%s/tasks/%s/status' % [@node.node, @upid]
|
240
|
+
end
|
241
|
+
|
242
|
+
def refresh!
|
243
|
+
d = rest_get @rest_prefix
|
244
|
+
d[:starttime] &&= Time.at d[:starttime]
|
245
|
+
d = {exitstatus: nil}.merge d
|
246
|
+
__update__ d.merge( node: @node, t: 'status', upid: @upid, task: @task)
|
247
|
+
end
|
248
|
+
|
249
|
+
def initialize
|
250
|
+
rest_prefix
|
251
|
+
@sid = upid
|
252
|
+
end
|
253
|
+
|
254
|
+
def inspect
|
255
|
+
h = instance_variables - %i[@node @task @sid @rest_prefix @upid @t]
|
256
|
+
h.map! {|k| "#{k[1..-1]}=#{instance_variable_get(k).inspect}" }
|
257
|
+
"#<#{self.class.name}|#{@upid} node=#{@node.node} #{h.join ' '}>"
|
258
|
+
end
|
259
|
+
|
260
|
+
def running?() 'running' == @status end
|
261
|
+
def finished?() 'stopped' == @status end
|
262
|
+
alias stopped? finished?
|
263
|
+
def successfull?() stopped? ? 'OK' == @exitstatus : nil end
|
264
|
+
def failed?() stopped? ? 'OK' != @exitstatus : nil end
|
265
|
+
end
|
266
|
+
|
267
|
+
def rest_prefix
|
215
268
|
@rest_prefix = "/nodes/#{@node.node}/tasks/#{upid}"
|
269
|
+
end
|
270
|
+
|
271
|
+
def initialize
|
272
|
+
rest_prefix
|
216
273
|
@sid = upid
|
217
274
|
end
|
218
275
|
|
219
276
|
def inspect
|
220
|
-
"#<#{self.class.name} #{upid}>"
|
277
|
+
"#<#{self.class.name} #{@upid}>"
|
221
278
|
end
|
222
279
|
|
223
|
-
#def finished?
|
224
|
-
# rest_get( "/nodes/#{node}/tasks/")
|
225
|
-
#end
|
226
|
-
|
227
280
|
def status
|
228
|
-
|
281
|
+
Status.fetch node: @node, task: self, upid: @upid
|
229
282
|
end
|
230
283
|
|
231
284
|
def log start: nil, limit: nil
|
@@ -235,8 +288,7 @@ module Proxmox
|
|
235
288
|
|
236
289
|
class Hosted < Base
|
237
290
|
def refresh!
|
238
|
-
|
239
|
-
__update__ rest_get( "#{@rest_prefix}/status/current").merge( node: node, t: t)
|
291
|
+
__update__ rest_get( "#{@rest_prefix}/status/current").merge( node: @node, t: @t)
|
240
292
|
end
|
241
293
|
|
242
294
|
def === t
|
@@ -392,8 +444,12 @@ module Proxmox
|
|
392
444
|
end
|
393
445
|
end
|
394
446
|
|
447
|
+
def rest_prefix
|
448
|
+
@rest_prefix ||= "/nodes/%s/qemu/%d" % [@node.node, @vmid]
|
449
|
+
end
|
450
|
+
|
395
451
|
def initialize
|
396
|
-
|
452
|
+
rest_prefix
|
397
453
|
@sid = "qm:#{@vmid}"
|
398
454
|
end
|
399
455
|
|
@@ -404,6 +460,11 @@ module Proxmox
|
|
404
460
|
def exec *args
|
405
461
|
node.exec 'qm', 'guest', 'exec', vmid, '--', *args
|
406
462
|
end
|
463
|
+
|
464
|
+
def resize disk, size
|
465
|
+
upid = rest_put "#{@rest_prefix}/resize", disk: disk, size: size
|
466
|
+
Task.send :__new__, node: @node, host: self, upid: upid
|
467
|
+
end
|
407
468
|
end
|
408
469
|
|
409
470
|
class LXC < Hosted
|
@@ -502,8 +563,12 @@ module Proxmox
|
|
502
563
|
end
|
503
564
|
end
|
504
565
|
|
566
|
+
def rest_prefix
|
567
|
+
@rest_prefix ||= "/nodes/%s/lxc/%d" % [@node.node, @vmid]
|
568
|
+
end
|
569
|
+
|
505
570
|
def initialize
|
506
|
-
|
571
|
+
rest_prefix
|
507
572
|
@sid = "ct:#{@vmid}"
|
508
573
|
end
|
509
574
|
|
@@ -521,8 +586,12 @@ module Proxmox
|
|
521
586
|
end
|
522
587
|
|
523
588
|
class HA < Base
|
589
|
+
def rest_prefix
|
590
|
+
@rest_prefix ||= "/cluster/ha/resources/#{virt.sid}"
|
591
|
+
end
|
592
|
+
|
524
593
|
def initialize
|
525
|
-
|
594
|
+
rest_prefix
|
526
595
|
end
|
527
596
|
|
528
597
|
class <<self
|
@@ -582,20 +651,51 @@ module Proxmox
|
|
582
651
|
self
|
583
652
|
end
|
584
653
|
|
585
|
-
def started?
|
586
|
-
|
654
|
+
def started?() 'started' == self.state end
|
655
|
+
def stopped?() 'stopped' == self.state end
|
656
|
+
def error?() 'error' == self.state end
|
657
|
+
def active?() ! ! digest end
|
658
|
+
end
|
659
|
+
|
660
|
+
class Storage < Base
|
661
|
+
def rest_prefix
|
662
|
+
@rest_prefix ||= "/nodes/#{@node.node}/storage/#{@storage}"
|
587
663
|
end
|
588
664
|
|
589
|
-
def
|
590
|
-
|
665
|
+
def content
|
666
|
+
rest_get( "#{@rest_prefix}/content").map do |c|
|
667
|
+
Content.send :__new__, c.merge( node: @node, storage: self, t: 'smc')
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
def initialize() rest_prefix end
|
672
|
+
|
673
|
+
def to_s() "#{@node.node}:#{@storage}" end
|
674
|
+
|
675
|
+
class Content < Base
|
676
|
+
def rest_prefix
|
677
|
+
@rest_prefix ||= "/nodes/#{@node.node}/storage/#{@storage}/content/#{@content}"
|
678
|
+
end
|
679
|
+
|
680
|
+
def initialize() rest_prefix end
|
681
|
+
def to_s() "#{node.node} #{volid}" end
|
591
682
|
end
|
683
|
+
end
|
592
684
|
|
593
|
-
|
594
|
-
|
685
|
+
class AplInfo < Base
|
686
|
+
def rest_prefix
|
687
|
+
@rest_prefix ||= "/nodes/#{@node.node}/aplinfo"
|
595
688
|
end
|
596
689
|
|
597
|
-
def
|
598
|
-
|
690
|
+
def initialize() rest_prefix end
|
691
|
+
def name() @template end
|
692
|
+
def system?() 'system' == @section end
|
693
|
+
def debian?() %r[\Adebian-] =~ @os end
|
694
|
+
def lxc?() 'lxc' == @type end
|
695
|
+
|
696
|
+
def download storage
|
697
|
+
upid = rest_post "#{@rest_prefix}", template: @template, storage: storage.to_s
|
698
|
+
Task.send :__new__, node: @node, host: self, upid: upid
|
599
699
|
end
|
600
700
|
end
|
601
701
|
end
|
data/lib/pve/templates.rb
CHANGED
@@ -186,7 +186,8 @@ module PVE::CTTemplate
|
|
186
186
|
options.ostemplate ||
|
187
187
|
case ostype
|
188
188
|
when 'debian'
|
189
|
-
|
189
|
+
# TODO: how to determine which template?
|
190
|
+
'local:vztmpl/debian-11-standard_11.0-1_amd64.tar.gz'
|
190
191
|
else
|
191
192
|
raise ArgumentError, "OS-Template for ostype #{ostype} not found or ostemplate not provided."
|
192
193
|
end
|
data/lib/pve/version.rb
CHANGED
data/pve.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.metadata["source_code_uri"] = spec.homepage
|
20
20
|
spec.metadata["changelog_uri"] = spec.homepage
|
21
21
|
|
22
|
-
spec.add_dependency 'dencli', '~> 0.
|
22
|
+
spec.add_dependency 'dencli', '~> 0.4'
|
23
23
|
spec.add_dependency 'rest-client', '~> 2.1'
|
24
24
|
spec.add_dependency 'ipaddress', '~> 0.8.3'
|
25
25
|
spec.add_dependency 'activesupport', '>= 2'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pve
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Knauf
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dencli
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: '0.4'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: '0.4'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rest-client
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,6 +114,7 @@ files:
|
|
114
114
|
- lib/pve/cli/ha.rb
|
115
115
|
- lib/pve/cli/node.rb
|
116
116
|
- lib/pve/cli/qm.rb
|
117
|
+
- lib/pve/cli/storage.rb
|
117
118
|
- lib/pve/cli/task.rb
|
118
119
|
- lib/pve/helper.rb
|
119
120
|
- lib/pve/proxmox.rb
|