1 package PMG
::Service
::pmgmirror
;
6 use Time
::HiRes qw
(gettimeofday tv_interval
);
9 use PVE
::Tools
qw(extract_param);
15 use PMG
::RESTEnvironment
;
19 use PMG
::ClusterConfig
;
22 use base
qw(PVE::Daemon);
24 my $cmdline = [$0, @ARGV];
26 my %daemon_options = (restart_on_error
=> 5, stop_wait_time
=> 5);
28 my $daemon = __PACKAGE__-
>new('pmgmirror', $cmdline, %daemon_options);
30 my $restart_request = 0;
34 my $updatetime = 60*2;
37 my $initial_memory_usage;
49 sub sync_data_from_node
{
50 my ($dbh, $rdb, $cinfo, $ni, $ticket, $rsynctime_ref) = @_;
52 my $ctime = PMG
::DBTools
::get_remote_time
($rdb);
55 my $timediff = abs($ltime - $ctime);
56 if ($timediff > $maxtimediff) {
57 die "large time difference (> $timediff seconds) - not syncing\n";
60 if ($ni->{type
} eq 'master') {
61 PMG
::Cluster
::sync_ruledb_from_master
($dbh, $rdb, $ni, $ticket);
62 PMG
::Cluster
::sync_deleted_nodes_from_master
($dbh, $rdb, $cinfo, $ni, $rsynctime_ref);
65 PMG
::Cluster
::sync_quarantine_db
($dbh, $rdb, $ni, $rsynctime_ref);
67 PMG
::Cluster
::sync_greylist_db
($dbh, $rdb, $ni);
69 PMG
::Cluster
::sync_userprefs_db
($dbh, $rdb, $ni);
71 PMG
::Cluster
::sync_statistic_db
($dbh, $rdb, $ni);
73 if ($ni->{type
} eq 'master') {
74 PMG
::Cluster
::sync_domainstat_db
($dbh, $rdb, $ni);
76 PMG
::Cluster
::sync_dailystat_db
($dbh, $rdb, $ni);
78 PMG
::Cluster
::sync_virusinfo_db
($dbh, $rdb, $ni);
81 PMG
::Cluster
::sync_localstat_db
($dbh, $rdb, $ni);
86 my $cinfo = PMG
::ClusterConfig-
>new(); # reload
87 my $role = $cinfo->{local}->{type
} // '-';
89 return if $role eq '-';
90 return if !$cinfo->{master
}; # just to be sure
92 my $start_time = [ gettimeofday
() ];
94 syslog
('info', "starting cluster syncronization");
96 my $master_ip = $cinfo->{master
}->{ip
};
97 my $master_name = $cinfo->{master
}->{name
};
99 if ($role ne 'master') {
100 PMG
::Cluster
::sync_config_from_master
($master_name, $master_ip);
103 my $csynctime = tv_interval
($start_time);
105 $cinfo = PMG
::ClusterConfig-
>new(); # reload
106 $role = $cinfo->{local}->{type
} // '-';
108 return if $role eq '-';
109 return if !$cinfo->{master
}; # just to be sure
111 my $ticket = PMG
::Ticket
::assemble_ticket
('root@pam');
113 my $dbh = PMG
::DBTools
::open_ruledb
();
119 my $sync_node = sub {
124 $rdb = PMG
::DBTools
::open_ruledb
(undef, '/var/run/pmgtunnel', $ni->{cid
});
125 sync_data_from_node
($dbh, $rdb, $cinfo, $ni, $ticket, \
$rsynctime);
129 $rdb->disconnect() if $rdb;
133 syslog
('err', "database sync '$ni->{name}' failed - $err");
135 PMG
::DBTools
::create_clusterinfo_default
($dbh, $ni->{cid
}, 'lastsync', 0, undef);
136 PMG
::DBTools
::write_maxint_clusterinfo
($dbh, $ni->{cid
}, 'lastsync', time());
140 # sync data from master first
141 if ($cinfo->{master
}->{cid
} ne $cinfo->{local}->{cid
}) {
142 $sync_node->($cinfo->{master
});
144 # rewrite config after sync from master
145 my $cfg = PMG
::Config-
>new();
146 my $ruledb = PMG
::RuleDB-
>new($dbh);
147 my $rulecache = PMG
::RuleCache-
>new($ruledb);
148 $cfg->rewrite_config($rulecache, 1);
151 foreach my $ni (values %{$cinfo->{ids
}}) {
152 next if $ni->{cid
} eq $cinfo->{local}->{cid
};
153 next if $ni->{cid
} eq $cinfo->{master
}->{cid
};
159 my $cptime = tv_interval
($start_time);
161 my $dbtime = $cptime - $rsynctime - $csynctime;
163 syslog
('info', sprintf("cluster syncronization finished (%d errors, %.2f seconds " .
164 "(files %.2f, database %.2f, config %.2f))",
165 $errcount, $cptime, $rsynctime, $dbtime, $csynctime));
174 $next_update = time() + $updatetime;
177 # Note: do nothing in first cycle (give pmgtunnel some time to startup)
178 cluster_sync
() if $cycle > 0;
181 syslog
('err', "sync error: $err");
186 last if $self->{terminate
};
188 my $mem = PVE
::ProcFSTools
::read_memory_usage
();
190 if (!defined($initial_memory_usage) || ($cycle < 10)) {
191 $initial_memory_usage = $mem->{resident
};
193 my $diff = $mem->{resident
} - $initial_memory_usage;
194 if ($diff > 5*1024*1024) {
195 syslog
('info', "restarting server after $cycle cycles to " .
196 "reduce memory usage (free $mem->{resident} ($diff) bytes)");
197 $self->restart_daemon();
202 while ((time() < $next_update) &&
203 ($wcount < $updatetime) && # protect against time wrap
204 !$restart_request && !$self->{terminate
}) {
206 $wcount++; sleep (1);
209 last if $self->{terminate
};
211 $self->restart_daemon() if $restart_request;
215 $daemon->register_start_command("Start the Database Mirror Daemon");
216 $daemon->register_stop_command("Stop the Database Mirror Daemon");
217 $daemon->register_restart_command(1, "Restart the Database Mirror Daemon");
220 start
=> [ __PACKAGE__
, 'start', []],
221 restart
=> [ __PACKAGE__
, 'restart', []],
222 stop
=> [ __PACKAGE__
, 'stop', []],