[pve-devel] [RFC pve-ha-manager 1/5] add FenceConfig class for external fencing devices

Thomas Lamprecht t.lamprecht at proxmox.com
Wed Nov 11 12:39:01 CET 2015


Add a FenceConfig package which includes methods to parse a config
file for fence devices in the format specified by dlm.conf, see the
Fencing section in the dlm.conf manpage for more details to the
format.

With this we can extract commands for fencing a node from the parsed
file.

For now we assume that the fence config resides under
/etc/pve/ha/fence_devices.cfg

Signed-off-by: Thomas Lamprecht <t.lamprecht at proxmox.com>
---
 src/PVE/HA/FenceConfig.pm | 200 ++++++++++++++++++++++++++++++++++++++++++++++
 src/PVE/HA/Makefile       |   2 +-
 2 files changed, 201 insertions(+), 1 deletion(-)
 create mode 100644 src/PVE/HA/FenceConfig.pm

diff --git a/src/PVE/HA/FenceConfig.pm b/src/PVE/HA/FenceConfig.pm
new file mode 100644
index 0000000..46fda33
--- /dev/null
+++ b/src/PVE/HA/FenceConfig.pm
@@ -0,0 +1,200 @@
+package PVE::HA::FenceConfig;
+
+use strict;
+use warnings;
+use PVE::Tools;
+use Data::Dumper;
+
+# my $device_cfg_file = "fence_devices.cfg"; # testing
+my $device_cfg_file = "/etc/pve/ha/fence_devices.cfg";
+
+# config example representation in hash form
+#my $test_fence_device = {
+#    foo_apc => { # <-- device name
+#	priority => 0,
+#	sub_devs => {
+#	    1 => { # two parallel devices
+#		agent => "fence_apc",
+#		base_args => "--ip=192.168.15.50 -l fencing -p 12345 -x -o off --delay=6",
+#		node_args => {
+#		    uno => "-n 1 -s 1",
+#		    due => "-n 1 -s 1",
+#		    tre => "-n 1 -s 1",
+#		},
+#	    },
+#	    2 => {
+#		agent => "fence_apc",
+#		base_args => "--ip=192.168.15.50 -l fencing -p 12345 -x -o on",
+#		node_args => {
+#		    uno => "-n 1 -s 1",
+#		    due => "-n 1 -s 1",
+#		    tre => "-n 1 -s 1",
+#		},
+#	    },
+#	}
+#    },
+#    bar_device => { # second fence device
+#	priority => 1,
+#	sub_devs => {
+#	    1 => {
+#		agent => "fence_pve",
+#		base_args => " --ip=192.168.15.35 --action=off --password=12345",
+#		node_args => {
+#		    uno => "-n 100",
+#		    due => "-n 101",
+#		    tre => "-n 102"
+#		}
+#	    },
+#	}
+#    },
+#};
+
+
+
+my $cfg_err = sub {
+    my ($lineno, $error_message) = @_;
+    die "$device_cfg_file#$lineno error: $error_message \n";
+};
+
+sub parse_config {
+
+    my $config = {};
+
+    if (!-f $device_cfg_file) {
+	warn "Fence device config file '$device_cfg_file' does not exist.";
+	return undef;
+    }
+
+    my $raw = PVE::Tools::file_get_contents($device_cfg_file);
+    my $lineno = 0;
+    my $priority = 0;
+    while ($raw =~ /^\s*(.+?)\s*$/gm) {
+	my $line = $1;
+
+	$lineno++;
+
+	next if $line =~ /^#/;
+
+	if ($line =~ m/^(device|connect)\s+(\S+)\s+(\S+)\s*(.*)$/) {
+	    my ($command, $dev_name, $target) = ($1, $2, $3);
+	    my $dev_number = 1; # default
+	    # allow spaces, and other special chars inside of ""
+	    my @arg_array = $4 =~ /(\S+(?:=(?:"(?:[^"\\]|\\.)*"|\S+))?)/g;
+	    my $args = gen_arg_str(@arg_array);
+
+	    # check for parallel devices
+	    if ($dev_name =~ m/^(\w+)(:(\d)+)?/) {
+		$dev_name = $1;
+		$dev_number = $3 if $3;
+	    }
+
+	    if ($command eq "device") {
+
+		my $dev = $config->{$dev_name} || {};
+
+		&$cfg_err($lineno, "device '$dev_name:$dev_number' already declared")
+		    if ($dev && $dev->{sub_devs}->{$dev_number});
+
+		$dev->{sub_devs}->{$dev_number} = {agent=>$target, base_args=>$args};
+		$dev->{priority} = $priority++ if !$dev->{priority};
+
+		$config->{$dev_name} = $dev;
+
+	    } else { # connect nodes to devices
+
+		&$cfg_err($lineno, "device '$dev_name' must be declared before" .
+			       " you can connect to them") if !$config->{$dev_name};
+
+		&$cfg_err($lineno, "No parallel device '$dev_name:$dev_number' found")
+		    if !$config->{$dev_name}->{sub_devs}->{$dev_number};
+
+		my $sdev = $config->{$dev_name}->{sub_devs}->{$dev_number};
+
+		my ($node) = $target =~ /node=(\w+)/;
+		&$cfg_err($lineno, "node=nodename needed to connect device ".
+			  "'$dev_name' to node") if !$node;
+
+		&$cfg_err($lineno, "node '$node' already connected to device ".
+			  "'$dev_name:$dev_number'") if $sdev->{node_args}->{$node};
+
+		$sdev->{node_args}->{$node} = $args;
+
+		$config->{$dev_name}->{sub_devs}->{$dev_number} = $sdev;
+
+	    }
+
+	} else {
+	    warn "$device_cfg_file#$lineno ignore line: $line\n"
+	}
+    }
+
+    return $config;
+
+}
+
+
+sub gen_arg_str {
+    my (@arguments) = @_;
+
+    my @shell_args = ();
+    foreach my $arg (@arguments) { push @shell_args, "--".$arg; }
+
+    return join (' ', @shell_args);
+}
+
+
+# returns command list to execute,
+# can be more than one command if parallel devices are configured
+# 'skip' denotes the number - 1 of devices we should skip and normaly equals to
+# failed fencing tries
+sub get_commands {
+    my ($node, $skip) = @_;
+
+    return undef if !$node;
+
+    $skip = 0 if !$skip || $skip<0;
+
+    my $config = parse_config();
+    return () if !$config;
+
+    foreach my $device (sort {$a->{priority} <=> $b->{priority}} values %$config) {
+	my @commands;
+
+	foreach my $sub_dev (values %{$device->{sub_devs}}) {
+	    if (my $node_args = $sub_dev->{node_args}->{$node}) {
+		my $agent = $sub_dev->{agent};
+		push @commands, $agent." ".$sub_dev->{base_args}." ".$node_args;
+	    }
+	}
+
+	if (@commands>0) {
+	    $skip--;
+	    return @commands if $skip<0;
+	}
+    }
+
+    # out of tries or no device for this node
+    return undef;
+}
+
+
+sub count_devices {
+    my ($node) = @_;
+
+    my $count = 0;
+
+    my $config = parse_config();
+
+    foreach my $device (values %$config) {
+	foreach my $sub_dev (values %{$device->{sub_devs}}) {
+	    if ($sub_dev->{node_args}->{$node}) {
+		$count += 1; # got a device here (no need to count parallel devs)
+		last;
+	    }
+	}
+    }
+
+    return $count;
+}
+
+1;
diff --git a/src/PVE/HA/Makefile b/src/PVE/HA/Makefile
index 4357015..b9902f6 100644
--- a/src/PVE/HA/Makefile
+++ b/src/PVE/HA/Makefile
@@ -1,4 +1,4 @@
-SOURCES=CRM.pm Env.pm Groups.pm Resources.pm Config.pm LRM.pm Manager.pm NodeStatus.pm Tools.pm
+SOURCES=CRM.pm Env.pm Groups.pm Resources.pm Config.pm LRM.pm Manager.pm NodeStatus.pm Tools.pm FenceConfig.pm
 
 .PHONY: install
 install:
-- 
2.1.4




More information about the pve-devel mailing list