]> git.proxmox.com Git - pmg-api.git/blame - PMG/Backup.pm
PMG/Backup.pm - do not store system users secrets in backup
[pmg-api.git] / PMG / Backup.pm
CommitLineData
9a8d51a4
DM
1package PMG::Backup;
2
3use strict;
4use warnings;
5use Data::Dumper;
6use File::Basename;
7use File::Path;
8
9use PVE::Tools;
10
11use PMG::pmgcfg;
12use PMG::AtomicFile;
13
14sub dump_table {
15 my ($dbh, $table, $ofh, $seq, $seqcol) = @_;
16
17 my $sth = $dbh->column_info(undef, undef, $table, undef);
18
19 my $attrs = $sth->fetchall_arrayref({});
20
21 my @col_arr;
22 foreach my $ref (@$attrs) {
23 push @col_arr, $ref->{COLUMN_NAME};
24 }
25
26 $sth->finish();
27
28 my $cols = join (', ', @col_arr);
29 $cols || die "unable to fetch column definitions: ERROR";
30
31 print $ofh "COPY $table ($cols) FROM stdin;\n";
32
33 my $cmd = "COPY $table ($cols) TO STDOUT";
34 $dbh->do($cmd);
35
36 my $data = '';
37 while ($dbh->pg_getcopydata($data) >= 0) {
38 print $ofh $data;
39 }
40
41 print $ofh "\\.\n\n";
42
43 if ($seq && $seqcol) {
44 print $ofh "SELECT setval('$seq', max($seqcol)) FROM $table;\n\n";
45 }
46}
47
48sub dumpdb {
49 my ($ofh) = @_;
50
51 print $ofh "SET client_encoding = 'SQL_ASCII';\n";
52 print $ofh "SET check_function_bodies = false;\n\n";
53
54 my $dbh = PMG::DBTools::open_ruledb();
55
56 print $ofh "BEGIN TRANSACTION;\n\n";
57
58 eval {
59 $dbh->begin_work;
60
61 # read a consistent snapshot
62 $dbh->do("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
63
64 dump_table($dbh, 'attribut', $ofh);
65 dump_table($dbh, 'object', $ofh, 'object_id_seq', 'id');
66 dump_table($dbh, 'objectgroup', $ofh, 'objectgroup_id_seq', 'id');
67 dump_table($dbh, 'rule', $ofh, 'rule_id_seq', 'id');
68 dump_table($dbh, 'rulegroup', $ofh);
69 dump_table($dbh, 'userprefs', $ofh);
70
71 # we do not save the following tables: cgreylist, cmailstore, cmsreceivers, clusterinfo
72 };
73 my $err = $@;
74
75 $dbh->rollback(); # end read-only transaction
76
77 $dbh->disconnect();
78
79 die $err if $err;
80
81 print $ofh "COMMIT TRANSACTION;\n\n";
82}
83
84sub dumpstatdb {
85 my ($ofh) = @_;
86
87 print $ofh "SET client_encoding = 'SQL_ASCII';\n";
88 print $ofh "SET check_function_bodies = false;\n\n";
89
90 my $dbh = PMG::DBTools::open_ruledb();
91
92 eval {
93 $dbh->begin_work;
94
95 # read a consistent snapshot
96 $dbh->do("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
97
98 print $ofh "BEGIN TRANSACTION;\n\n";
99
100 dump_table($dbh, 'dailystat', $ofh);
101 dump_table($dbh, 'domainstat', $ofh);
102 dump_table($dbh, 'virusinfo', $ofh);
103 dump_table($dbh, 'localstat', $ofh);
104
105 # drop/create the index is a little bit faster (20%)
106
107 print $ofh "DROP INDEX cstatistic_time_index;\n\n";
108 print $ofh "ALTER TABLE cstatistic DROP CONSTRAINT cstatistic_id_key;\n\n";
109 print $ofh "ALTER TABLE cstatistic DROP CONSTRAINT cstatistic_pkey;\n\n";
110 dump_table($dbh, 'cstatistic', $ofh, 'cstatistic_id_seq', 'id');
111 print $ofh "ALTER TABLE ONLY cstatistic ADD CONSTRAINT cstatistic_pkey PRIMARY KEY (cid, rid);\n\n";
112 print $ofh "ALTER TABLE ONLY cstatistic ADD CONSTRAINT cstatistic_id_key UNIQUE (id);\n\n";
113 print $ofh "CREATE INDEX CStatistic_Time_Index ON CStatistic (Time);\n\n";
114
115 print $ofh "DROP INDEX CStatistic_ID_Index;\n\n";
116 dump_table($dbh, 'creceivers', $ofh);
117 print $ofh "CREATE INDEX CStatistic_ID_Index ON CReceivers (CStatistic_CID, CStatistic_RID);\n\n";
118
119 dump_table($dbh, 'statinfo', $ofh);
120
121 print $ofh "COMMIT TRANSACTION;\n\n";
122 };
123 my $err = $@;
124
125 $dbh->rollback(); # end read-only transaction
126
127 $dbh->disconnect();
128
129 die $err if $err;
130}
131
132sub pmg_backup {
133 my ($filename, $include_statistics) = @_;
134
135 my $time = time;
136 my $dirname = "/tmp/proxbackup_$$.$time";
137 my $dbfn = "Proxmox_ruledb.sql";
138 my $statfn = "Proxmox_statdb.sql";
139 my $tarfn = "config_backup.tar";
140 my $sigfn = "proxmox_backup_v1.md5";
141 my $verfn = "version.txt";
142
143 eval {
144
145 my $targetdir = dirname($filename);
146 mkdir $targetdir; # try to create target dir
147 -d $targetdir ||
fb9e46a0 148 die "unable to access target directory '$targetdir'\n";
9a8d51a4
DM
149
150 # create a temporary directory
151 mkdir $dirname;
152
153 # dump the database first
154 my $fh = PMG::AtomicFile->open("$dirname/$dbfn", "w") ||
155 die "cant open '$dirname/$dbfn' - $! :ERROR";
156
157 dumpdb($fh);
158
159 $fh->close(1);
160
161 if ($include_statistics) {
162 # dump the statistic db
163 my $sfh = PMG::AtomicFile->open("$dirname/$statfn", "w") ||
164 die "cant open '$dirname/$statfn' - $! :ERROR";
165
166 dumpstatdb($sfh);
167
168 $sfh->close(1);
169 }
170
171 my $pkg = PMG::pmgcfg::package();
172 my $ver = PMG::pmgcfg::version();
173
174 my $vfh = PMG::AtomicFile->open ("$dirname/$verfn", "w") ||
175 die "cant open '$dirname/$verfn' - $! :ERROR";
176
177 $time = time;
178 my $now = localtime;
179 print $vfh "product: $pkg\nversion: $ver\nbackuptime:$time:$now\n";
180 $vfh->close(1);
181
bd1325ed 182 my $extra_cfgs = [];
9a8d51a4 183
bd1325ed 184 push @$extra_cfgs, '/etc/mail/spamassassin/custom.cf';
9a8d51a4 185
bd1325ed 186 #push @$extra_cfgs, '/etc/postfix/tls_policy';
9a8d51a4
DM
187
188 my $extradb = $include_statistics ? $statfn : '';
189
bd1325ed 190 my $extra = join(' ', @$extra_cfgs);
9a8d51a4
DM
191
192 system("/bin/tar cf $dirname/$tarfn -C / " .
bd1325ed 193 "/etc/pmg $extra>/dev/null 2>&1") == 0 ||
9a8d51a4
DM
194 die "unable to create system configuration backup: ERROR";
195
196 system("cd $dirname; md5sum $tarfn $dbfn $extradb $verfn> $sigfn") == 0 ||
197 die "unable to create backup signature: ERROR";
198
199 system("rm -f $filename; tar czf $filename -C $dirname $verfn $sigfn $dbfn $extradb $tarfn") == 0 ||
200 die "unable to create backup archive: ERROR";
201 };
202 my $err = $@;
203
204 rmtree $dirname;
205
206 if ($err) {
207 unlink $filename;
208 die $err;
209 }
210}
211
a6d276e9
DM
212sub pmg_restore {
213 my ($filename, $restore_database, $restore_config, $restore_statistics) = @_;
214
215 my $dbname = 'Proxmox_ruledb';
216
217 my $time = time;
218 my $dirname = "/tmp/proxrestore_$$.$time";
219 my $dbfn = "Proxmox_ruledb.sql";
220 my $statfn = "Proxmox_statdb.sql";
221 my $tarfn = "config_backup.tar";
222 my $sigfn = "proxmox_backup_v1.md5";
223
224 eval {
225 # create a temporary directory
226 mkdir $dirname;
227
228 system("cd $dirname; tar xzf $filename >/dev/null 2>&1") == 0 ||
229 die "unable to extract backup archive: ERROR";
230
231 system("cd $dirname; md5sum -c $sigfn") == 0 ||
232 die "proxmox backup signature check failed: ERROR";
233
234 if ($restore_config) {
235 # restore the tar file
236 mkdir "$dirname/config/";
237 system("tar xpf $dirname/$tarfn -C $dirname/config/") == 0 ||
238 die "unable to restore configuration tar archive: ERROR";
239
240 -d "$dirname/config/etc/pmg" ||
241 die "backup does not contain a valid system configuration directory (/etc/pmg)\n";
242 # unlink unneeded files
243 unlink "$dirname/config/etc/pmg/cluster.conf"; # never restore cluster config
244 rmtree "$dirname/config/etc/pmg/master";
245
246 # backup old config to /etc/pmg.oldremove current config
247 rmtree "/etc/pmg";
248 mkdir "/etc/pmg";
249 # copy files
250 system("cp -a $dirname/config/etc/pmg/* /etc/pmg/") == 0 ||
251 die "unable to restore system configuration: ERROR";
252
253 my $cfg = PMG::Config->new();
254 my $ruledb = PMG::RuleDB->new();
255 my $rulecache = PMG::RuleCache->new($ruledb);
256 $cfg->rewrite_config($rulecache, 1);
257 }
258
259 if ($restore_database) {
260 # recreate the database
261
262 # stop all services accessing the database
263 PMG::Utils::service_wait_stopped(40, $PMG::Utils::db_service_list);
264
265 print "Destroy existing rule database\n";
266 PMG::DBTools::delete_ruledb($dbname);
267
268 print "Create new database\n";
269 my $dbh = PMG::DBTools::create_ruledb($dbname);
270 my $ruledb = PMG::RuleDB->new($dbh);
271 PMG::DBTools::init_ruledb($ruledb);
272
273 system("cat $dirname/$dbfn|psql $dbname >/dev/null 2>&1") == 0 ||
274 die "unable to restore rule database: ERROR";
275
276 if ($restore_statistics) {
277 if (-f "$dirname/$statfn") {
278 system("cat $dirname/$statfn|psql $dbname >/dev/null 2>&1") == 0 ||
279 die "unable to restore statistic database: ERROR";
280 }
281 }
282
283 print STDERR "run analyze to speed up database queries\n";
284 PMG::DBTools::postgres_admin_cmd('psql', { input => 'analyze;' }, $dbname);
285
286 print "Analyzing/Upgrading existing Databases...";
287 PMG::DBTools::upgradedb($ruledb);
288 print "done\n";
289
290 # cleanup old spam/virus storage
291 PMG::MailQueue::create_spooldirs(0, 1);
292
293 my $cfg = PMG::Config->new();
294 my $rulecache = PMG::RuleCache->new($ruledb);
295 $cfg->rewrite_config($rulecache, 1);
296
297 # and restart services as soon as possible
298 foreach my $service (reverse @$PMG::Utils::db_service_list) {
299 eval { PVE::Tools::run_command(['systemctl', 'start', $service]); };
300 warn $@ if $@;
301 }
302 }
303 };
304 my $err = $@;
305
306 rmtree $dirname;
307
308 die $err if $err;
309}
310
9a8d51a4 3111;