]> git.proxmox.com Git - pmg-api.git/blame - PMG/DBTools.pm
PMG::DBTools::cluster_sync_status - new helper
[pmg-api.git] / PMG / DBTools.pm
CommitLineData
a355f100
DM
1package PMG::DBTools;
2
3use strict;
4use warnings;
5
6use POSIX ":sys_wait_h";
7use POSIX ':signal_h';
8use DBI;
9
f3464b71 10use PVE::SafeSyslog;
a355f100
DM
11use PVE::Tools;
12
f3464b71 13use PMG::Utils;
0a580593
DM
14use PMG::RuleDB;
15
987ee804
DM
16our $default_db_name = "Proxmox_ruledb";
17
a355f100
DM
18sub open_ruledb {
19 my ($database, $host, $port) = @_;
20
21 $port = 5432 if !$port;
22
987ee804 23 $database = $default_db_name if !$database;
a355f100
DM
24
25 if ($host) {
26
27 my $dsn = "dbi:Pg:dbname=$database;host=$host;port=$port;";
28
29 my $timeout = 5;
30 # only low level alarm interface works for DBI->connect
31 my $mask = POSIX::SigSet->new(SIGALRM);
32 my $action = POSIX::SigAction->new(sub { die "connect timeout\n" }, $mask);
33 my $oldaction = POSIX::SigAction->new();
34 sigaction(SIGALRM, $action, $oldaction);
35
36 my $rdb;
37
38 eval {
39 alarm($timeout);
40 $rdb = DBI->connect($dsn, "postgres", undef,
41 { PrintError => 0, RaiseError => 1 });
42 alarm(0);
43 };
44 alarm(0);
45 sigaction(SIGALRM, $oldaction); # restore original handler
f1bf78ff 46
a355f100
DM
47 die $@ if $@;
48
49 return $rdb;
50 } else {
51 my $dsn = "DBI:Pg:dbname=$database";
52
f1bf78ff 53 my $dbh = DBI->connect($dsn, "postgres", undef,
a355f100
DM
54 { PrintError => 0, RaiseError => 1 });
55
56 return $dbh;
57 }
58}
59
60sub delete_ruledb {
61 my ($dbname) = @_;
62
63 PVE::Tools::run_command(['dropdb', '-U', 'postgres', $dbname]);
64}
65
f1bf78ff
DM
66sub database_list {
67
68 my $database_list = {};
69
70 my $parser = sub {
71 my $line = shift;
72
73 my ($name, $owner) = map { PVE::Tools::trim($_) } split(/\|/, $line);
74 return if !$name || !$owner;
75
76 $database_list->{$name} = { owner => $owner };
77 };
78
79 my $cmd = ['psql', '-U', 'postgres', '--list', '--quiet', '--tuples-only'];
80
81 PVE::Tools::run_command($cmd, outfunc => $parser);
82
83 return $database_list;
84}
85
86my $dbfunction_maxint = <<__EOD;
87 CREATE OR REPLACE FUNCTION maxint (INTEGER, INTEGER) RETURNS INTEGER AS
88 'BEGIN IF \$1 > \$2 THEN RETURN \$1; ELSE RETURN \$2; END IF; END;' LANGUAGE plpgsql;
89__EOD
90
91my $dbfunction_minint = <<__EOD;
92 CREATE OR REPLACE FUNCTION minint (INTEGER, INTEGER) RETURNS INTEGER AS
93 'BEGIN IF \$1 < \$2 THEN RETURN \$1; ELSE RETURN \$2; END IF; END;' LANGUAGE plpgsql;
94__EOD
95
96# merge function to avoid update/insert race condition
97# see: http://www.postgresql.org/docs/9.1/static/plpgsql-control-structures.html#PLPGSQL-ERROR-TRAPPING
98my $dbfunction_merge_greylist = <<__EOD;
99 CREATE OR REPLACE FUNCTION merge_greylist (in_ipnet VARCHAR, in_host INTEGER, in_sender VARCHAR,
100 in_receiver VARCHAR, in_instance VARCHAR,
101 in_rctime INTEGER, in_extime INTEGER, in_delay INTEGER,
102 in_blocked INTEGER, in_passed INTEGER, in_mtime INTEGER,
103 in_cid INTEGER) RETURNS INTEGER AS
104 'BEGIN
105 LOOP
106 UPDATE CGreylist SET Host = CASE WHEN MTime >= in_mtime THEN Host ELSE in_host END,
107 CID = maxint (CID, in_cid), RCTime = minint (rctime, in_rctime),
108 ExTime = maxint (extime, in_extime),
109 Delay = maxint (delay, in_delay),
110 Blocked = maxint (blocked, in_blocked),
111 Passed = maxint (passed, in_passed)
112 WHERE IPNet = in_ipnet AND Sender = in_sender AND Receiver = in_receiver;
113
114 IF found THEN
115 RETURN 0;
116 END IF;
117
118 BEGIN
119 INSERT INTO CGREYLIST (IPNet, Host, Sender, Receiver, Instance, RCTime, ExTime, Delay, Blocked, Passed, MTime, CID)
120 VALUES (in_ipnet, in_host, in_sender, in_receiver, in_instance, in_rctime, in_extime,
121 in_delay, in_blocked, in_passed, in_mtime, in_cid);
122 RETURN 1;
123 EXCEPTION WHEN unique_violation THEN
124 -- do nothing - continue loop
125 END;
126 END LOOP;
127 END;' LANGUAGE plpgsql;
128__EOD
129
130my $cgreylist_ctablecmd = <<__EOD;
131 CREATE TABLE CGreylist
132 (IPNet VARCHAR(16) NOT NULL,
133 Host INTEGER NOT NULL,
134 Sender VARCHAR(255) NOT NULL,
135 Receiver VARCHAR(255) NOT NULL,
136 Instance VARCHAR(255),
137 RCTime INTEGER NOT NULL,
138 ExTime INTEGER NOT NULL,
139 Delay INTEGER NOT NULL DEFAULT 0,
140 Blocked INTEGER NOT NULL,
141 Passed INTEGER NOT NULL,
142 CID INTEGER NOT NULL,
143 MTime INTEGER NOT NULL,
144 PRIMARY KEY (IPNet, Sender, Receiver));
145
146 CREATE INDEX CGreylist_Instance_Sender_Index ON CGreylist (Instance, Sender);
147
148 CREATE INDEX CGreylist_ExTime_Index ON CGreylist (ExTime);
149
150 CREATE INDEX CGreylist_MTime_Index ON CGreylist (MTime);
151__EOD
152
153my $clusterinfo_ctablecmd = <<__EOD;
154 CREATE TABLE ClusterInfo
155 (CID INTEGER NOT NULL,
156 Name VARCHAR NOT NULL,
157 IValue INTEGER,
158 SValue VARCHAR,
159 PRIMARY KEY (CID, Name))
160__EOD
161
162my $daily_stat_ctablecmd = <<__EOD;
163 CREATE TABLE DailyStat
164 (Time INTEGER NOT NULL UNIQUE,
165 CountIn INTEGER NOT NULL,
166 CountOut INTEGER NOT NULL,
167 BytesIn REAL NOT NULL,
168 BytesOut REAL NOT NULL,
169 VirusIn INTEGER NOT NULL,
170 VirusOut INTEGER NOT NULL,
171 SpamIn INTEGER NOT NULL,
172 SpamOut INTEGER NOT NULL,
173 BouncesIn INTEGER NOT NULL,
174 BouncesOut INTEGER NOT NULL,
175 GreylistCount INTEGER NOT NULL,
176 SPFCount INTEGER NOT NULL,
177 PTimeSum REAL NOT NULL,
178 MTime INTEGER NOT NULL,
179 RBLCount INTEGER DEFAULT 0 NOT NULL,
180 PRIMARY KEY (Time));
181
182 CREATE INDEX DailyStat_MTime_Index ON DailyStat (MTime);
183
184__EOD
185
186my $domain_stat_ctablecmd = <<__EOD;
187 CREATE TABLE DomainStat
188 (Time INTEGER NOT NULL,
189 Domain VARCHAR(255) NOT NULL,
190 CountIn INTEGER NOT NULL,
191 CountOut INTEGER NOT NULL,
192 BytesIn REAL NOT NULL,
193 BytesOut REAL NOT NULL,
194 VirusIn INTEGER NOT NULL,
195 VirusOut INTEGER NOT NULL,
196 SpamIn INTEGER NOT NULL,
197 SpamOut INTEGER NOT NULL,
198 BouncesIn INTEGER NOT NULL,
199 BouncesOut INTEGER NOT NULL,
200 PTimeSum REAL NOT NULL,
201 MTime INTEGER NOT NULL,
202 PRIMARY KEY (Time, Domain));
203
204 CREATE INDEX DomainStat_MTime_Index ON DomainStat (MTime);
205__EOD
206
207my $statinfo_ctablecmd = <<__EOD;
208 CREATE TABLE StatInfo
209 (Name VARCHAR(255) NOT NULL UNIQUE,
210 IValue INTEGER,
211 SValue VARCHAR(255),
212 PRIMARY KEY (Name))
213__EOD
214
215my $virusinfo_stat_ctablecmd = <<__EOD;
216 CREATE TABLE VirusInfo
217 (Time INTEGER NOT NULL,
218 Name VARCHAR NOT NULL,
219 Count INTEGER NOT NULL,
220 MTime INTEGER NOT NULL,
221 PRIMARY KEY (Time, Name));
222
223 CREATE INDEX VirusInfo_MTime_Index ON VirusInfo (MTime);
224
225__EOD
226
227# mail storage stable
228# QTypes
229# V - Virus quarantine
230# S - Spam quarantine
231# D - Delayed Mails - not implemented
232# A - Held for Audit - not implemented
233# Status
234# N - new
235# D - deleted
236
237my $cmailstore_ctablecmd = <<__EOD;
238 CREATE TABLE CMailStore
239 (CID INTEGER DEFAULT 0 NOT NULL,
240 RID INTEGER NOT NULL,
241 ID SERIAL UNIQUE,
242 Time INTEGER NOT NULL,
243 QType "char" NOT NULL,
244 Bytes INTEGER NOT NULL,
245 Spamlevel INTEGER NOT NULL,
246 Info VARCHAR NULL,
247 Sender VARCHAR(255) NOT NULL,
248 Header VARCHAR NOT NULL,
249 File VARCHAR(255) NOT NULL,
250 PRIMARY KEY (CID, RID));
251 CREATE INDEX CMailStore_Time_Index ON CMailStore (Time);
252
253 CREATE TABLE CMSReceivers
254 (CMailStore_CID INTEGER NOT NULL,
255 CMailStore_RID INTEGER NOT NULL,
256 PMail VARCHAR(255) NOT NULL,
257 Receiver VARCHAR(255),
258 TicketID INTEGER NOT NULL,
259 Status "char" NOT NULL,
260 MTime INTEGER NOT NULL);
261
262 CREATE INDEX CMailStore_ID_Index ON CMSReceivers (CMailStore_CID, CMailStore_RID);
263
264 CREATE INDEX CMSReceivers_MTime_Index ON CMSReceivers (MTime);
265
266__EOD
267
268my $cstatistic_ctablecmd = <<__EOD;
269 CREATE TABLE CStatistic
270 (CID INTEGER DEFAULT 0 NOT NULL,
271 RID INTEGER NOT NULL,
272 ID SERIAL UNIQUE,
273 Time INTEGER NOT NULL,
274 Bytes INTEGER NOT NULL,
275 Direction Boolean NOT NULL,
276 Spamlevel INTEGER NOT NULL,
277 VirusInfo VARCHAR(255) NULL,
278 PTime INTEGER NOT NULL,
279 Sender VARCHAR(255) NOT NULL,
280 PRIMARY KEY (CID, RID));
281
282 CREATE INDEX CStatistic_Time_Index ON CStatistic (Time);
283
284 CREATE TABLE CReceivers
285 (CStatistic_CID INTEGER NOT NULL,
286 CStatistic_RID INTEGER NOT NULL,
287 Receiver VARCHAR(255) NOT NULL,
288 Blocked Boolean NOT NULL);
289
290 CREATE INDEX CStatistic_ID_Index ON CReceivers (CStatistic_CID, CStatistic_RID);
291__EOD
292
293# user preferences (black an whitelists, ...)
294# Name: perference name ('BL' -> blacklist, 'WL' -> whitelist)
295# Data: arbitrary data
296my $userprefs_ctablecmd = <<__EOD;
297 CREATE TABLE UserPrefs
298 (PMail VARCHAR,
299 Name VARCHAR(255),
300 Data VARCHAR,
301 MTime INTEGER NOT NULL,
302 PRIMARY KEY (PMail, Name));
303
304 CREATE INDEX UserPrefs_MTime_Index ON UserPrefs (MTime);
305
306__EOD
758c7b6b 307
0a580593
DM
308sub cond_create_dbtable {
309 my ($dbh, $name, $ctablecmd) = @_;
310
311 eval {
312 $dbh->begin_work;
313
314 my $cmd = "SELECT tablename FROM pg_tables " .
315 "WHERE tablename = lower ('$name')";
316
317 my $sth = $dbh->prepare ($cmd);
758c7b6b 318
0a580593
DM
319 $sth->execute();
320
321 if (!(my $ref = $sth->fetchrow_hashref())) {
322 $dbh->do ($ctablecmd);
323 }
758c7b6b 324
0a580593
DM
325 $sth->finish();
326
327 $dbh->commit;
328 };
329 if (my $err = $@) {
330 $dbh->rollback;
9ef3f143 331 die $err;
0a580593
DM
332 }
333}
f1bf78ff
DM
334
335sub create_ruledb {
336 my ($dbname) = @_;
337
987ee804 338 $dbname = $default_db_name if !$dbname;
f1bf78ff
DM
339
340 # use sql_ascii to avoid any character set conversions, and be compatible with
341 # older postgres versions (update from 8.1 must be possible)
342 my $cmd = [ 'createdb', '-U', 'postgres', '-E', 'sql_ascii',
343 '-T', 'template0', '--lc-collate=C', '--lc-ctype=C', $dbname ];
344
345 PVE::Tools::run_command($cmd);
346
347 my $dbh = open_ruledb($dbname);
348
349 #$dbh->do ($dbloaddrivers_sql);
350 #$dbh->do ($dbfunction_update_modtime);
351
352 $dbh->do ($dbfunction_minint);
353
354 $dbh->do ($dbfunction_maxint);
355
356 $dbh->do ($dbfunction_merge_greylist);
357
358 $dbh->do (
359<<EOD
360 CREATE TABLE Attribut
361 (Object_ID INTEGER NOT NULL,
362 Name VARCHAR(20) NOT NULL,
363 Value BYTEA NULL,
364 PRIMARY KEY (Object_ID, Name));
365
366 CREATE INDEX Attribut_Object_ID_Index ON Attribut(Object_ID);
367
368 CREATE TABLE Object
369 (ID SERIAL UNIQUE,
370 ObjectType INTEGER NOT NULL,
371 Objectgroup_ID INTEGER NOT NULL,
372 Value BYTEA NULL,
373 PRIMARY KEY (ID));
374
375 CREATE TABLE Objectgroup
376 (ID SERIAL UNIQUE,
377 Name VARCHAR(255) NOT NULL,
378 Info VARCHAR(255) NULL,
379 Class VARCHAR(10) NOT NULL,
380 PRIMARY KEY (ID));
381
382 CREATE TABLE Rule
383 (ID SERIAL UNIQUE,
384 Name VARCHAR(255) NULL,
385 Priority INTEGER NOT NULL,
386 Active INTEGER NOT NULL DEFAULT 0,
387 Direction INTEGER NOT NULL DEFAULT 2,
388 Count INTEGER NOT NULL DEFAULT 0,
389 PRIMARY KEY (ID));
390
391 CREATE TABLE RuleGroup
392 (Objectgroup_ID INTEGER NOT NULL,
393 Rule_ID INTEGER NOT NULL,
394 Grouptype INTEGER NOT NULL,
395 PRIMARY KEY (Objectgroup_ID, Rule_ID, Grouptype));
396
397 $cgreylist_ctablecmd;
398
399 $clusterinfo_ctablecmd;
400
401 $daily_stat_ctablecmd;
402
403 $domain_stat_ctablecmd;
404
405 $statinfo_ctablecmd;
406
407 $cmailstore_ctablecmd;
408
409 $cstatistic_ctablecmd;
410
411 $userprefs_ctablecmd;
412
413 $virusinfo_stat_ctablecmd;
414EOD
415 );
416
417 return $dbh;
418}
419
0a580593
DM
420sub cond_create_action_quarantine {
421 my ($ruledb) = @_;
422
423 my $dbh = $ruledb->{dbh};
424
425 eval {
426 my $sth = $dbh->prepare(
427 "SELECT * FROM Objectgroup, Object " .
428 "WHERE Object.ObjectType = ? AND Objectgroup.Class = ? " .
429 "AND Object.objectgroup_id = Objectgroup.id");
430
431 my $otype = PMG::RuleDB::Quarantine::otype();
432 if ($sth->execute($otype, 'action') <= 0) {
433 my $obj = PMG::RuleDB::Quarantine->new ();
434 my $txt = decode_entities(PMG::RuleDB::Quarantine->otype_text);
435 my $quarantine = $ruledb->create_group_with_obj
9578dcd7 436 ($obj, $txt, 'Move to quarantine.');
0a580593
DM
437 }
438 };
439}
440
441sub cond_create_std_actions {
442 my ($ruledb) = @_;
443
444 cond_create_action_quarantine($ruledb);
445
446 #cond_create_action_report_spam($ruledb);
447}
448
449
e7c865af
DM
450sub upgrade_mailstore_db {
451 my ($dbh) = @_;
452
453 eval {
454 $dbh->begin_work;
455
456 my $cmd = "SELECT tablename FROM pg_tables WHERE tablename = lower ('MailStore')";
457
458 my $sth = $dbh->prepare($cmd);
459 $sth->execute();
460 my $ref = $sth->fetchrow_hashref();
461 $sth->finish();
462
463 if ($ref) { # table exists
464
465 $cmd = "INSERT INTO CMailStore " .
466 "(CID, RID, ID, Time, QType, Bytes, Spamlevel, Info, Header, Sender, File) " .
467 "SELECT 0, ID, ID, Time, QType, Bytes, Spamlevel, Info, Header, Sender, File FROM MailStore";
468
469 $dbh->do($cmd);
470
471 $cmd = "INSERT INTO CMSReceivers " .
472 "(CMailStore_CID, CMailStore_RID, PMail, Receiver, TicketID, Status, MTime) " .
473 "SELECT 0, MailStore_ID, PMail, Receiver, TicketID, Status, 0 FROM MSReceivers";
474
475 $dbh->do($cmd);
476
477 $dbh->do("SELECT setval ('cmailstore_id_seq', nextval ('mailstore_id_seq'))");
478
479 $dbh->do("DROP TABLE MailStore");
480 $dbh->do("DROP TABLE MSReceivers");
481 }
482
483 $dbh->commit;
484 };
485 if (my $err = $@) {
486 $dbh->rollback;
487 die $err;
488 }
489}
490
491sub upgrade_dailystat_db {
492 my ($dbh) = @_;
493
494 eval { # make sure we have MTime
495 $dbh->do("ALTER TABLE DailyStat ADD COLUMN MTime INTEGER;" .
496 "UPDATE DailyStat SET MTime = EXTRACT (EPOCH FROM now());");
497 };
498
499 eval { # make sure we have correct constraints for MTime
500 $dbh->do ("ALTER TABLE DailyStat ALTER COLUMN MTime SET NOT NULL;");
501 };
502
503 eval { # make sure we have RBLCount
504 $dbh->do ("ALTER TABLE DailyStat ADD COLUMN RBLCount INTEGER;" .
505 "UPDATE DailyStat SET RBLCount = 0;");
506 };
507
508 eval { # make sure we have correct constraints for RBLCount
509 $dbh->do ("ALTER TABLE DailyStat ALTER COLUMN RBLCount SET DEFAULT 0;" .
510 "ALTER TABLE DailyStat ALTER COLUMN RBLCount SET NOT NULL;");
511 };
512
513 eval {
514 $dbh->begin_work;
515
516 my $cmd = "SELECT indexname FROM pg_indexes WHERE indexname = lower ('DailyStat_MTime_Index')";
517
518 my $sth = $dbh->prepare($cmd);
519 $sth->execute();
520 my $ref = $sth->fetchrow_hashref();
521 $sth->finish();
522
523 if (!$ref) { # index does not exist
524 $dbh->do ("CREATE INDEX DailyStat_MTime_Index ON DailyStat (MTime)");
525 }
526
527 $dbh->commit;
528 };
529 if (my $err = $@) {
530 $dbh->rollback;
531 die $err;
532 }
533}
534
535sub upgrade_domainstat_db {
536 my ($dbh) = @_;
537
538 eval { # make sure we have MTime
539 $dbh->do("ALTER TABLE DomainStat ADD COLUMN MTime INTEGER;" .
540 "UPDATE DomainStat SET MTime = EXTRACT (EPOCH FROM now());" .
541 "ALTER TABLE DomainStat ALTER COLUMN MTime SET NOT NULL;");
542 };
543
544 eval {
545 $dbh->begin_work;
546
547 my $cmd = "SELECT indexname FROM pg_indexes WHERE indexname = lower ('DomainStat_MTime_Index')";
548
549 my $sth = $dbh->prepare($cmd);
550 $sth->execute();
551 my $ref = $sth->fetchrow_hashref();
552 $sth->finish();
553
554 if (!$ref) { # index does not exist
555 $dbh->do ("CREATE INDEX DomainStat_MTime_Index ON DomainStat (MTime)");
556 }
557
558 $dbh->commit;
559 };
560 if (my $err = $@) {
561 $dbh->rollback;
562 die $@;
563 }
564}
565
566sub upgrade_statistic_db {
567 my ($dbh) = @_;
568
569 eval {
570 $dbh->begin_work;
571
572 my $cmd = "SELECT tablename FROM pg_tables WHERE tablename = lower ('Statistic')";
573
574 my $sth = $dbh->prepare($cmd);
575 $sth->execute();
576 my $ref = $sth->fetchrow_hashref();
577 $sth->finish();
578
579 if ($ref) { # old table exists
580
581 my $timezone = tz_local_offset();;
582
583 $dbh->do("INSERT INTO VirusInfo (Time, Name, Count, MTime) " .
584 "SELECT ((time + $timezone) / 86400) * 86400 as day, virusinfo, " .
585 "count (virusinfo), max (Time) FROM Statistic " .
586 "WHERE virusinfo IS NOT NULL GROUP BY day, virusinfo");
587
588 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime (time());
589 my $end = timelocal(0, 0, 0, $mday, $mon, $year);
590 my $start = $end - 3600*24*7; # / days
591
592 $cmd = "INSERT INTO CStatistic " .
593 "(CID, RID, ID, Time, Bytes, Direction, Spamlevel, VirusInfo, PTime, Sender) " .
594 "SELECT 0, ID, ID, Time, Bytes, Direction, Spamlevel, VirusInfo, PTime, Sender FROM Statistic " .
595 "WHERE time >= $start";
596
597 $dbh->do($cmd);
598
599 $dbh->do("SELECT setval ('cstatistic_id_seq', nextval ('statistic_id_seq'))");
600
601 $dbh->do("INSERT INTO StatInfo (name, ivalue) VALUES ('virusinfo_index', " .
602 "nextval ('statistic_id_seq'))");
603
604 $cmd = "INSERT INTO CReceivers (CStatistic_CID, CStatistic_RID, Receiver, Blocked) " .
605 "SELECT 0, Mail_ID, Receiver, Blocked FROM Receivers " .
606 "WHERE EXISTS (SELECT * FROM CStatistic WHERE CID = 0 AND RID = Mail_ID)";
607
608 $dbh->do($cmd);
609
610 $dbh->do("DROP TABLE Statistic");
611 $dbh->do("DROP TABLE Receivers");
612 }
613
614 $dbh->commit;
615 };
616 if (my $err = $@) {
617 $dbh->rollback;
618 die $err;
619 }
620}
621
622sub upgrade_greylist_db {
623 my ($dbh) = @_;
624
625 eval {
626 $dbh->begin_work;
627
628 my $cmd = "SELECT tablename FROM pg_tables WHERE tablename = lower ('Greylist')";
629
630 my $sth = $dbh->prepare($cmd);
631 $sth->execute();
632 my $ref = $sth->fetchrow_hashref();
633 $sth->finish();
634
635 if ($ref) { # table exists
636
637 $cmd = "INSERT INTO CGreylist " .
638 "(IPNet, Host, Sender, Receiver, Instance, RCTime, ExTime, Delay, Blocked, Passed, MTime, CID) " .
639 "SELECT IPNet, Host, Sender, Receiver, Instance, RCTime, ExTime, Delay, Blocked, Passed, RCTime, 0 FROM Greylist";
640
641 $dbh->do($cmd);
642
643 $dbh->do("DROP TABLE Greylist");
644 }
645
646 $dbh->commit;
647 };
648 if (my $err = $@) {
649 $dbh->rollback;
650 die $err;
651 }
652}
653
654sub upgrade_userprefs_db {
655 my ($dbh) = @_;
656
657 eval {
658 $dbh->do("ALTER TABLE UserPrefs ADD COLUMN MTime INTEGER;" .
659 "UPDATE UserPrefs SET MTime = EXTRACT (EPOCH FROM now());" .
660 "ALTER TABLE UserPrefs ALTER COLUMN MTime SET NOT NULL;");
661 };
662
663
664 eval {
665 $dbh->begin_work;
666
667 my $cmd = "SELECT indexname FROM pg_indexes WHERE indexname = lower ('UserPrefs_MTime_Index')";
668
669 my $sth = $dbh->prepare($cmd);
670 $sth->execute();
671 my $ref = $sth->fetchrow_hashref();
672 $sth->finish();
673
674 if (!$ref) { # index does not exist
675 $dbh->do("CREATE INDEX UserPrefs_MTime_Index ON UserPrefs (MTime)");
676 }
677
678 $dbh->commit;
679 };
680 if ($@) {
681 $dbh->rollback;
682 die $@;
683 }
684}
685
0a580593
DM
686sub upgradedb {
687 my ($ruledb) = @_;
688
689 my $dbh = $ruledb->{dbh};
690
e7c865af 691 $dbh->do($dbfunction_minint);
0a580593 692
e7c865af 693 $dbh->do($dbfunction_maxint);
0a580593 694
e7c865af 695 $dbh->do($dbfunction_merge_greylist);
0a580593 696
758c7b6b 697 # make sure we do not use slow sequential scans when upgraing
0a580593
DM
698 # database (before analyze can gather statistics)
699 $dbh->do("set enable_seqscan = false");
700
e7c865af
DM
701 cond_create_dbtable($dbh, 'DailyStat', $daily_stat_ctablecmd);
702 cond_create_dbtable($dbh, 'DomainStat', $domain_stat_ctablecmd);
703 cond_create_dbtable($dbh, 'StatInfo', $statinfo_ctablecmd);
704 cond_create_dbtable($dbh, 'CMailStore', $cmailstore_ctablecmd);
705 cond_create_dbtable($dbh, 'UserPrefs', $userprefs_ctablecmd);
706 cond_create_dbtable($dbh, 'CGreylist', $cgreylist_ctablecmd);
707 cond_create_dbtable($dbh, 'CStatistic', $cstatistic_ctablecmd);
708 cond_create_dbtable($dbh, 'ClusterInfo', $clusterinfo_ctablecmd);
709 cond_create_dbtable($dbh, 'VirusInfo', $virusinfo_stat_ctablecmd);
0a580593 710
e7c865af 711 cond_create_std_actions($ruledb);
0a580593 712
e7c865af 713 upgrade_mailstore_db($dbh);
0a580593 714
e7c865af 715 upgrade_statistic_db($dbh);
0a580593 716
e7c865af 717 upgrade_userprefs_db($dbh);
0a580593 718
e7c865af 719 upgrade_greylist_db($dbh);
0a580593 720
e7c865af 721 upgrade_dailystat_db($dbh);
0a580593 722
e7c865af 723 upgrade_domainstat_db($dbh);
0a580593
DM
724
725 # update obsolete content type names
726 eval {
e7c865af
DM
727 $dbh->do("UPDATE Object " .
728 "SET value = 'content-type:application/java-vm' ".
729 "WHERE objecttype = 3003 " .
730 "AND value = 'content-type:application/x-java-vm';");
0a580593
DM
731 };
732
733 eval {
734 $dbh->do ("ANALYZE");
735 };
736}
737
738sub init_ruledb {
739 my ($ruledb, $reset, $testmode) = @_;
740
741 my $dbh = $ruledb->{dbh};
742
743 if (!$reset) {
744 # Greylist Objectgroup
745 my $greylistgroup = PMG::RuleDB::Group->new
746 ("GreyExclusion", "-", "greylist");
747 $ruledb->save_group ($greylistgroup);
748
749 } else {
750 # we do not touch greylist objects
751 my $glids = "SELECT object.ID FROM Object, Objectgroup WHERE " .
752 "objectgroup_id = objectgroup.id and class = 'greylist'";
753
754 $dbh->do ("DELETE FROM Rule; " .
755 "DELETE FROM RuleGroup; " .
756 "DELETE FROM Attribut WHERE Object_ID NOT IN ($glids); " .
757 "DELETE FROM Object WHERE ID NOT IN ($glids); " .
758 "DELETE FROM Objectgroup WHERE class != 'greylist';");
759 }
760
761 # WHO Objects
762
763 # Blacklist
764 my $obj = PMG::RuleDB::EMail->new ('nomail@fromthisdomain.com');
765 my $blacklist = $ruledb->create_group_with_obj(
766 $obj, 'Blacklist', 'Global blacklist');
758c7b6b 767
0a580593
DM
768 # Whitelist
769 $obj = PMG::RuleDB::EMail->new('mail@fromthisdomain.com');
770 my $whitelist = $ruledb->create_group_with_obj(
771 $obj, 'Whitelist', 'Global whitelist');
772
773 # WHEN Objects
774
775 # Working hours
a29b9649 776 $obj = PMG::RuleDB::TimeFrame->new(8*60, 16*60);
0a580593
DM
777 my $working_hours =$ruledb->create_group_with_obj($obj, 'Office Hours' ,
778 'Usual office hours');
779
780 # WHAT Objects
781
0a580593 782 # Images
a29b9649
DM
783 $obj = PMG::RuleDB::ContentTypeFilter->new('image/.*');
784 my $img_content = $ruledb->create_group_with_obj(
785 $obj, 'Images', 'All kinds of graphic files');
786
0a580593 787 # Multimedia
a29b9649
DM
788 $obj = PMG::RuleDB::ContentTypeFilter->new('audio/.*');
789 my $mm_content = $ruledb->create_group_with_obj(
790 $obj, 'Multimedia', 'Audio and Video');
0a580593 791
a29b9649
DM
792 $obj = PMG::RuleDB::ContentTypeFilter->new('video/.*');
793 $ruledb->group_add_object($mm_content, $obj);
0a580593
DM
794
795 # Office Files
a29b9649
DM
796 $obj = PMG::RuleDB::ContentTypeFilter->new('application/vnd\.ms-excel');
797 my $office_content = $ruledb->create_group_with_obj(
798 $obj, 'Office Files', 'Common Office Files');
758c7b6b 799
a29b9649
DM
800 $obj = PMG::RuleDB::ContentTypeFilter->new(
801 'application/vnd\.ms-powerpoint');
758c7b6b 802
a29b9649 803 $ruledb->group_add_object($office_content, $obj);
758c7b6b 804
a29b9649 805 $obj = PMG::RuleDB::ContentTypeFilter->new('application/msword');
0a580593 806 $ruledb->group_add_object ($office_content, $obj);
758c7b6b 807
a29b9649
DM
808 $obj = PMG::RuleDB::ContentTypeFilter->new(
809 'application/vnd\.openxmlformats-officedocument\..*');
810 $ruledb->group_add_object($office_content, $obj);
758c7b6b 811
a29b9649
DM
812 $obj = PMG::RuleDB::ContentTypeFilter->new(
813 'application/vnd\.oasis\.opendocument\..*');
814 $ruledb->group_add_object($office_content, $obj);
0a580593 815
a29b9649
DM
816 $obj = PMG::RuleDB::ContentTypeFilter->new(
817 'application/vnd\.stardivision\..*');
818 $ruledb->group_add_object($office_content, $obj);
758c7b6b 819
a29b9649
DM
820 $obj = PMG::RuleDB::ContentTypeFilter->new(
821 'application/vnd\.sun\.xml\..*');
822 $ruledb->group_add_object($office_content, $obj);
758c7b6b 823
0a580593 824 # Dangerous Content
a29b9649
DM
825 $obj = PMG::RuleDB::ContentTypeFilter->new(
826 'application/x-ms-dos-executable');
827 my $exe_content = $ruledb->create_group_with_obj(
828 $obj, 'Dangerous Content', 'executable files and partial messages');
758c7b6b 829
a29b9649
DM
830 $obj = PMG::RuleDB::ContentTypeFilter->new('application/x-java');
831 $ruledb->group_add_object($exe_content, $obj);
832 $obj = PMG::RuleDB::ContentTypeFilter->new('application/javascript');
833 $ruledb->group_add_object($exe_content, $obj);
834 $obj = PMG::RuleDB::ContentTypeFilter->new('application/x-executable');
835 $ruledb->group_add_object($exe_content, $obj);
79ef2452
DM
836 $obj = PMG::RuleDB::ContentTypeFilter->new('application/x-ms-dos-executable');
837 $ruledb->group_add_object($exe_content, $obj);
a29b9649
DM
838 $obj = PMG::RuleDB::ContentTypeFilter->new('message/partial');
839 $ruledb->group_add_object($exe_content, $obj);
840 $obj = PMG::RuleDB::MatchFilename->new('.*\.(vbs|pif|lnk|shs|shb)');
841 $ruledb->group_add_object($exe_content, $obj);
be6c5e4a 842 $obj = PMG::RuleDB::MatchFilename->new('.*\.\{.+\}');
a29b9649 843 $ruledb->group_add_object($exe_content, $obj);
0a580593
DM
844
845 # Virus
758c7b6b
DM
846 $obj = PMG::RuleDB::Virus->new();
847 my $virus = $ruledb->create_group_with_obj(
848 $obj, 'Virus', 'Matches virus infected mail');
849
0a580593
DM
850 # WHAT Objects
851
852 # Spam
758c7b6b
DM
853 $obj = PMG::RuleDB::Spam->new(3);
854 my $spam3 = $ruledb->create_group_with_obj(
855 $obj, 'Spam (Level 3)', 'Matches possible spam mail');
e7c865af 856
758c7b6b
DM
857 $obj = PMG::RuleDB::Spam->new(5);
858 my $spam5 = $ruledb->create_group_with_obj(
859 $obj, 'Spam (Level 5)', 'Matches possible spam mail');
e7c865af 860
758c7b6b
DM
861 $obj = PMG::RuleDB::Spam->new(10);
862 my $spam10 = $ruledb->create_group_with_obj(
863 $obj, 'Spam (Level 10)', 'Matches possible spam mail');
0a580593
DM
864
865 # ACTIONS
866
758c7b6b
DM
867 # Mark Spam
868 $obj = PMG::RuleDB::ModField->new('X-SPAM-LEVEL', '__SPAM_INFO__');
869 my $mod_spam_level = $ruledb->create_group_with_obj(
e7c865af 870 $obj, 'Modify Spam Level',
758c7b6b 871 'Mark mail as spam by adding a header tag.');
0a580593
DM
872
873 # Mark Spam
758c7b6b
DM
874 $obj = PMG::RuleDB::ModField->new('subject', 'SPAM: __SUBJECT__');
875 my $mod_spam_subject = $ruledb->create_group_with_obj(
e7c865af 876 $obj, 'Modify Spam Subject',
758c7b6b 877 'Mark mail as spam by modifying the subject.');
e7c865af 878
0a580593 879 # Remove matching attachments
758c7b6b
DM
880 $obj = PMG::RuleDB::Remove->new(0);
881 my $remove = $ruledb->create_group_with_obj(
882 $obj, 'Remove attachments', 'Remove matching attachments');
e7c865af 883
0a580593 884 # Remove all attachments
758c7b6b
DM
885 $obj = PMG::RuleDB::Remove->new(1);
886 my $remove_all = $ruledb->create_group_with_obj(
887 $obj, 'Remove all attachments', 'Remove all attachments');
0a580593
DM
888
889 # Accept
758c7b6b
DM
890 $obj = PMG::RuleDB::Accept->new();
891 my $accept = $ruledb->create_group_with_obj(
892 $obj, 'Accept', 'Accept mail for Delivery');
0a580593
DM
893
894 # Block
758c7b6b
DM
895 $obj = PMG::RuleDB::Block->new ();
896 my $block = $ruledb->create_group_with_obj($obj, 'Block', 'Block mail');
0a580593
DM
897
898 # Quarantine
758c7b6b
DM
899 $obj = PMG::RuleDB::Quarantine->new();
900 my $quarantine = $ruledb->create_group_with_obj(
901 $obj, 'Quarantine', 'Move mail to quarantine');
0a580593
DM
902
903 # Notify Admin
758c7b6b
DM
904 $obj = PMG::RuleDB::Notify->new('__ADMIN__');
905 my $notify_admin = $ruledb->create_group_with_obj(
906 $obj, 'Notify Admin', 'Send notification');
0a580593
DM
907
908 # Notify Sender
758c7b6b
DM
909 $obj = PMG::RuleDB::Notify->new('__SENDER__');
910 my $notify_sender = $ruledb->create_group_with_obj(
911 $obj, 'Notify Sender', 'Send notification');
0a580593
DM
912
913 # Add Disclaimer
758c7b6b
DM
914 $obj = PMG::RuleDB::Disclaimer->new ();
915 my $add_discl = $ruledb->create_group_with_obj(
916 $obj, 'Disclaimer', 'Add Disclaimer');
0a580593
DM
917
918 # Attach original mail
919 #$obj = Proxmox::RuleDB::Attach->new ();
758c7b6b 920 #my $attach_orig = $ruledb->create_group_with_obj ($obj, 'Attach Original Mail',
0a580593
DM
921 # 'Attach Original Mail');
922
923 ####################### RULES ##################################
924
925 ## Block Dangerous Files
a29b9649 926 my $rule = PMG::RuleDB::Rule->new ('Block Dangerous Files', 93, 1, 0);
0a580593
DM
927 $ruledb->save_rule ($rule);
928
929 $ruledb->rule_add_what_group ($rule, $exe_content);
930 $ruledb->rule_add_action ($rule, $remove);
931
932 ## Block Viruses
a29b9649 933 $rule = PMG::RuleDB::Rule->new ('Block Viruses', 96, 1, 0);
0a580593
DM
934 $ruledb->save_rule ($rule);
935
936 $ruledb->rule_add_what_group ($rule, $virus);
937 $ruledb->rule_add_action ($rule, $notify_admin);
758c7b6b 938
0a580593
DM
939 if ($testmode) {
940 $ruledb->rule_add_action ($rule, $block);
941 } else {
942 $ruledb->rule_add_action ($rule, $quarantine);
943 }
944
945 ## Virus Alert
a29b9649 946 $rule = PMG::RuleDB::Rule->new ('Virus Alert', 96, 1, 1);
0a580593
DM
947 $ruledb->save_rule ($rule);
948
949 $ruledb->rule_add_what_group ($rule, $virus);
950 $ruledb->rule_add_action ($rule, $notify_sender);
951 $ruledb->rule_add_action ($rule, $notify_admin);
952 $ruledb->rule_add_action ($rule, $block);
758c7b6b 953
0a580593 954 ## Blacklist
a29b9649 955 $rule = PMG::RuleDB::Rule->new ('Blacklist', 98, 1, 0);
0a580593
DM
956 $ruledb->save_rule ($rule);
957
958 $ruledb->rule_add_from_group ($rule, $blacklist);
959 $ruledb->rule_add_action ($rule, $block);
960
961 ## Modify header
962 if (!$testmode) {
a29b9649 963 $rule = PMG::RuleDB::Rule->new ('Modify Header', 90, 1, 0);
0a580593
DM
964 $ruledb->save_rule ($rule);
965 $ruledb->rule_add_action ($rule, $mod_spam_level);
966 }
967
968 ## Whitelist
a29b9649 969 $rule = PMG::RuleDB::Rule->new ('Whitelist', 85, 1, 0);
0a580593
DM
970 $ruledb->save_rule ($rule);
971
972 $ruledb->rule_add_from_group ($rule, $whitelist);
973 $ruledb->rule_add_action ($rule, $accept);
974
975 if ($testmode) {
a29b9649 976 $rule = PMG::RuleDB::Rule->new ('Mark Spam', 80, 1, 0);
0a580593
DM
977 $ruledb->save_rule ($rule);
978
979 $ruledb->rule_add_what_group ($rule, $spam10);
980 $ruledb->rule_add_action ($rule, $mod_spam_level);
981 $ruledb->rule_add_action ($rule, $mod_spam_subject);
982 } else {
983 # Quarantine/Mark Spam (Level 3)
a29b9649 984 $rule = PMG::RuleDB::Rule->new ('Quarantine/Mark Spam (Level 3)', 80, 1, 0);
0a580593
DM
985 $ruledb->save_rule ($rule);
986
987 $ruledb->rule_add_what_group ($rule, $spam3);
988 $ruledb->rule_add_action ($rule, $mod_spam_subject);
989 $ruledb->rule_add_action ($rule, $quarantine);
990 #$ruledb->rule_add_action ($rule, $count_spam);
991 }
992
993 # Quarantine/Mark Spam (Level 5)
a29b9649 994 $rule = PMG::RuleDB::Rule->new ('Quarantine/Mark Spam (Level 5)', 79, 0, 0);
0a580593
DM
995 $ruledb->save_rule ($rule);
996
997 $ruledb->rule_add_what_group ($rule, $spam5);
998 $ruledb->rule_add_action ($rule, $mod_spam_subject);
999 $ruledb->rule_add_action ($rule, $quarantine);
1000
1001 ## Block Spam Level 10
a29b9649 1002 $rule = PMG::RuleDB::Rule->new ('Block Spam (Level 10)', 78, 0, 0);
0a580593
DM
1003 $ruledb->save_rule ($rule);
1004
1005 $ruledb->rule_add_what_group ($rule, $spam10);
1006 $ruledb->rule_add_action ($rule, $block);
1007
1008 ## Block Outgoing Spam
a29b9649 1009 $rule = PMG::RuleDB::Rule->new ('Block outgoing Spam', 70, 0, 1);
0a580593
DM
1010 $ruledb->save_rule ($rule);
1011
1012 $ruledb->rule_add_what_group ($rule, $spam3);
1013 $ruledb->rule_add_action ($rule, $notify_admin);
1014 $ruledb->rule_add_action ($rule, $notify_sender);
1015 $ruledb->rule_add_action ($rule, $block);
1016
1017 ## Add disclaimer
a29b9649 1018 $rule = PMG::RuleDB::Rule->new ('Add Disclaimer', 60, 0, 1);
0a580593
DM
1019 $ruledb->save_rule ($rule);
1020 $ruledb->rule_add_action ($rule, $add_discl);
1021
1022 # Block Multimedia Files
a29b9649 1023 $rule = PMG::RuleDB::Rule->new ('Block Multimedia Files', 87, 0, 2);
0a580593
DM
1024 $ruledb->save_rule ($rule);
1025
1026 $ruledb->rule_add_what_group ($rule, $mm_content);
1027 $ruledb->rule_add_action ($rule, $remove);
1028
1029 #$ruledb->rule_add_from_group ($rule, $anybody);
1030 #$ruledb->rule_add_from_group ($rule, $trusted);
1031 #$ruledb->rule_add_to_group ($rule, $anybody);
1032 #$ruledb->rule_add_what_group ($rule, $ct_filter);
1033 #$ruledb->rule_add_action ($rule, $add_discl);
1034 #$ruledb->rule_add_action ($rule, $remove);
1035 #$ruledb->rule_add_action ($rule, $bcc);
1036 #$ruledb->rule_add_action ($rule, $storeq);
1037 #$ruledb->rule_add_action ($rule, $accept);
1038
1039 cond_create_std_actions ($ruledb);
1040}
1041
8f4e102b
DM
1042sub get_remote_time {
1043 my ($rdb) = @_;
1044
1045 my $sth = $rdb->prepare("SELECT EXTRACT (EPOCH FROM TIMESTAMP (0) WITH TIME ZONE 'now') as ctime;");
1046 $sth->execute();
1047 my $ctinfo = $sth->fetchrow_hashref();
1048 $sth->finish ();
1049
1050 return $ctinfo ? $ctinfo->{ctime} : 0;
1051}
1052
cfdf6608
DM
1053sub init_masterdb {
1054 my ($lcid, $database) = @_;
1055
1056 die "got unexpected cid for new master" if !$lcid;
1057
1058 my $dbh;
1059
1060 eval {
1061 $dbh = open_ruledb($database);
1062
1063 $dbh->begin_work;
1064
1065 print STDERR "update quarantine database\n";
1066 $dbh->do ("UPDATE CMailStore SET CID = $lcid WHERE CID = 0;" .
1067 "UPDATE CMSReceivers SET CMailStore_CID = $lcid WHERE CMailStore_CID = 0;");
1068
1069 print STDERR "update statistic database\n";
1070 $dbh->do ("UPDATE CStatistic SET CID = $lcid WHERE CID = 0;" .
1071 "UPDATE CReceivers SET CStatistic_CID = $lcid WHERE CStatistic_CID = 0;");
1072
1073 print STDERR "update greylist database\n";
1074 $dbh->do ("UPDATE CGreylist SET CID = $lcid WHERE CID = 0;");
1075
1076 $dbh->commit;
1077 };
1078 my $err = $@;
1079
1080 if ($dbh) {
1081 $dbh->rollback if $err;
1082 $dbh->disconnect();
1083 }
1084
1085 die $err if $err;
1086}
1087
987ee804
DM
1088sub update_master_clusterinfo {
1089 my ($clientcid) = @_;
1090
1091 my $dbh = open_ruledb();
1092
1093 $dbh->do("DELETE FROM ClusterInfo WHERE CID = $clientcid");
1094
1095 my @mt = ('CMSReceivers', 'CGreylist', 'UserPrefs', 'DomainStat', 'DailyStat', 'VirusInfo');
1096
1097 foreach my $table (@mt) {
1098 $dbh->do ("INSERT INTO ClusterInfo (cid, name, ivalue) select $clientcid, 'lastmt_$table', " .
1099 "EXTRACT(EPOCH FROM now())");
1100 }
1101}
1102
1103sub update_client_clusterinfo {
1104 my ($mastercid) = @_;
1105
1106 my $dbh = open_ruledb();
1107
1108 $dbh->do ("DELETE FROM StatInfo"); # not needed at node
1109
1110 $dbh->do ("DELETE FROM ClusterInfo WHERE CID = $mastercid");
1111
1112 $dbh->do ("INSERT INTO ClusterInfo (cid, name, ivalue) select $mastercid, 'lastid_CMailStore', " .
1113 "COALESCE (max (rid), -1) FROM CMailStore WHERE cid = $mastercid");
1114
1115 $dbh->do ("INSERT INTO ClusterInfo (cid, name, ivalue) select $mastercid, 'lastid_CStatistic', " .
1116 "COALESCE (max (rid), -1) FROM CStatistic WHERE cid = $mastercid");
1117
1118 my @mt = ('CMSReceivers', 'CGreylist', 'UserPrefs', 'DomainStat', 'DailyStat', 'VirusInfo');
1119
1120 foreach my $table (@mt) {
1121 $dbh->do ("INSERT INTO ClusterInfo (cid, name, ivalue) select $mastercid, 'lastmt_$table', " .
1122 "COALESCE (max (mtime), 0) FROM $table");
1123 }
1124}
1125
1126sub init_nodedb {
1127 my ($cinfo) = @_;
1128
1129 my $ni = $cinfo->{master};
1130
1131 die "no master defined - unable to sync data from master\n" if !$ni;
1132
1133 my $master_ip = $ni->{ip};
1134 my $master_cid = $ni->{cid};
22c16ff6 1135 my $master_name = $ni->{name};
987ee804
DM
1136
1137 my $fn = "/tmp/masterdb$$.tar";
1138 unlink $fn;
1139
1140 my $dbname = $default_db_name;
1141
1142 eval {
1143 print STDERR "copying master database from '${master_ip}'\n";
1144
22c16ff6
DM
1145 my $cmd = [['/usr/bin/ssh', '-o', 'BatchMode=yes',
1146 '-o', "HostKeyAlias=${master_name}",
1147 $master_ip, 'pg_dump', '-U', 'postgres',
987ee804 1148 $dbname, '-F', 'c', \ ">$fn" ]];
22c16ff6 1149
987ee804
DM
1150 PVE::Tools::run_command($cmd);
1151
1152 my $size = -s $fn;
1153
1154 print STDERR "copying master database finished (got $size bytes)\n";
1155
1156 print STDERR "delete local database\n";
1157
1158 $cmd = [ 'dropdb', '-U', 'postgres', $dbname , '--if-exists'];
1159 PVE::Tools::run_command($cmd);
1160
1161 print STDERR "create new local database\n";
1162
1163 $cmd = ['createdb', '-U', 'postgres', $dbname];
1164 PVE::Tools::run_command($cmd);
1165
1166 print STDERR "insert received data into local database\n";
1167
1168 my $mess;
1169 my $parser = sub {
1170 my $line = shift;
1171
1172 if ($line =~ m/restoring data for table \"(.+)\"/) {
1173 print STDERR "restoring table $1\n";
1174 } elsif (!$mess && ($line =~ m/creating (INDEX|CONSTRAINT)/)) {
1175 $mess = "creating indexes";
1176 print STDERR "$mess\n";
1177 }
1178 };
1179
1180 $cmd = ['pg_restore', '-U', 'postgres', '-d', $dbname, '-v', $fn];
1181 PVE::Tools::run_command($cmd, outfunc => $parser, errfunc => $parser,
1182 errmsg => "pg_restore failed");
1183
1184 print STDERR "run analyze to speed up database queries\n";
1185
1186 $cmd = ['psql', '-U', 'postgres', $dbname];
1187 PVE::Tools::run_command($cmd);
1188
1189 update_client_clusterinfo($master_cid);
1190 };
1191
1192 my $err = $@;
1193
1194 unlink $fn;
1195
1196 die $err if $err;
1197}
1198
f3464b71
DM
1199sub cluster_sync_status {
1200 my ($cinfo) = @_;
1201
1202 my $dbh;
1203
1204 my $minmtime;
1205
1206 foreach my $ni (values %{$cinfo->{ids}}) {
1207 next if $cinfo->{local}->{cid} == $ni->{cid}; # skip local CID
1208 $minmtime->{$ni->{cid}} = 0;
1209 }
1210
1211 eval {
1212 $dbh = open_ruledb();
1213
1214 my $sth = $dbh->prepare(
1215 "SELECT cid, MIN (ivalue) as minmtime FROM ClusterInfo " .
1216 "WHERE name = 'lastsync' AND ivalue > 0 " .
1217 "GROUP BY cid");
1218
1219 $sth->execute();
1220
1221 while (my $info = $sth->fetchrow_hashref()) {
1222 foreach my $ni (values %{$cinfo->{ids}}) {
1223 next if $cinfo->{local}->{cid} == $ni->{cid}; # skip local CID
1224 if ($ni->{cid} == $info->{cid}) { # node exists
1225 $minmtime->{$ni->{cid}} = $info->{minmtime};
1226 }
1227 }
1228 }
1229
1230 $sth->finish ();
1231 };
1232 my $err = $@;
1233
1234 $dbh->disconnect() if $dbh;
1235
1236 if ($err) {
1237 syslog('err', PMG::Utils::msgquote($err));
1238 }
1239
1240 return $minmtime;
1241}
1242
cfdf6608 1243
a355f100 12441;