]>
git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Backup.pm
1 package PVE
::API2
::Backup
;
8 use PVE
::Tools
qw(extract_param);
9 use PVE
::Cluster
qw(cfs_register_file cfs_lock_file cfs_read_file cfs_write_file);
11 use PVE
::RPCEnvironment
;
14 use PVE
::Exception
qw(raise_param_exc);
17 use base
qw(PVE::RESTHandler);
19 cfs_register_file
('vzdump.cron',
20 \
&parse_vzdump_cron_config
,
21 \
&write_vzdump_cron_config
);
23 PVE
::JSONSchema
::register_format
('pve-day-of-week', \
&verify_day_of_week
);
24 sub verify_day_of_week
{
25 my ($value, $noerr) = @_;
27 return $value if $value =~ m/^(mon|tue|wed|thu|fri|sat|sun)$/;
29 return undef if $noerr;
31 die "invalid day '$value'\n";
34 my $vzdump_job_id_prop = {
36 description
=> "The job ID.",
40 my $dowhash_to_dow = sub {
44 push @da, $num ?
1 : 'mon' if $d->{mon
};
45 push @da, $num ?
2 : 'tue' if $d->{tue
};
46 push @da, $num ?
3 : 'wed' if $d->{wed
};
47 push @da, $num ?
4 : 'thu' if $d->{thu
};
48 push @da, $num ?
5 : 'fri' if $d->{fri
};
49 push @da, $num ?
6 : 'sat' if $d->{sat
};
50 push @da, $num ?
7 : 'sun' if $d->{sun
};
55 # parse crontab style day of week
57 my ($dowstr, $noerr) = @_;
59 my $dowmap = {mon
=> 1, tue
=> 2, wed
=> 3, thu
=> 4,
60 fri
=> 5, sat
=> 6, sun
=> 7};
61 my $rdowmap = { '1' => 'mon', '2' => 'tue', '3' => 'wed', '4' => 'thu',
62 '5' => 'fri', '6' => 'sat', '7' => 'sun', '0' => 'sun'};
66 $dowstr = '1,2,3,4,5,6,7' if $dowstr eq '*';
68 foreach my $day (PVE
::Tools
::split_list
($dowstr)) {
69 if ($day =~ m/^(mon|tue|wed|thu|fri|sat|sun)-(mon|tue|wed|thu|fri|sat|sun)$/i) {
70 for (my $i = $dowmap->{lc($1)}; $i <= $dowmap->{lc($2)}; $i++) {
71 my $r = $rdowmap->{$i};
74 } elsif ($day =~ m/^(mon|tue|wed|thu|fri|sat|sun|[0-7])$/i) {
75 $day = $rdowmap->{$day} if $day =~ m/\d/;
78 return undef if $noerr;
79 die "unable to parse day of week '$dowstr'\n";
86 my $vzdump_properties = {
87 additionalProperties
=> 0,
88 properties
=> PVE
::VZDump
::json_config_properties
({}),
91 sub parse_vzdump_cron_config
{
92 my ($filename, $raw) = @_;
94 my $jobs = []; # correct jobs
96 my $ejobs = []; # mailfomerd lines
98 my $jid = 1; # we start at 1
100 my $digest = Digest
::SHA
::sha1_hex
(defined($raw) ?
$raw : '');
102 while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
105 next if $line =~ m/^\#/;
106 next if $line =~ m/^\s*$/;
107 next if $line =~ m/^PATH\s*=/; # we always overwrite path
109 if ($line =~ m
|^(\d
+)\s
+(\d
+)\s
+\
*\s
+\
*\s
+(\S
+)\s
+root\s
+(/\S+/)?
(#)?vzdump(\s+(.*))?$|) {
111 my $minute = int($1);
117 my $dowhash = parse_dow
($dow, 1);
118 die "unable to parse day of week '$dow' in '$filename'\n" if !$dowhash;
120 my $args = PVE
::Tools
::split_args
($param);
121 my $opts = PVE
::JSONSchema
::get_options
($vzdump_properties, $args, 'vmid');
123 $opts->{enabled
} = !defined($enabled);
124 $opts->{id
} = "$digest:$jid";
126 $opts->{starttime
} = sprintf "%02d:%02d", $hour, $minute;
127 $opts->{dow
} = &$dowhash_to_dow($dowhash);
133 syslog
('err', "parse error in '$filename': $err");
134 push @$ejobs, { line
=> $line };
136 } elsif ($line =~ m
|^\S
+\s
+(\S
+)\s
+\S
+\s
+\S
+\s
+\S
+\s
+\S
+\s
+(\S
.*)$|) {
137 syslog
('err', "warning: malformed line in '$filename'");
138 push @$ejobs, { line
=> $line };
140 syslog
('err', "ignoring malformed line in '$filename'");
145 $res->{digest
} = $digest;
146 $res->{jobs
} = $jobs;
147 $res->{ejobs
} = $ejobs;
152 sub write_vzdump_cron_config
{
153 my ($filename, $cfg) = @_;
155 my $out = "# cluster wide vzdump cron schedule\n";
156 $out .= "# Automatically generated file - do not edit\n\n";
157 $out .= "PATH=\"/usr/sbin:/usr/bin:/sbin:/bin\"\n\n";
159 my $jobs = $cfg->{jobs
} || [];
160 foreach my $job (@$jobs) {
161 my $enabled = ($job->{enabled
}) ?
'' : '#';
162 my $dh = parse_dow
($job->{dow
});
164 if ($dh->{mon
} && $dh->{tue
} && $dh->{wed
} && $dh->{thu
} &&
165 $dh->{fri
} && $dh->{sat
} && $dh->{sun
}) {
168 $dow = &$dowhash_to_dow($dh, 1);
174 die "no job start time specified\n" if !$job->{starttime
};
175 if ($job->{starttime
} =~ m/^(\d{1,2}):(\d{1,2})$/) {
176 ($hour, $minute) = (int($1), int($2));
177 die "hour '$hour' out of range\n" if $hour < 0 || $hour > 23;
178 die "minute '$minute' out of range\n" if $minute < 0 || $minute > 59;
180 die "unable to parse job start time\n";
183 $job->{quiet
} = 1; # we do not want messages from cron
185 my $cmd = PVE
::VZDump
::command_line
($job);
187 $out .= sprintf "$minute $hour * * %-11s root $enabled$cmd\n", $dow;
190 my $ejobs = $cfg->{ejobs
} || [];
191 foreach my $job (@$ejobs) {
192 $out .= "$job->{line}\n" if $job->{line
};
198 __PACKAGE__-
>register_method({
202 description
=> "List vzdump backup schedule.",
204 check
=> ['perm', '/', ['Sys.Audit']],
207 additionalProperties
=> 0,
215 id
=> $vzdump_job_id_prop
218 links
=> [ { rel
=> 'child', href
=> "{id}" } ],
223 my $rpcenv = PVE
::RPCEnvironment
::get
();
224 my $user = $rpcenv->get_user();
226 my $data = cfs_read_file
('vzdump.cron');
228 my $res = $data->{jobs
} || [];
233 __PACKAGE__-
>register_method({
234 name
=> 'create_job',
238 description
=> "Create new vzdump backup job.",
240 check
=> ['perm', '/', ['Sys.Modify']],
241 description
=> "The 'tmpdir', 'dumpdir' and 'script' parameters are additionally restricted to the 'root\@pam' user.",
244 additionalProperties
=> 0,
245 properties
=> PVE
::VZDump
::json_config_properties
({
248 description
=> "Job Start time.",
249 pattern
=> '\d{1,2}:\d{1,2}',
253 type
=> 'string', format
=> 'pve-day-of-week-list',
255 description
=> "Day of week selection.",
256 default => 'mon,tue,wed,thu,fri,sat,sun',
261 description
=> "Enable or disable the job.",
266 returns
=> { type
=> 'null' },
270 my $rpcenv = PVE
::RPCEnvironment
::get
();
271 my $user = $rpcenv->get_user();
273 foreach my $key (qw(tmpdir dumpdir script)) {
274 raise_param_exc
({ $key => "Only root may set this option."})
275 if defined($param->{$key}) && ($user ne 'root@pam');
278 if (my $pool = $param->{pool
}) {
279 $rpcenv->check_pool_exist($pool);
280 $rpcenv->check($user, "/pool/$pool", ['VM.Backup']);
284 my $create_job = sub {
285 my $data = cfs_read_file
('vzdump.cron');
287 $param->{dow
} = 'mon,tue,wed,thu,fri,sat,sun' if !defined($param->{dow
});
288 $param->{enabled
} = 1 if !defined($param->{enabled
});
289 PVE
::VZDump
::verify_vzdump_parameters
($param, 1);
291 push @{$data->{jobs
}}, $param;
293 cfs_write_file
('vzdump.cron', $data);
295 cfs_lock_file
('vzdump.cron', undef, $create_job);
301 __PACKAGE__-
>register_method({
305 description
=> "Read vzdump backup job definition.",
307 check
=> ['perm', '/', ['Sys.Audit']],
310 additionalProperties
=> 0,
312 id
=> $vzdump_job_id_prop
321 my $rpcenv = PVE
::RPCEnvironment
::get
();
322 my $user = $rpcenv->get_user();
324 my $data = cfs_read_file
('vzdump.cron');
326 my $jobs = $data->{jobs
} || [];
328 foreach my $job (@$jobs) {
329 return $job if $job->{id
} eq $param->{id
};
332 raise_param_exc
({ id
=> "No such job '$param->{id}'" });
336 __PACKAGE__-
>register_method({
337 name
=> 'delete_job',
340 description
=> "Delete vzdump backup job definition.",
342 check
=> ['perm', '/', ['Sys.Modify']],
346 additionalProperties
=> 0,
348 id
=> $vzdump_job_id_prop
351 returns
=> { type
=> 'null' },
355 my $rpcenv = PVE
::RPCEnvironment
::get
();
356 my $user = $rpcenv->get_user();
358 my $delete_job = sub {
359 my $data = cfs_read_file
('vzdump.cron');
361 my $jobs = $data->{jobs
} || [];
365 foreach my $job (@$jobs) {
366 if ($job->{id
} eq $param->{id
}) {
369 push @$newjobs, $job;
373 raise_param_exc
({ id
=> "No such job '$param->{id}'" }) if !$found;
375 $data->{jobs
} = $newjobs;
377 cfs_write_file
('vzdump.cron', $data);
379 cfs_lock_file
('vzdump.cron', undef, $delete_job);
385 __PACKAGE__-
>register_method({
386 name
=> 'update_job',
390 description
=> "Update vzdump backup job definition.",
392 check
=> ['perm', '/', ['Sys.Modify']],
395 additionalProperties
=> 0,
396 properties
=> PVE
::VZDump
::json_config_properties
({
397 id
=> $vzdump_job_id_prop,
400 description
=> "Job Start time.",
401 pattern
=> '\d{1,2}:\d{1,2}',
405 type
=> 'string', format
=> 'pve-day-of-week-list',
407 description
=> "Day of week selection.",
410 type
=> 'string', format
=> 'pve-configid-list',
411 description
=> "A list of settings you want to delete.",
417 description
=> "Enable or disable the job.",
422 returns
=> { type
=> 'null' },
426 my $rpcenv = PVE
::RPCEnvironment
::get
();
427 my $user = $rpcenv->get_user();
429 if (my $pool = $param->{pool
}) {
430 $rpcenv->check_pool_exist($pool);
431 $rpcenv->check($user, "/pool/$pool", ['VM.Backup']);
434 my $update_job = sub {
435 my $data = cfs_read_file
('vzdump.cron');
437 my $jobs = $data->{jobs
} || [];
439 die "no options specified\n" if !scalar(keys %$param);
441 PVE
::VZDump
::verify_vzdump_parameters
($param);
443 my @delete = PVE
::Tools
::split_list
(extract_param
($param, 'delete'));
445 foreach my $job (@$jobs) {
446 if ($job->{id
} eq $param->{id
}) {
448 foreach my $k (@delete) {
449 if (!PVE
::VZDump
::option_exists
($k)) {
450 raise_param_exc
({ delete => "unknown option '$k'" });
456 foreach my $k (keys %$param) {
457 $job->{$k} = $param->{$k};
460 $job->{all
} = 1 if (defined($job->{exclude
}) && !defined($job->{pool
}));
462 if (defined($param->{vmid
})) {
464 delete $job->{exclude
};
466 } elsif ($param->{all
}) {
469 } elsif ($job->{pool
}) {
474 PVE
::VZDump
::verify_vzdump_parameters
($job, 1);
476 cfs_write_file
('vzdump.cron', $data);
481 raise_param_exc
({ id
=> "No such job '$param->{id}'" });
483 cfs_lock_file
('vzdump.cron', undef, $update_job);