]>
git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Backup.pm
1 package PVE
::API2
::Backup
;
7 use PVE
::Tools
qw(extract_param);
8 use PVE
::Cluster
qw(cfs_register_file cfs_lock_file cfs_read_file cfs_write_file);
10 use PVE
::RPCEnvironment
;
13 use PVE
::Exception
qw(raise_param_exc);
16 use base
qw(PVE::RESTHandler);
18 cfs_register_file
('vzdump',
19 \
&parse_vzdump_cron_config
,
20 \
&write_vzdump_cron_config
);
22 PVE
::JSONSchema
::register_format
('pve-day-of-week', \
&verify_day_of_week
);
23 sub verify_day_of_week
{
24 my ($value, $noerr) = @_;
26 return $value if $value =~ m/^(mon|tue|wed|thu|fri|sat|sun)$/;
28 return undef if $noerr;
30 die "invalid day '$value'\n";
34 my $dowhash_to_dow = sub {
38 push @da, $num ?
1 : 'mon' if $d->{mon
};
39 push @da, $num ?
2 : 'tue' if $d->{tue
};
40 push @da, $num ?
3 : 'wed' if $d->{wed
};
41 push @da, $num ?
4 : 'thu' if $d->{thu
};
42 push @da, $num ?
5 : 'fri' if $d->{fri
};
43 push @da, $num ?
6 : 'sat' if $d->{sat
};
44 push @da, $num ?
7 : 'sun' if $d->{sun
};
49 # parse crontab style day of week
51 my ($dowstr, $noerr) = @_;
53 my $dowmap = {mon
=> 1, tue
=> 2, wed
=> 3, thu
=> 4,
54 fri
=> 5, sat
=> 6, sun
=> 7};
55 my $rdowmap = { '1' => 'mon', '2' => 'tue', '3' => 'wed', '4' => 'thu',
56 '5' => 'fri', '6' => 'sat', '7' => 'sun', '0' => 'sun'};
60 $dowstr = '1,2,3,4,5,6,7' if $dowstr eq '*';
62 foreach my $day (split (/,/, $dowstr)) {
63 if ($day =~ m/^(mon|tue|wed|thu|fri|sat|sun)-(mon|tue|wed|thu|fri|sat|sun)$/i) {
64 for (my $i = $dowmap->{lc($1)}; $i <= $dowmap->{lc($2)}; $i++) {
65 my $r = $rdowmap->{$i};
68 } elsif ($day =~ m/^(mon|tue|wed|thu|fri|sat|sun|[0-7])$/i) {
69 $day = $rdowmap->{$day} if $day =~ m/\d/;
72 return undef if $noerr;
73 die "unable to parse day of week '$dowstr'\n";
80 my $vzdump_propetries = {
81 additionalProperties
=> 0,
82 properties
=> PVE
::VZDump
::json_config_properties
({}),
85 sub parse_vzdump_cron_config
{
86 my ($filename, $raw) = @_;
88 my $jobs = []; # correct jobs
90 my $ejobs = []; # mailfomerd lines
92 my $jid = 1; # we start at 1
94 my $digest = Digest
::SHA1
::sha1_hex
(defined($raw) ?
$raw : '');
96 while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
99 next if $line =~ m/^\#/;
100 next if $line =~ m/^\s*$/;
101 next if $line =~ m/^PATH\s*=/; # we always overwrite path
103 if ($line =~ m
|^(\d
+)\s
+(\d
+)\s
+\
*\s
+\
*\s
+(\S
+)\s
+root\s
+(/\S+/)?vzdump
(\s
+(.*))?
$|) {
105 my $minute = int($1);
110 my $dowhash = parse_dow
($dow, 1);
111 die "unable to parse day of week '$dow' in '$filename'\n" if !$dowhash;
113 my $args = [ split(/\s+/, $param)];
115 my $opts = PVE
::JSONSchema
::get_options
($vzdump_propetries, $args, undef, undef, 'vmid');
117 $opts->{id
} = "$digest:$jid";
119 $opts->{hour
} = $hour;
120 $opts->{minute
} = $minute;
121 $opts->{dow
} = &$dowhash_to_dow($dowhash);
127 syslog
('err', "parse error in '$filename': $err");
128 push @$ejobs, { line
=> $line };
130 } elsif ($line =~ m
|^\S
+\s
+(\S
+)\s
+\S
+\s
+\S
+\s
+\S
+\s
+\S
+\s
+(\S
.*)$|) {
131 syslog
('err', "warning: malformed line in '$filename'");
132 push @$ejobs, { line
=> $line };
134 syslog
('err', "ignoring malformed line in '$filename'");
139 $res->{digest
} = $digest;
140 $res->{jobs
} = $jobs;
141 $res->{ejobs
} = $ejobs;
146 sub write_vzdump_cron_config
{
147 my ($filename, $cfg) = @_;
149 my $out = "# cluster wide vzdump cron schedule\n";
150 $out .= "# Atomatically generated file - do not edit\n\n";
151 $out .= "PATH=\"/usr/sbin:/usr/bin:/sbin:/bin\"\n\n";
153 my $jobs = $cfg->{jobs
} || [];
154 foreach my $job (@$jobs) {
155 my $dh = parse_dow
($job->{dow
});
157 if ($dh->{mon
} && $dh->{tue
} && $dh->{wed
} && $dh->{thu
} &&
158 $dh->{fri
} && $dh->{sat
} && $dh->{sun
}) {
161 $dow = &$dowhash_to_dow($dh, 1);
166 foreach my $p (keys %$job) {
167 next if $p eq 'id' || $p eq 'vmid' || $p eq 'hour' ||
168 $p eq 'minute' || $p eq 'dow';
169 $param .= " --$p " . $job->{$p};
172 $param .= " $job->{vmid}" if $job->{vmid
};
174 $out .= sprintf "$job->{minute} $job->{hour} * * %-11s root vzdump$param\n", $dow;
177 my $ejobs = $cfg->{ejobs
} || [];
178 foreach my $job (@$ejobs) {
179 $out .= "$job->{line}\n" if $job->{line
};
185 __PACKAGE__-
>register_method({
189 description
=> "List vzdump backup schedule.",
191 additionalProperties
=> 0,
199 id
=> { type
=> 'string' },
202 links
=> [ { rel
=> 'child', href
=> "{id}" } ],
207 my $rpcenv = PVE
::RPCEnvironment
::get
();
208 my $user = $rpcenv->get_user();
210 my $data = cfs_read_file
('vzdump');
212 my $res = $data->{jobs
} || [];
217 __PACKAGE__-
>register_method({
218 name
=> 'create_job',
222 description
=> "Create new vzdump backup job.",
224 additionalProperties
=> 0,
225 properties
=> PVE
::VZDump
::json_config_properties
({
228 description
=> "Start time (hour).",
235 description
=> "Start time (minute).",
241 type
=> 'string', format
=> 'pve-day-of-week-list',
243 description
=> "Day of week selection.",
244 default => 'mon,tue,wed,thu,fri,sat,sun',
248 returns
=> { type
=> 'null' },
252 my $rpcenv = PVE
::RPCEnvironment
::get
();
253 my $user = $rpcenv->get_user();
255 my $data = cfs_read_file
('vzdump');
257 $param->{minute
} = 0 if !defined($param->{minute
});
258 $param->{dow
} = 'mon,tue,wed,thu,fri,sat,sun' if !defined($param->{dow
});
260 $param->{all
} = 1 if defined($param->{exclude
});
261 raise_param_exc
({ all
=> "option conflicts with option 'vmid'"})
262 if $param->{all
} && $param->{vmid
};
264 raise_param_exc
({ vmid
=> "property is missing"})
265 if !$param->{all
} && !$param->{vmid
};
267 push @{$data->{jobs
}}, $param;
269 cfs_write_file
('vzdump', $data);
274 __PACKAGE__-
>register_method({
278 description
=> "Read vzdump backup job definition.",
280 additionalProperties
=> 0,
284 description
=> "The job ID.",
295 my $rpcenv = PVE
::RPCEnvironment
::get
();
296 my $user = $rpcenv->get_user();
298 my $data = cfs_read_file
('vzdump');
300 my $jobs = $data->{jobs
} || [];
302 foreach my $job (@$jobs) {
303 return $job if $job->{id
} eq $param->{id
};
306 raise_param_exc
({ id
=> "No such job '$param->{id}'" });
310 __PACKAGE__-
>register_method({
311 name
=> 'delete_job',
314 description
=> "Delete vzdump backup job definition.",
317 additionalProperties
=> 0,
321 description
=> "The job ID.",
326 returns
=> { type
=> 'null' },
330 my $rpcenv = PVE
::RPCEnvironment
::get
();
331 my $user = $rpcenv->get_user();
333 my $data = cfs_read_file
('vzdump');
335 my $jobs = $data->{jobs
} || [];
339 foreach my $job (@$jobs) {
340 if ($job->{id
} eq $param->{id
}) {
343 push @$newjobs, $job;
347 raise_param_exc
({ id
=> "No such job '$param->{id}'" }) if !$found;
349 $data->{jobs
} = $newjobs;
351 cfs_write_file
('vzdump', $data);
356 __PACKAGE__-
>register_method({
357 name
=> 'update_job',
361 description
=> "Update vzdump backup job definition.",
363 additionalProperties
=> 0,
364 properties
=> PVE
::VZDump
::json_config_properties
({
367 description
=> "The job ID.",
373 description
=> "Start time (hour).",
380 description
=> "Start time (minute).",
385 type
=> 'string', format
=> 'pve-day-of-week-list',
387 description
=> "Day of week selection.",
391 returns
=> { type
=> 'null' },
395 my $rpcenv = PVE
::RPCEnvironment
::get
();
396 my $user = $rpcenv->get_user();
398 my $data = cfs_read_file
('vzdump');
400 my $jobs = $data->{jobs
} || [];
402 raise_param_exc
({ all
=> "option conflicts with option 'vmid'"})
403 if $param->{all
} && $param->{vmid
};
405 foreach my $job (@$jobs) {
406 if ($job->{id
} eq $param->{id
}) {
408 foreach my $k (keys %$param) {
409 $job->{$k} = $param->{$k};
412 $job->{all
} = 1 if defined($job->{exclude
});
414 if ($param->{vmid
}) {
416 delete $job->{exclude
};
417 } elsif ($param->{all
}) {
421 raise_param_exc
({ all
=> "option conflicts with option 'vmid'"})
422 if $job->{all
} && $job->{vmid
};
424 raise_param_exc
({ vmid
=> "property is missing"})
425 if !$job->{all
} && !$job->{vmid
};
427 cfs_write_file
('vzdump', $data);
433 raise_param_exc
({ id
=> "No such job '$param->{id}'" });