my $maxfiles = delete $param->{maxfiles};
my $prune_backups = $param->{'prune-backups'};
- warn "both 'maxfiles' and 'prune-backups' defined as ${kind} - ignoring 'maxfiles'\n"
+ debugmsg('warn', "both 'maxfiles' and 'prune-backups' defined as ${kind} - ignoring 'maxfiles'")
if defined($maxfiles) && defined($prune_backups);
if (defined($prune_backups)) {
+ return if ref($prune_backups) eq 'HASH'; # already parsed
$param->{'prune-backups'} = PVE::JSONSchema::parse_property_string(
'prune-backups',
$prune_backups
die "can't use storage '$storage' for backups - wrong content type\n"
if (!$scfg->{content}->{backup});
- PVE::Storage::activate_storage($cfg, $storage);
-
my $info = {
scfg => $scfg,
};
defined($default) ? ($_ => $default) : ()
} keys %$confdesc
};
+ $parse_prune_backups_maxfiles->($defaults, "defaults in VZDump schema");
my $raw;
eval { $raw = PVE::Tools::file_get_contents($fn); };
my @mailto = split_list($res->{mailto});
$res->{mailto} = [ @mailto ];
}
+ $parse_prune_backups_maxfiles->($res, "options in '$fn'");
foreach my $key (keys %$defaults) {
$res->{$key} = $defaults->{$key} if !defined($res->{$key});
}
+ if (defined($res->{storage}) && defined($res->{dumpdir})) {
+ debugmsg('warn', "both 'storage' and 'dumpdir' defined in '$fn' - ignoring 'dumpdir'");
+ delete $res->{dumpdir};
+ }
+
return $res;
}
$text_log_part .= "$vmid: no log available\n\n";
next;
}
- if (open (TMP, "$log")) {
- while (my $line = <TMP>) {
+ if (open (my $TMP, '<', "$log")) {
+ while (my $line = <$TMP>) {
next if $line =~ /^status: \d+/; # not useful in mails
$text_log_part .= encode8bit ("$vmid: $line");
}
+ close ($TMP);
} else {
$text_log_part .= "$vmid: Could not open log file\n\n";
}
- close (TMP);
$text_log_part .= "\n";
}
$text_log_part .= $detail_post if defined($detail_post);
$html .= "<tr><td>VMID<td>NAME<td>STATUS<td>TIME<td>SIZE<td>FILENAME</tr>\n";
my $ssize = 0;
-
foreach my $task (@$tasklist) {
my $vmid = $task->{vmid};
my $name = $task->{hostname};
if ($task->{state} eq 'ok') {
-
$ssize += $task->{size};
- $html .= sprintf ("<tr><td>%s<td>%s<td>OK<td>%s<td align=right>%s<td>%s</tr>\n",
- $vmid, $name,
- format_time($task->{backuptime}),
- format_size ($task->{size}),
- escape_html ($task->{target}));
+ $html .= sprintf (
+ "<tr><td>%s<td>%s<td>OK<td>%s<td align=right>%s<td>%s</tr>\n",
+ $vmid,
+ $name,
+ format_time($task->{backuptime}),
+ format_size ($task->{size}),
+ escape_html ($task->{target}),
+ );
} else {
- $html .= sprintf ("<tr><td>%s<td>%s<td><font color=red>FAILED<td>%s<td colspan=2>%s</tr>\n",
- $vmid, $name, format_time($task->{backuptime}),
- escape_html ($task->{msg}));
+ $html .= sprintf (
+ "<tr><td>%s<td>%s<td><font color=red>FAILED<td>%s<td colspan=2>%s</tr>\n",
+ $vmid,
+ $name,
+ format_time($task->{backuptime}),
+ escape_html ($task->{msg}),
+ );
}
}
$html_log_part .= "$vmid: no log available\n\n";
next;
}
- if (open (TMP, "$log")) {
- while (my $line = <TMP>) {
+ if (open (my $TMP, '<', "$log")) {
+ while (my $line = <$TMP>) {
next if $line =~ /^status: \d+/; # not useful in mails
if ($line =~ m/^\S+\s\d+\s+\d+:\d+:\d+\s+(ERROR|WARN):/) {
$html_log_part .= encode8bit ("$vmid: <font color=red>".
$html_log_part .= encode8bit ("$vmid: " . escape_html ($line));
}
}
+ close ($TMP);
} else {
$html_log_part .= "$vmid: Could not open log file\n\n";
}
- close (TMP);
$html_log_part .= "\n";
}
$html_log_part .= escape_html($detail_post) if defined($detail_post);
$html_log_part .= "</pre>";
- my $html_end .= "\n</body></html>\n";
+ my $html_end = "\n</body></html>\n";
# end html part
if (length($text) + length($text_log_part) +
- length($html) + length($html_log_part) < MAX_MAIL_SIZE)
+ length($html) + length($html_log_part) +
+ length($html_end) < MAX_MAIL_SIZE)
{
$html .= $html_log_part;
+ $html .= $html_end;
$text .= $text_log_part;
} else {
my $msg = "Log output was too long to be sent by mail. ".
my $defaults = read_vzdump_defaults();
- $opts->{remove} = 1 if !defined($opts->{remove});
-
foreach my $k (keys %$defaults) {
- next if $k eq 'exclude-path' || $k eq 'maxfiles'; # dealt with separately
+ next if $k eq 'exclude-path' || $k eq 'prune-backups'; # dealt with separately
if ($k eq 'dumpdir' || $k eq 'storage') {
$opts->{$k} = $defaults->{$k} if !defined ($opts->{dumpdir}) &&
!defined ($opts->{storage});
$opts->{tmpdir} =~ s|/+$|| if ($opts->{tmpdir});
$skiplist = [] if !$skiplist;
- my $self = bless { cmdline => $cmdline, opts => $opts, skiplist => $skiplist };
+ my $self = bless {
+ cmdline => $cmdline,
+ opts => $opts,
+ skiplist => $skiplist,
+ }, $class;
my $findexcl = $self->{findexcl} = [];
if ($defaults->{'exclude-path'}) {
}
if ($opts->{stdexcludes}) {
- push @$findexcl, '/tmp/?*',
- '/var/tmp/?*',
- '/var/run/?*.pid';
+ push @$findexcl,
+ '/tmp/?*',
+ '/var/tmp/?*',
+ '/var/run/?*.pid',
+ ;
}
foreach my $p (@plugins) {
-
- my $pd = $p->new ($self);
+ my $pd = $p->new($self);
push @{$self->{plugins}}, $pd;
}
my $errors = '';
if ($opts->{storage}) {
+ my $storage_cfg = PVE::Storage::config();
+ eval { PVE::Storage::activate_storage($storage_cfg, $opts->{storage}) };
+ if (my $err = $@) {
+ chomp($err);
+ $errors .= "could not activate storage '$opts->{storage}': $err";
+ }
+
my $info = eval { storage_info ($opts->{storage}) };
if (my $err = $@) {
+ chomp($err);
$errors .= "could not get storage information for '$opts->{storage}': $err";
} else {
$opts->{dumpdir} = $info->{dumpdir};
$opts->{scfg} = $info->{scfg};
$opts->{pbs} = $info->{pbs};
-
- if (!defined($opts->{'prune-backups'}) && !defined($opts->{maxfiles})) {
- $opts->{'prune-backups'} = $info->{'prune-backups'};
- $opts->{maxfiles} = $info->{maxfiles};
- }
+ $opts->{'prune-backups'} //= $info->{'prune-backups'};
}
} elsif ($opts->{dumpdir}) {
$errors .= "dumpdir '$opts->{dumpdir}' does not exist"
die "internal error";
}
- if (!defined($opts->{'prune-backups'})) {
- my $maxfiles = delete $opts->{maxfiles} // $defaults->{maxfiles};
- $maxfiles = int($maxfiles); # shouldn't be necessary, but be safe
- if ($maxfiles) {
- $opts->{'prune-backups'} = { 'keep-last' => $maxfiles };
- } else {
- $opts->{'prune-backups'} = { 'keep-all' => 1 };
- }
- }
+ $opts->{'prune-backups'} //= $defaults->{'prune-backups'};
# avoid triggering any remove code path if keep-all is set
$opts->{remove} = 0 if $opts->{'prune-backups'}->{'keep-all'};
die "missing UPID" if !$upid; # should not happen
- if (!open (SERVER_FLCK, ">>$lockfile")) {
+ my $SERVER_FLCK;
+ if (!open ($SERVER_FLCK, '>>', "$lockfile")) {
debugmsg ('err', "can't open lock on file '$lockfile' - $!", undef, 1);
die "can't open lock on file '$lockfile' - $!";
}
- if (!flock (SERVER_FLCK, LOCK_EX|LOCK_NB)) {
-
+ if (!flock ($SERVER_FLCK, LOCK_EX|LOCK_NB)) {
if (!$maxwait) {
debugmsg ('err', "can't acquire lock '$lockfile' (wait = 0)", undef, 1);
die "can't acquire lock '$lockfile' (wait = 0)";
}
debugmsg('info', "trying to get global lock - waiting...", undef, 1);
-
eval {
alarm ($maxwait * 60);
local $SIG{ALRM} = sub { alarm (0); die "got timeout\n"; };
- if (!flock (SERVER_FLCK, LOCK_EX)) {
+ if (!flock ($SERVER_FLCK, LOCK_EX)) {
my $err = $!;
- close (SERVER_FLCK);
+ close ($SERVER_FLCK);
alarm (0);
die "$err\n";
}
}
PVE::Tools::file_set_contents($pidfile, $upid);
+
+ return $SERVER_FLCK;
}
sub run_hook_script {
die "The hook script '$script' is not executable.\n";
}
- my $cmd = "$script $phase";
+ my $cmd = [$script, $phase];
- $cmd .= " $task->{mode} $task->{vmid}" if ($task);
+ if ($task) {
+ push @$cmd, $task->{mode};
+ push @$cmd, $task->{vmid};
+ }
local %ENV;
# set immutable opts directly (so they are available in all phases)
if ($task->{tmplog}) {
if ($self->{opts}->{pbs}) {
if ($task->{state} eq 'ok') {
- my $param = [$pbs_snapshot_name, $task->{tmplog}];
- PVE::Storage::PBSPlugin::run_raw_client_cmd(
- $opts->{scfg}, $opts->{storage}, 'upload-log', $param, errmsg => "upload log failed");
+ eval {
+ PVE::Storage::PBSPlugin::run_raw_client_cmd(
+ $opts->{scfg},
+ $opts->{storage},
+ 'upload-log',
+ [ $pbs_snapshot_name, $task->{tmplog} ],
+ errmsg => "uploading backup task log failed",
+ );
+ };
+ debugmsg('warn', "$@") if $@; # $@ contains already error prefix
}
} elsif ($task->{logfile}) {
system {'cp'} 'cp', $task->{tmplog}, $task->{logfile};
return defined($confdesc->{$key});
}
+# NOTE it might make sense to merge this and verify_vzdump_parameters(), but one
+# needs to adapt command_line() in guest-common's PVE/VZDump/Common.pm and detect
+# a second parsing attempt, because verify_vzdump_parameters() is called twice
+# during the update_job API call.
+sub parse_mailto_exclude_path {
+ my ($param) = @_;
+
+ # exclude-path list need to be 0 separated
+ if (defined($param->{'exclude-path'})) {
+ my @expaths = split(/\0/, $param->{'exclude-path'} || '');
+ $param->{'exclude-path'} = [ @expaths ];
+ }
+
+ if (defined($param->{mailto})) {
+ my @mailto = PVE::Tools::split_list(extract_param($param, 'mailto'));
+ $param->{mailto} = [ @mailto ];
+ }
+
+ return;
+}
+
sub verify_vzdump_parameters {
my ($param, $check_missing) = @_;
$param->{all} = 1 if (defined($param->{exclude}) && !$param->{pool});
- warn "option 'size' is deprecated and will be removed in a future " .
- "release, please update your script/configuration!\n"
- if defined($param->{size});
-
return if !$check_missing;
raise_param_exc({ vmid => "property is missing"})