--- /dev/null
+package PVE::HA::Env;
+
+use strict;
+use warnings;
+
+use PVE::SafeSyslog;
+
+# abstract out the cluster environment
+
+sub new {
+ my ($this) = @_;
+
+ my $class = ref($this) || $this;
+
+ my $self = bless {}, $class;
+
+ return $self;
+}
+
+sub log {
+ my ($self, $level, $msg) = @_;
+
+ syslog($level, $msg);
+}
+
+# aquire a cluster wide lock
+sub get_ha_manager_lock {
+ my ($self) = @_;
+
+ die "implement me";
+}
+
+# return current time
+# overwrite that if you want to simulate
+sub get_time {
+ my ($self) = @_;
+
+ return time();
+}
+
+sub sleep {
+ my ($self, $delay) = @_;
+
+ sleep($delay);
+}
+
+sub loop_start_hook {
+ my ($self) = @_;
+
+ # do nothing
+}
+
+sub loop_end_hook {
+ my ($self) = @_;
+
+ # do nothing
+}
+
+
+1;
--- /dev/null
+package PVE::HA::SimEnv;
+
+use strict;
+use warnings;
+use POSIX qw(strftime);
+
+use PVE::HA::Env;
+
+use base qw(PVE::HA::Env);
+
+my $max_sim_time = 1000;
+
+sub log {
+ my ($self, $level, $msg) = @_;
+
+ my $time = $self->get_time();
+
+ my $timestr = strftime("%H:%M:%S", gmtime($time));
+
+ print "$level $timestr: $msg\n";
+}
+
+my $cur_time = 0;
+
+sub get_time {
+ my ($self) = @_;
+
+ return $cur_time;
+}
+
+sub sleep {
+ my ($self, $delay) = @_;
+
+ $cur_time += $delay;
+}
+
+sub get_ha_manager_lock {
+ my ($self) = @_;
+
+ ++$cur_time;
+
+ return 1;
+}
+
+sub loop_start_hook {
+ my ($self) = @_;
+
+ $self->{loop_start_time} = $cur_time;
+
+ # do nothing
+}
+
+sub loop_end_hook {
+ my ($self) = @_;
+
+ my $delay = $cur_time - $self->{loop_start_time};
+
+ die "loop take too long ($delay seconds)\n" if $delay > 20;
+
+ $cur_time++;
+
+ die "simulation end\n" if $cur_time > $max_sim_time;
+}
+
+1;
--- /dev/null
+#!/usr/bin/perl
+
+use lib '.';
+
+use strict;
+use warnings;
+
+use PVE::Tools;
+use PVE::HA::SimEnv;
+
+my $statusdir = "/var/tmp/pve-ha-manager";
+
+mkdir $statusdir;
+
+my $haenv = PVE::HA::SimEnv->new();
+
+my $status;
+
+sub get_manager_status {
+
+ my $status = PVE::Tools::file_read_firstline("$statusdir/status");
+
+ $status = 'startup' if !$status;
+
+ return $status;
+}
+
+sub set_manager_status {
+ my ($new_status) = @_;
+
+ return if $status eq $new_status;
+
+ $haenv->log('info', "manager status change $status => $new_status");
+
+ $status = $new_status;
+
+ PVE::Tools::file_set_contents("$statusdir/status", $status);
+}
+
+sub get_manager_lock {
+
+ my $count = 0;
+ for (;;) {
+ return 1 if $haenv->get_ha_manager_lock();
+ last if ++$count > 5;
+ $haenv->sleep(1);
+ }
+
+ return 0;
+}
+
+
+$status = get_manager_status();
+
+# can happen after crash?
+if ($status eq 'quorate') {
+ set_manager_status('recover');
+} else {
+ set_manager_status('startup');
+
+}
+
+
+$haenv->log('info', "starting simulation environment (status = $status)");
+
+eval {
+
+ for (;;) {
+
+ $haenv->loop_start_hook();
+
+ if ($status eq 'recover') {
+ $haenv->log('info', "waiting for 5 seconds");
+ $haenv->sleep(5);
+ set_manager_status('startup');
+ } elsif ($status eq 'startup') {
+ if (get_manager_lock()) {
+ set_manager_status('quorate');
+ }
+ } elsif ($status eq 'quorate') {
+
+ } elsif ($status eq 'lost_quorum') {
+
+ } elsif ($status eq 'halt') {
+ die "halt\n";
+ } else {
+ die "got unexpected status '$status'\n";
+ }
+
+ $haenv->loop_end_hook();
+ }
+};
+if (my $err = $@) {
+ $haenv->log('err', "exit now (status = $status) - $err ");
+} else {
+ $haenv->log('info', "exit simulation environment (status = $status)");
+}
+
+exit(0);
+