]>
Commit | Line | Data |
---|---|---|
9beee926 DM |
1 | package PVE::CLI::pct; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
6 | use PVE::SafeSyslog; | |
7 | use PVE::Tools qw(extract_param); | |
8 | use PVE::Cluster; | |
9 | use PVE::INotify; | |
10 | use PVE::RPCEnvironment; | |
11 | use PVE::JSONSchema qw(get_standard_option); | |
12 | use PVE::CLIHandler; | |
13 | use PVE::API2::LXC; | |
14 | use PVE::API2::LXC::Config; | |
15 | use PVE::API2::LXC::Status; | |
16 | use PVE::API2::LXC::Snapshot; | |
17 | ||
18 | use Data::Dumper; | |
19 | ||
20 | use base qw(PVE::CLIHandler); | |
21 | ||
22 | my $nodename = PVE::INotify::nodename(); | |
23 | ||
24 | my $upid_exit = sub { | |
25 | my $upid = shift; | |
26 | my $status = PVE::Tools::upid_read_status($upid); | |
27 | exit($status eq 'OK' ? 0 : -1); | |
28 | }; | |
29 | ||
c72bd0ba WL |
30 | __PACKAGE__->register_method ({ |
31 | name => 'unlock', | |
32 | path => 'unlock', | |
33 | method => 'PUT', | |
34 | description => "Unlock the VM.", | |
35 | parameters => { | |
36 | additionalProperties => 0, | |
37 | properties => { | |
38 | vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid }), | |
39 | }, | |
40 | }, | |
41 | returns => { type => 'null'}, | |
42 | code => sub { | |
43 | my ($param) = @_; | |
44 | ||
45 | my $vmid = $param->{vmid}; | |
46 | ||
47 | PVE::LXC::lock_container($vmid, 5, sub { | |
48 | my $conf = PVE::LXC::load_config($vmid); | |
49 | delete $conf->{lock}; | |
50 | PVE::LXC::write_config($vmid, $conf); | |
51 | }); | |
52 | ||
53 | return undef; | |
54 | }}); | |
55 | ||
9beee926 DM |
56 | __PACKAGE__->register_method ({ |
57 | name => 'console', | |
58 | path => 'console', | |
59 | method => 'GET', | |
60 | description => "Launch a console for the specified container.", | |
61 | parameters => { | |
62 | additionalProperties => 0, | |
63 | properties => { | |
64 | vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid_running }), | |
65 | }, | |
66 | }, | |
67 | returns => { type => 'null' }, | |
68 | ||
69 | code => sub { | |
70 | my ($param) = @_; | |
71 | ||
72 | # test if container exists on this node | |
73 | my $conf = PVE::LXC::load_config($param->{vmid}); | |
74 | ||
75 | my $cmd = PVE::LXC::get_console_command($param->{vmid}, $conf); | |
76 | exec(@$cmd); | |
77 | }}); | |
78 | ||
79 | __PACKAGE__->register_method ({ | |
80 | name => 'enter', | |
81 | path => 'enter', | |
82 | method => 'GET', | |
83 | description => "Launch a shell for the specified container.", | |
84 | parameters => { | |
85 | additionalProperties => 0, | |
86 | properties => { | |
87 | vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid_running }), | |
88 | }, | |
89 | }, | |
90 | returns => { type => 'null' }, | |
91 | ||
92 | code => sub { | |
93 | my ($param) = @_; | |
94 | ||
95 | # test if container exists on this node | |
96 | PVE::LXC::load_config($param->{vmid}); | |
97 | ||
98 | exec('lxc-attach', '-n', $param->{vmid}); | |
99 | }}); | |
100 | ||
101 | __PACKAGE__->register_method ({ | |
102 | name => 'exec', | |
103 | path => 'exec', | |
104 | method => 'GET', | |
105 | description => "Launch a command inside the specified container.", | |
106 | parameters => { | |
107 | additionalProperties => 0, | |
108 | properties => { | |
4b09527c | 109 | vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid_running }), |
9beee926 DM |
110 | 'extra-args' => get_standard_option('extra-args'), |
111 | }, | |
112 | }, | |
113 | returns => { type => 'null' }, | |
114 | ||
115 | code => sub { | |
116 | my ($param) = @_; | |
117 | ||
118 | # test if container exists on this node | |
119 | PVE::LXC::load_config($param->{vmid}); | |
120 | ||
121 | if (!@{$param->{'extra-args'}}) { | |
122 | die "missing command"; | |
123 | } | |
124 | exec('lxc-attach', '-n', $param->{vmid}, '--', @{$param->{'extra-args'}}); | |
125 | }}); | |
126 | ||
5ef9e3d1 | 127 | __PACKAGE__->register_method ({ |
f8327ed5 EK |
128 | name => 'fsck', |
129 | path => 'fsck', | |
130 | method => 'PUT', | |
5ef9e3d1 | 131 | description => "Run a filesystem check (fsck) on a container volume.", |
f8327ed5 EK |
132 | parameters => { |
133 | additionalProperties => 0, | |
134 | properties => { | |
135 | vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid_stopped }), | |
136 | force => { | |
137 | optional => 1, | |
138 | type => 'boolean', | |
139 | description => "Force checking, even if the filesystem seems clean", | |
140 | default => 0, | |
141 | }, | |
142 | device => { | |
143 | optional => 1, | |
144 | type => 'string', | |
145 | description => "A volume on which to run the filesystem check", | |
146 | enum => [PVE::LXC::mountpoint_names()], | |
147 | }, | |
148 | }, | |
149 | }, | |
150 | returns => { type => 'null' }, | |
151 | code => sub { | |
152 | ||
153 | my ($param) = @_; | |
154 | my $vmid = $param->{'vmid'}; | |
155 | my $device = defined($param->{'device'}) ? $param->{'device'} : 'rootfs'; | |
156 | ||
157 | my $command = ['fsck', '-a', '-l']; | |
158 | push(@$command, '-f') if $param->{force}; | |
159 | ||
160 | # critical path: all of this will be done while the container is locked | |
161 | my $do_fsck = sub { | |
162 | ||
163 | my $conf = PVE::LXC::load_config($vmid); | |
164 | my $storage_cfg = PVE::Storage::config(); | |
165 | ||
166 | defined($conf->{$device}) || die "cannot run command on unexisting mountpoint $device\n"; | |
167 | ||
168 | my $mount_point = PVE::LXC::parse_ct_mountpoint($conf->{$device}); | |
169 | my $volid = $mount_point->{volume}; | |
170 | ||
171 | my $path; | |
172 | my $storage_id = PVE::Storage::parse_volume_id($volid, 1); | |
173 | ||
174 | if ($storage_id) { | |
f8327ed5 | 175 | my (undef, undef, undef, undef, undef, undef, $format) = |
da9e487b | 176 | PVE::Storage::parse_volname($storage_cfg, $volid); |
f8327ed5 | 177 | |
da9e487b DM |
178 | die "unable to run fsck for '$volid' (format == $format)\n" |
179 | if $format ne 'raw'; | |
f8327ed5 EK |
180 | |
181 | $path = PVE::Storage::path($storage_cfg, $volid); | |
182 | ||
183 | } else { | |
da9e487b | 184 | if (($volid =~ m|^/.+|) && (-b $volid)) { |
f8327ed5 EK |
185 | # pass block devices directly |
186 | $path = $volid; | |
187 | } else { | |
da9e487b | 188 | die "path '$volid' does not point to a block device\n"; |
f8327ed5 EK |
189 | } |
190 | } | |
191 | ||
192 | push(@$command, $path); | |
193 | ||
da9e487b DM |
194 | PVE::LXC::check_running($vmid) && |
195 | die "cannot run fsck on active container\n"; | |
196 | ||
f8327ed5 EK |
197 | PVE::Tools::run_command($command); |
198 | }; | |
199 | ||
200 | PVE::LXC::lock_container($vmid, undef, $do_fsck); | |
201 | return undef; | |
202 | }}); | |
203 | ||
9beee926 DM |
204 | our $cmddef = { |
205 | list=> [ 'PVE::API2::LXC', 'vmlist', [], { node => $nodename }, sub { | |
206 | my $res = shift; | |
207 | return if !scalar(@$res); | |
208 | my $format = "%-10s %-10s %-20s\n"; | |
209 | printf($format, 'VMID', 'Status', 'Name'); | |
210 | foreach my $d (sort {$a->{vmid} <=> $b->{vmid} } @$res) { | |
211 | printf($format, $d->{vmid}, $d->{status}, $d->{name}); | |
212 | } | |
213 | }], | |
214 | config => [ "PVE::API2::LXC::Config", 'vm_config', ['vmid'], | |
215 | { node => $nodename }, sub { | |
216 | my $config = shift; | |
217 | foreach my $k (sort (keys %$config)) { | |
218 | next if $k eq 'digest'; | |
219 | my $v = $config->{$k}; | |
220 | if ($k eq 'description') { | |
221 | $v = PVE::Tools::encode_text($v); | |
222 | } | |
223 | print "$k: $v\n"; | |
224 | } | |
225 | }], | |
226 | set => [ 'PVE::API2::LXC::Config', 'update_vm', ['vmid'], { node => $nodename }], | |
40626217 | 227 | |
985b18ed | 228 | resize => [ "PVE::API2::LXC", 'resize_vm', ['vmid', 'disk', 'size'], { node => $nodename } ], |
9beee926 DM |
229 | |
230 | create => [ 'PVE::API2::LXC', 'create_vm', ['vmid', 'ostemplate'], { node => $nodename }, $upid_exit ], | |
231 | restore => [ 'PVE::API2::LXC', 'create_vm', ['vmid', 'ostemplate'], { node => $nodename, restore => 1 }, $upid_exit ], | |
232 | ||
233 | start => [ 'PVE::API2::LXC::Status', 'vm_start', ['vmid'], { node => $nodename }, $upid_exit], | |
234 | suspend => [ 'PVE::API2::LXC::Status', 'vm_suspend', ['vmid'], { node => $nodename }, $upid_exit], | |
235 | resume => [ 'PVE::API2::LXC::Status', 'vm_resume', ['vmid'], { node => $nodename }, $upid_exit], | |
236 | shutdown => [ 'PVE::API2::LXC::Status', 'vm_shutdown', ['vmid'], { node => $nodename }, $upid_exit], | |
237 | stop => [ 'PVE::API2::LXC::Status', 'vm_stop', ['vmid'], { node => $nodename }, $upid_exit], | |
238 | ||
239 | migrate => [ "PVE::API2::LXC", 'migrate_vm', ['vmid', 'target'], { node => $nodename }, $upid_exit], | |
240 | ||
241 | console => [ __PACKAGE__, 'console', ['vmid']], | |
242 | enter => [ __PACKAGE__, 'enter', ['vmid']], | |
c72bd0ba | 243 | unlock => [ __PACKAGE__, 'unlock', ['vmid']], |
9beee926 | 244 | exec => [ __PACKAGE__, 'exec', ['vmid', 'extra-args']], |
f8327ed5 | 245 | fsck => [ __PACKAGE__, 'fsck', ['vmid']], |
9beee926 DM |
246 | |
247 | destroy => [ 'PVE::API2::LXC', 'destroy_vm', ['vmid'], | |
248 | { node => $nodename }, $upid_exit ], | |
249 | ||
250 | snapshot => [ "PVE::API2::LXC::Snapshot", 'snapshot', ['vmid', 'snapname'], | |
251 | { node => $nodename } , $upid_exit ], | |
252 | ||
253 | delsnapshot => [ "PVE::API2::LXC::Snapshot", 'delsnapshot', ['vmid', 'snapname'], { node => $nodename } , $upid_exit ], | |
254 | ||
255 | listsnapshot => [ "PVE::API2::LXC::Snapshot", 'list', ['vmid'], { node => $nodename }, | |
256 | sub { | |
257 | my $res = shift; | |
258 | foreach my $e (@$res) { | |
259 | my $headline = $e->{description} || 'no-description'; | |
260 | $headline =~ s/\n.*//sg; | |
261 | my $parent = $e->{parent} // 'no-parent'; | |
262 | printf("%-20s %-20s %s\n", $e->{name}, $parent, $headline); | |
263 | } | |
264 | }], | |
265 | ||
266 | rollback => [ "PVE::API2::LXC::Snapshot", 'rollback', ['vmid', 'snapname'], { node => $nodename } , $upid_exit ], | |
267 | ||
268 | template => [ "PVE::API2::LXC", 'template', ['vmid'], { node => $nodename }], | |
269 | }; | |
270 | ||
271 | ||
272 | 1; | |
273 | ||
274 | __END__ | |
275 | ||
276 | =head1 NAME | |
277 | ||
278 | pct - Tool to manage Linux Containers (LXC) on Proxmox VE | |
279 | ||
280 | =head1 SYNOPSIS | |
281 | ||
282 | =include synopsis | |
283 | ||
284 | =head1 DESCRIPTION | |
285 | ||
286 | pct is a tool to manages Linux Containers (LXC). You can create | |
287 | and destroy containers, and control execution | |
288 | (start/stop/suspend/resume). Besides that, you can use pct to set | |
289 | parameters in the associated config file, like network configuration or | |
290 | memory. | |
291 | ||
292 | =head1 EXAMPLES | |
293 | ||
294 | Create a container based on a Debian template | |
295 | (provided you downloaded the template via the webgui before) | |
296 | ||
297 | pct create 100 /var/lib/vz/template/cache/debian-8.0-standard_8.0-1_amd64.tar.gz | |
298 | ||
299 | Start a container | |
300 | ||
301 | pct start 100 | |
302 | ||
303 | Start a login session via getty | |
304 | ||
305 | pct console 100 | |
306 | ||
307 | Enter the lxc namespace and run a shell as root user | |
308 | ||
309 | pct enter 100 | |
310 | ||
311 | Display the configuration | |
312 | ||
313 | pct config 100 | |
314 | ||
315 | Add a network interface called eth0, bridged to the host bridge vmbr0, | |
316 | set the address and gateway, while it's running | |
317 | ||
318 | pct set 100 -net0 name=eth0,bridge=vmbr0,ip=192.168.15.147/24,gw=192.168.15.1 | |
319 | ||
320 | Reduce the memory of the container to 512MB | |
321 | ||
322 | pct set -memory 512 100 | |
323 | ||
324 | =head1 FILES | |
325 | ||
326 | /etc/pve/lxc/<vmid>.conf | |
327 | ||
328 | Configuration file for the container <vmid> | |
329 | ||
330 | =head1 SEE ALSO | |
331 | ||
332 | L<B<qm(1)>>, L<B<pvesh(1)>> | |
333 | ||
334 | =include pve_copyright |