]> git.proxmox.com Git - pve-container.git/blob - src/PVE/API2/LXC/Snapshot.pm
remove Data::Dumper usages
[pve-container.git] / src / PVE / API2 / LXC / Snapshot.pm
1 package PVE::API2::LXC::Snapshot;
2
3 use strict;
4 use warnings;
5
6 use PVE::SafeSyslog;
7 use PVE::Tools qw(extract_param run_command);
8 use PVE::Exception qw(raise raise_param_exc);
9 use PVE::INotify;
10 use PVE::Cluster qw(cfs_read_file);
11 use PVE::AccessControl;
12 use PVE::Firewall;
13 use PVE::Storage;
14 use PVE::RESTHandler;
15 use PVE::RPCEnvironment;
16 use PVE::LXC;
17 use PVE::LXC::Create;
18 use PVE::JSONSchema qw(get_standard_option);
19 use base qw(PVE::RESTHandler);
20
21 __PACKAGE__->register_method({
22 name => 'list',
23 path => '',
24 method => 'GET',
25 description => "List all snapshots.",
26 permissions => {
27 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
28 },
29 proxyto => 'node',
30 protected => 1, # lxc pid files are only readable by root
31 parameters => {
32 additionalProperties => 0,
33 properties => {
34 vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid }),
35 node => get_standard_option('pve-node'),
36 },
37 },
38 returns => {
39 type => 'array',
40 items => {
41 type => "object",
42 properties => {
43 name => {
44 description => "Snapshot identifier. Value 'current' identifies the current VM.",
45 type => 'string',
46 },
47 description => {
48 description => "Snapshot description.",
49 type => 'string',
50 },
51 snaptime => {
52 description => "Snapshot creation time",
53 type => 'integer',
54 renderer => 'timestamp',
55 optional => 1,
56 },
57 parent => {
58 description => "Parent snapshot identifier.",
59 type => 'string',
60 optional => 1,
61 },
62 },
63 },
64 links => [ { rel => 'child', href => "{name}" } ],
65 },
66 code => sub {
67 my ($param) = @_;
68
69 my $vmid = $param->{vmid};
70
71 my $conf = PVE::LXC::Config->load_config($vmid);
72 my $snaphash = $conf->{snapshots} || {};
73
74 my $res = [];
75
76 foreach my $name (keys %$snaphash) {
77 my $d = $snaphash->{$name};
78 my $item = {
79 name => $name,
80 snaptime => $d->{snaptime} || 0,
81 description => $d->{description} || '',
82 };
83 $item->{parent} = $d->{parent} if defined($d->{parent});
84 $item->{snapstate} = $d->{snapstate} if $d->{snapstate};
85 push @$res, $item;
86 }
87
88 my $running = PVE::LXC::check_running($vmid) ? 1 : 0;
89 my $current = {
90 name => 'current',
91 digest => $conf->{digest},
92 running => $running,
93 description => "You are here!",
94 };
95 $current->{parent} = $conf->{parent} if defined($conf->{parent});
96
97 push @$res, $current;
98
99 return $res;
100 }});
101
102 __PACKAGE__->register_method({
103 name => 'snapshot',
104 path => '',
105 method => 'POST',
106 protected => 1,
107 proxyto => 'node',
108 description => "Snapshot a container.",
109 permissions => {
110 check => ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
111 },
112 parameters => {
113 additionalProperties => 0,
114 properties => {
115 node => get_standard_option('pve-node'),
116 vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid }),
117 snapname => get_standard_option('pve-snapshot-name'),
118 # vmstate => {
119 # optional => 1,
120 # type => 'boolean',
121 # description => "Save the vmstate",
122 # },
123 description => {
124 optional => 1,
125 type => 'string',
126 description => "A textual description or comment.",
127 },
128 },
129 },
130 returns => {
131 type => 'string',
132 description => "the task ID.",
133 },
134 code => sub {
135 my ($param) = @_;
136
137 my $rpcenv = PVE::RPCEnvironment::get();
138
139 my $authuser = $rpcenv->get_user();
140
141 my $node = extract_param($param, 'node');
142
143 my $vmid = extract_param($param, 'vmid');
144
145 my $snapname = extract_param($param, 'snapname');
146
147 die "unable to use snapshot name 'current' (reserved name)\n"
148 if $snapname eq 'current';
149
150 die "unable to use snapshot name 'vzdump' (reserved name)\n"
151 if $snapname eq 'vzdump';
152
153 my $realcmd = sub {
154 PVE::Cluster::log_msg('info', $authuser, "snapshot container $vmid: $snapname");
155 PVE::LXC::Config->snapshot_create($vmid, $snapname, 0, $param->{description});
156 };
157
158 return $rpcenv->fork_worker('vzsnapshot', $vmid, $authuser, $realcmd);
159 }});
160
161 __PACKAGE__->register_method({
162 name => 'delsnapshot',
163 path => '{snapname}',
164 method => 'DELETE',
165 protected => 1,
166 proxyto => 'node',
167 description => "Delete a LXC snapshot.",
168 permissions => {
169 check => ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
170 },
171 parameters => {
172 additionalProperties => 0,
173 properties => {
174 node => get_standard_option('pve-node'),
175 vmid => get_standard_option('pve-vmid'),
176 snapname => get_standard_option('pve-snapshot-name'),
177 force => {
178 optional => 1,
179 type => 'boolean',
180 description => "For removal from config file, even if removing disk snapshots fails.",
181 },
182 },
183 },
184 returns => {
185 type => 'string',
186 description => "the task ID.",
187 },
188 code => sub {
189 my ($param) = @_;
190
191 my $rpcenv = PVE::RPCEnvironment::get();
192
193 my $authuser = $rpcenv->get_user();
194
195 my $node = extract_param($param, 'node');
196
197 my $vmid = extract_param($param, 'vmid');
198
199 my $snapname = extract_param($param, 'snapname');
200
201 my $realcmd = sub {
202 PVE::Cluster::log_msg('info', $authuser, "delete snapshot VM $vmid: $snapname");
203 PVE::LXC::Config->snapshot_delete($vmid, $snapname, $param->{force});
204 };
205
206 return $rpcenv->fork_worker('vzdelsnapshot', $vmid, $authuser, $realcmd);
207 }});
208
209 __PACKAGE__->register_method({
210 name => 'snapshot_cmd_idx',
211 path => '{snapname}',
212 description => '',
213 method => 'GET',
214 permissions => {
215 user => 'all',
216 },
217 parameters => {
218 additionalProperties => 0,
219 properties => {
220 vmid => get_standard_option('pve-vmid'),
221 node => get_standard_option('pve-node'),
222 snapname => get_standard_option('pve-snapshot-name'),
223 },
224 },
225 returns => {
226 type => 'array',
227 items => {
228 type => "object",
229 properties => {},
230 },
231 links => [ { rel => 'child', href => "{cmd}" } ],
232 },
233 code => sub {
234 my ($param) = @_;
235
236 my $res = [];
237
238 push @$res, { cmd => 'rollback' };
239 push @$res, { cmd => 'config' };
240
241 return $res;
242 }});
243
244 __PACKAGE__->register_method({
245 name => 'rollback',
246 path => '{snapname}/rollback',
247 method => 'POST',
248 protected => 1,
249 proxyto => 'node',
250 description => "Rollback LXC state to specified snapshot.",
251 permissions => {
252 check => ['perm', '/vms/{vmid}', [ 'VM.Snapshot', 'VM.Snapshot.Rollback' ], any => 1],
253 },
254 parameters => {
255 additionalProperties => 0,
256 properties => {
257 node => get_standard_option('pve-node'),
258 vmid => get_standard_option('pve-vmid'),
259 snapname => get_standard_option('pve-snapshot-name'),
260 },
261 },
262 returns => {
263 type => 'string',
264 description => "the task ID.",
265 },
266 code => sub {
267 my ($param) = @_;
268
269 my $rpcenv = PVE::RPCEnvironment::get();
270
271 my $authuser = $rpcenv->get_user();
272
273 my $node = extract_param($param, 'node');
274
275 my $vmid = extract_param($param, 'vmid');
276
277 my $snapname = extract_param($param, 'snapname');
278
279 my $realcmd = sub {
280 PVE::Cluster::log_msg('info', $authuser, "rollback snapshot LXC $vmid: $snapname");
281 PVE::LXC::Config->snapshot_rollback($vmid, $snapname);
282 };
283
284 my $worker = sub {
285 # hold migration lock, this makes sure that nobody create replication snapshots
286 return PVE::GuestHelpers::guest_migration_lock($vmid, 10, $realcmd);
287 };
288
289 return $rpcenv->fork_worker('vzrollback', $vmid, $authuser, $worker);
290 }});
291
292 __PACKAGE__->register_method({
293 name => 'update_snapshot_config',
294 path => '{snapname}/config',
295 method => 'PUT',
296 protected => 1,
297 proxyto => 'node',
298 description => "Update snapshot metadata.",
299 permissions => {
300 check => ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
301 },
302 parameters => {
303 additionalProperties => 0,
304 properties => {
305 node => get_standard_option('pve-node'),
306 vmid => get_standard_option('pve-vmid'),
307 snapname => get_standard_option('pve-snapshot-name'),
308 description => {
309 optional => 1,
310 type => 'string',
311 description => "A textual description or comment.",
312 },
313 },
314 },
315 returns => { type => 'null' },
316 code => sub {
317 my ($param) = @_;
318
319 my $rpcenv = PVE::RPCEnvironment::get();
320
321 my $authuser = $rpcenv->get_user();
322
323 my $vmid = extract_param($param, 'vmid');
324
325 my $snapname = extract_param($param, 'snapname');
326
327 return undef if !defined($param->{description});
328
329 my $updatefn = sub {
330
331 my $conf = PVE::LXC::Config->load_config($vmid);
332 PVE::LXC::Config->check_lock($conf);
333
334 my $snap = $conf->{snapshots}->{$snapname};
335
336 die "snapshot '$snapname' does not exist\n" if !defined($snap);
337
338 $snap->{description} = $param->{description} if defined($param->{description});
339
340 PVE::LXC::Config->write_config($vmid, $conf, 1);
341 };
342
343 PVE::LXC::Config->lock_config($vmid, $updatefn);
344
345 return undef;
346 }});
347
348 __PACKAGE__->register_method({
349 name => 'get_snapshot_config',
350 path => '{snapname}/config',
351 method => 'GET',
352 proxyto => 'node',
353 description => "Get snapshot configuration",
354 permissions => {
355 check => ['perm', '/vms/{vmid}', [ 'VM.Snapshot', 'VM.Snapshot.Rollback' ], any => 1],
356 },
357 parameters => {
358 additionalProperties => 0,
359 properties => {
360 node => get_standard_option('pve-node'),
361 vmid => get_standard_option('pve-vmid'),
362 snapname => get_standard_option('pve-snapshot-name'),
363 },
364 },
365 returns => { type => "object" },
366 code => sub {
367 my ($param) = @_;
368
369 my $rpcenv = PVE::RPCEnvironment::get();
370
371 my $authuser = $rpcenv->get_user();
372
373 my $vmid = extract_param($param, 'vmid');
374
375 my $snapname = extract_param($param, 'snapname');
376
377 my $conf = PVE::LXC::Config->load_config($vmid);
378
379 my $snap = $conf->{snapshots}->{$snapname};
380
381 die "snapshot '$snapname' does not exist\n" if !defined($snap);
382
383 delete $snap->{lxc};
384
385 return $snap;
386 }});
387
388 1;