]>
git.proxmox.com Git - pmg-api.git/blob - src/PMG/Backup.pm
8 use POSIX
qw(strftime);
10 use PVE
::JSONSchema
qw(get_standard_option);
15 use PMG
::Utils
qw(postgres_admin_cmd);
18 "/etc/mail/spamassassin/custom.cf",
19 "/etc/mail/spamassassin/pmg-scores.cf",
22 sub get_restore_options
{
24 node
=> get_standard_option
('pve-node'),
26 description
=> "Restore system configuration.",
32 description
=> "Restore the rule database. This is the default.",
38 description
=> "Restore statistic databases. Only considered when you restore the 'database'.",
46 my ($dbh, $table, $ofh, $seq, $seqcol) = @_;
48 my $sth = $dbh->column_info(undef, undef, $table, undef);
50 my $attrs = $sth->fetchall_arrayref({});
53 foreach my $ref (@$attrs) {
54 push @col_arr, $ref->{COLUMN_NAME
};
59 my $cols = join (', ', @col_arr);
60 $cols || die "unable to fetch column definitions: ERROR";
62 print $ofh "COPY $table ($cols) FROM stdin;\n";
64 my $cmd = "COPY $table ($cols) TO STDOUT";
68 while ($dbh->pg_getcopydata($data) >= 0) {
74 if ($seq && $seqcol) {
75 print $ofh "SELECT setval('$seq', max($seqcol)) FROM $table;\n\n";
82 print $ofh "SET client_encoding = 'SQL_ASCII';\n";
83 print $ofh "SET check_function_bodies = false;\n\n";
85 my $dbh = PMG
::DBTools
::open_ruledb
();
87 print $ofh "BEGIN TRANSACTION;\n\n";
92 # read a consistent snapshot
93 $dbh->do("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
95 dump_table
($dbh, 'attribut', $ofh);
96 dump_table
($dbh, 'object', $ofh, 'object_id_seq', 'id');
97 dump_table
($dbh, 'objectgroup', $ofh, 'objectgroup_id_seq', 'id');
98 dump_table
($dbh, 'rule', $ofh, 'rule_id_seq', 'id');
99 dump_table
($dbh, 'rulegroup', $ofh);
100 dump_table
($dbh, 'userprefs', $ofh);
102 # we do not save the following tables: cgreylist, cmailstore, cmsreceivers, clusterinfo
106 $dbh->rollback(); # end read-only transaction
112 print $ofh "COMMIT TRANSACTION;\n\n";
118 print $ofh "SET client_encoding = 'SQL_ASCII';\n";
119 print $ofh "SET check_function_bodies = false;\n\n";
121 my $dbh = PMG
::DBTools
::open_ruledb
();
126 # read a consistent snapshot
127 $dbh->do("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
129 print $ofh "BEGIN TRANSACTION;\n\n";
131 dump_table
($dbh, 'dailystat', $ofh);
132 dump_table
($dbh, 'domainstat', $ofh);
133 dump_table
($dbh, 'virusinfo', $ofh);
134 dump_table
($dbh, 'localstat', $ofh);
136 # drop/create the index is a little bit faster (20%)
138 print $ofh "DROP INDEX cstatistic_time_index;\n\n";
139 print $ofh "ALTER TABLE cstatistic DROP CONSTRAINT cstatistic_id_key;\n\n";
140 print $ofh "ALTER TABLE cstatistic DROP CONSTRAINT cstatistic_pkey;\n\n";
141 dump_table
($dbh, 'cstatistic', $ofh, 'cstatistic_id_seq', 'id');
142 print $ofh "ALTER TABLE ONLY cstatistic ADD CONSTRAINT cstatistic_pkey PRIMARY KEY (cid, rid);\n\n";
143 print $ofh "ALTER TABLE ONLY cstatistic ADD CONSTRAINT cstatistic_id_key UNIQUE (id);\n\n";
144 print $ofh "CREATE INDEX CStatistic_Time_Index ON CStatistic (Time);\n\n";
146 print $ofh "DROP INDEX CStatistic_ID_Index;\n\n";
147 dump_table
($dbh, 'creceivers', $ofh);
148 print $ofh "CREATE INDEX CStatistic_ID_Index ON CReceivers (CStatistic_CID, CStatistic_RID);\n\n";
150 dump_table
($dbh, 'statinfo', $ofh);
152 print $ofh "COMMIT TRANSACTION;\n\n";
156 $dbh->rollback(); # end read-only transaction
163 # this function assumes that directory $dirname exists and is empty
165 my ($dirname, $include_statistics) = @_;
167 die "No backupdir provided!\n" if !defined($dirname);
170 my $dbfn = "Proxmox_ruledb.sql";
171 my $statfn = "Proxmox_statdb.sql";
172 my $tarfn = "config_backup.tar";
173 my $sigfn = "proxmox_backup_v1.md5";
174 my $verfn = "version.txt";
178 # dump the database first
179 my $fh = PMG
::AtomicFile-
>open("$dirname/$dbfn", "w") ||
180 die "can't open '$dirname/$dbfn' - $! :ERROR";
186 if ($include_statistics) {
187 # dump the statistic db
188 my $sfh = PMG
::AtomicFile-
>open("$dirname/$statfn", "w") ||
189 die "can't open '$dirname/$statfn' - $! :ERROR";
196 my $pkg = PMG
::pmgcfg
::package();
197 my $release = PMG
::pmgcfg
::release
();
199 my $vfh = PMG
::AtomicFile-
>open ("$dirname/$verfn", "w") ||
200 die "can't open '$dirname/$verfn' - $! :ERROR";
204 print $vfh "product: $pkg\nversion: $release\nbackuptime:$time:$now\n";
209 push @$extra_cfgs, @{$sa_configs};
211 my $extradb = $include_statistics ?
$statfn : '';
213 my $extra = join(' ', @$extra_cfgs);
215 system("/bin/tar cf $dirname/$tarfn -C / " .
216 "/etc/pmg $extra>/dev/null 2>&1") == 0 ||
217 die "unable to create system configuration backup: ERROR";
219 system("cd $dirname; md5sum $tarfn $dbfn $extradb $verfn> $sigfn") == 0 ||
220 die "unable to create backup signature: ERROR";
230 sub pmg_backup_pack
{
231 my ($filename, $include_statistics) = @_;
234 my $dirname = "/tmp/proxbackup_$$.$time";
238 my $targetdir = dirname
($filename);
239 mkdir $targetdir; # try to create target dir
241 die "unable to access target directory '$targetdir'\n";
244 # create backup directory
247 pmg_backup
($dirname, $include_statistics);
249 system("rm -f $filename; tar czf $filename --strip-components=1 -C $dirname .") == 0 ||
250 die "unable to create backup archive: ERROR\n";
263 my ($filename, $restore_database, $restore_config, $restore_statistics) = @_;
265 my $dbname = 'Proxmox_ruledb';
268 my $dirname = "/tmp/proxrestore_$$.$time";
269 my $dbfn = "Proxmox_ruledb.sql";
270 my $statfn = "Proxmox_statdb.sql";
271 my $tarfn = "config_backup.tar";
272 my $sigfn = "proxmox_backup_v1.md5";
276 # directory indicates that the files were restored from a PBS remote
277 if ( -d
$filename ) {
278 $dirname = $filename;
285 # remove any leftovers
287 # create a temporary directory
290 system("cd $dirname; tar xzf $filename >/dev/null 2>&1") == 0 ||
291 die "unable to extract backup archive: ERROR";
294 system("cd $dirname; md5sum -c $sigfn") == 0 ||
295 die "proxmox backup signature check failed: ERROR";
297 if ($restore_config) {
298 # restore the tar file
299 mkdir "$dirname/config/";
300 system("tar xpf $dirname/$tarfn -C $dirname/config/") == 0 ||
301 die "unable to restore configuration tar archive: ERROR";
303 -d
"$dirname/config/etc/pmg" ||
304 die "backup does not contain a valid system configuration directory (/etc/pmg)\n";
305 # unlink unneeded files
306 unlink "$dirname/config/etc/pmg/cluster.conf"; # never restore cluster config
307 rmtree
"$dirname/config/etc/pmg/master";
309 # remove current config, but keep directory for INotify
310 rmtree
("/etc/pmg", { keep_root
=> 1 });
312 system("cp -a $dirname/config/etc/pmg/* /etc/pmg/") == 0 ||
313 die "unable to restore system configuration: ERROR";
315 for my $sa_cfg (@{$sa_configs}) {
316 if (-f
"$dirname/config/${sa_cfg}") {
317 my $data = PVE
::Tools
::file_get_contents
(
318 "$dirname/config/${sa_cfg}", 1024*1024);
319 PVE
::Tools
::file_set_contents
($sa_cfg, $data);
323 my $cfg = PMG
::Config-
>new();
324 my $ruledb = PMG
::RuleDB-
>new();
325 my $rulecache = PMG
::RuleCache-
>new($ruledb);
326 $cfg->rewrite_config($rulecache, 1);
329 if ($restore_database) {
330 # recreate the database
332 # stop all services accessing the database
333 PMG
::Utils
::service_wait_stopped
(40, $PMG::Utils
::db_service_list
);
335 print "Destroy existing rule database\n";
336 PMG
::DBTools
::delete_ruledb
($dbname);
338 print "Create new database\n";
339 my $dbh = PMG
::DBTools
::create_ruledb
($dbname);
341 system("cat $dirname/$dbfn|psql $dbname >/dev/null 2>&1") == 0 ||
342 die "unable to restore rule database: ERROR";
344 if ($restore_statistics) {
345 if (-f
"$dirname/$statfn") {
346 system("cat $dirname/$statfn|psql $dbname >/dev/null 2>&1") == 0 ||
347 die "unable to restore statistic database: ERROR";
351 print STDERR
"run analyze to speed up database queries\n";
352 postgres_admin_cmd
('psql', { input
=> 'analyze;' }, $dbname);
354 print "Analyzing/Upgrading existing Databases...";
355 my $ruledb = PMG
::RuleDB-
>new($dbh);
356 PMG
::DBTools
::upgradedb
($ruledb);
359 # cleanup old spam/virus storage
360 PMG
::MailQueue
::create_spooldirs
(0, 1);
362 my $cfg = PMG
::Config-
>new();
363 my $rulecache = PMG
::RuleCache-
>new($ruledb);
364 $cfg->rewrite_config($rulecache, 1);
366 # and restart services as soon as possible
367 foreach my $service (reverse @$PMG::Utils
::db_service_list
) {
368 eval { PVE
::Tools
::run_command
(['systemctl', 'start', $service]); };
380 sub send_backup_notification
{
381 my ($notify_on, $target, $log, $err) = @_;
383 return if !$notify_on;
384 return if $notify_on eq 'never';
385 return if $notify_on eq 'error' && !$err;
387 my $cfg = PMG
::Config-
>new();
388 my $email = $cfg->get ('admin', 'email');
390 warn "not sending notification: no admin email configured\n";
394 my $nodename = PVE
::INotify
::nodename
();
395 my $fqdn = PVE
::Tools
::get_fqdn
($nodename);
399 hostname
=> $nodename,
401 date
=> strftime
("%F", localtime()),
407 my $tt = PMG
::Config
::get_template_toolkit
();
409 my $mailfrom = "Proxmox Mail Gateway <postmaster>";
410 PMG
::Utils
::finalize_report
($tt, 'backup-notification.tt', $vars, $mailfrom, $email);