use strict;
use warnings;
-use POSIX qw(strftime EINTR);
-use JSON;
-use IO::File;
+
use Fcntl qw(:DEFAULT :flock);
use File::Copy;
use File::Path qw(make_path remove_tree);
+use IO::File;
+use JSON;
+use POSIX qw(strftime EINTR);
+
use PVE::HA::FenceConfig;
use PVE::HA::Groups;
my $watchdog_timeout = 60;
-
# Status directory layout
#
# configuration
# $testdir/service_config Service configuration
# $testdir/groups HA groups configuration
# $testdir/service_status_<node> Service status
+# $testdir/datacenter.cfg Datacenter wide HA configuration
#
# runtime status for simulation system
my $filename = "$self->{statusdir}/lrm_status_$node";
- return PVE::HA::Tools::read_json_from_file($filename, {});
+ return PVE::HA::Tools::read_json_from_file($filename, {});
}
sub write_lrm_status {
my $filename = "$self->{statusdir}/lrm_status_$node";
- PVE::HA::Tools::write_json_to_file($filename, $status_obj);
+ PVE::HA::Tools::write_json_to_file($filename, $status_obj);
}
sub read_hardware_status_nolock {
my ($self) = @_;
my $filename = "$self->{statusdir}/service_config";
- my $conf = PVE::HA::Tools::read_json_from_file($filename);
+ my $conf = PVE::HA::Tools::read_json_from_file($filename);
foreach my $sid (keys %$conf) {
my $d = $conf->{$sid};
return $conf;
}
+sub update_service_config {
+ my ($self, $sid, $param) = @_;
+
+ my $conf = $self->read_service_config();
+
+ my $sconf = $conf->{$sid} || die "no such resource '$sid'\n";
+
+ foreach my $k (%$param) {
+ $sconf->{$k} = $param->{$k};
+ }
+
+ $self->write_service_config($conf);
+}
+
sub write_service_config {
my ($self, $conf) = @_;
die "no such service '$sid'\n" if !$conf->{$sid};
- die "current_node for '$sid' does not match ($current_node != $conf->{$sid}->{node})\n"
+ die "current_node for '$sid' does not match ($current_node != $conf->{$sid}->{node})\n"
if $current_node ne $conf->{$sid}->{node};
-
+
$conf->{$sid}->{node} = $new_node;
$self->write_service_config($conf);
my ($self, $cmd) = @_;
my $code = sub { $self->queue_crm_commands_nolock($cmd); };
-
+
$self->global_lock($code);
return undef;
return $data;
};
-
+
return $self->global_lock($code);
}
my ($self, $node) = @_;
my $filename = "$self->{statusdir}/service_status_$node";
- return PVE::HA::Tools::read_json_from_file($filename);
+ return PVE::HA::Tools::read_json_from_file($filename);
}
sub write_service_status {
# fixme: add test if a service runs on two nodes!!!
return $res;
-}
+}
my $default_group_config = <<__EOD;
group: prefer_node1
copy("$testdir/fence.cfg", "$statusdir/fence.cfg");
}
+ if (-f "$testdir/datacenter.cfg") {
+ copy("$testdir/datacenter.cfg", "$statusdir/datacenter.cfg");
+ }
+
my $cstatus = $self->read_hardware_status_nolock();
foreach my $node (sort keys %$cstatus) {
if (-f "$testdir/service_status_$node") {
copy("$testdir/service_status_$node", "$statusdir/service_status_$node");
- } else {
+ } else {
$self->write_service_status($node, {});
}
}
return $self->{statusdir};
}
+sub read_datacenter_conf {
+ my ($self, $node) = @_;
+
+ my $filename = "$self->{statusdir}/datacenter.cfg";
+ return PVE::HA::Tools::read_json_from_file($filename, {});
+}
+
sub global_lock {
my ($self, $code, @param) = @_;
eval { $res = &$code($fh, @param) };
my $err = $@;
-
+
close($fh);
die $err if $err;
-
+
return $res;
}
}
my $quorate = ($online_count > int($node_count/2)) ? 1 : 0;
-
+
if (!$quorate) {
foreach my $node (keys %$cstatus) {
my $d = $cstatus->{$node};
return !defined($res) || $res;
}
-# simulate hardware commands
-# power <node> <on|off>
-# network <node> <on|off>
-# cfs <node> <rw|update> <work|fail>
-# reboot <node>
-# shutdown <node>
-# restart-lrm <node>
-# service <sid> <started|disabled|stopped|ignored>
-# service <sid> <migrate|relocate> <target>
-# service <sid> lock/unlock [lockname]
-
+# simulate hardware commands, the following commands are available:
+# power <node> <on|off>
+# network <node> <on|off>
+# delay <seconds>
+# cfs <node> <rw|update> <work|fail>
+# reboot <node>
+# shutdown <node>
+# restart-lrm <node>
+# service <sid> <started|disabled|stopped|ignored>
+# service <sid> <migrate|relocate> <target>
+# service <sid> stop <timeout>
+# service <sid> lock/unlock [lockname]
+# service <sid> <add|delete>
sub sim_hardware_cmd {
my ($self, $cmdstr, $logid) = @_;
my $cstatus = $self->read_hardware_status_nolock();
- my ($cmd, $objid, $action, $target) = split(/\s+/, $cmdstr);
+ my ($cmd, $objid, $action, $param) = split(/\s+/, $cmdstr);
die "sim_hardware_cmd: no node or service for command specified"
if !$objid;
} elsif ($cmd eq 'cfs') {
die "sim_hardware_cmd: unknown cfs action '$action' for node '$node'"
if $action !~ m/^(rw|update)$/;
- die "sim_hardware_cmd: unknown cfs command '$target' for '$action' on node '$node'"
- if $target !~ m/^(work|fail)$/;
+ die "sim_hardware_cmd: unknown cfs command '$param' for '$action' on node '$node'"
+ if $param !~ m/^(work|fail)$/;
- $cstatus->{$node}->{cfs}->{$action} = $target eq 'work';
+ $cstatus->{$node}->{cfs}->{$action} = $param eq 'work';
$self->write_hardware_status_nolock($cstatus);
} elsif ($cmd eq 'reboot' || $cmd eq 'shutdown') {
} elsif ($action eq 'migrate' || $action eq 'relocate') {
die "sim_hardware_cmd: missing target node for '$action' command"
- if !$target;
+ if !$param;
+
+ $self->queue_crm_commands_nolock("$action $sid $param");
+
+ } elsif ($action eq 'stop') {
- $self->queue_crm_commands_nolock("$action $sid $target");
+ die "sim_hardware_cmd: missing timeout for '$action' command"
+ if !defined($param);
+
+ $self->queue_crm_commands_nolock("$action $sid $param");
} elsif ($action eq 'add') {
- $self->add_service($sid, {state => 'started', node => $target});
+ $self->add_service($sid, {state => 'started', node => $param});
} elsif ($action eq 'delete') {
} elsif ($action eq 'lock') {
- $self->lock_service($sid, $target);
+ $self->lock_service($sid, $param);
} elsif ($action eq 'unlock') {
- $self->unlock_service($sid, $target);
+ $self->unlock_service($sid, $param);
} else {
die "sim_hardware_cmd: unknown service action '$action' " .
my $update_cmd = sub {
my $filename = "$self->{statusdir}/watchdog_status";
-
+
my ($res, $wdstatus);
if (-f $filename) {
} else {
$wdstatus = {};
}
-
+
($wdstatus, $res) = &$code($wdstatus);
PVE::Tools::file_set_contents($filename, encode_json($wdstatus));
foreach my $id (keys %$wdstatus) {
delete $wdstatus->{$id} if $wdstatus->{$id}->{node} eq $node;
}
-
+
PVE::Tools::file_set_contents($filename, encode_json($wdstatus));
}
}
delete $wdstatus->{$wfh};
}
}
-
+
return ($wdstatus, $res);
};
my $tdiff = $ctime - $wd->{update_time};
die "watchdog expired" if $tdiff > $watchdog_timeout;
-
+
$wd->{update_time} = $ctime;
return ($wdstatus);