]> git.proxmox.com Git - pve-ha-manager.git/blob - src/PVE/HA/FenceConfig.pm
f8b5f19417976ec05a6ba159d1b94e8a779df866
[pve-ha-manager.git] / src / PVE / HA / FenceConfig.pm
1 package PVE::HA::FenceConfig;
2
3 use strict;
4 use warnings;
5
6 use PVE::Tools;
7
8 sub parse_config {
9 my ($fn, $raw) = @_;
10
11 return {} if !$raw;
12
13 my $config = {};
14
15 my $lineno = 0;
16 my $priority = 0;
17 my $parse_errors = '';
18
19 my $parse_line = sub {
20 my ($line) = @_;
21
22 if ($line !~ m/^(device|connect)\s+(\S+)\s+(\S+)\s+(.+)$/) {
23 warn "$fn ignore line $lineno: $line\n";
24 return;
25 }
26 my ($command, $dev_name, $target) = ($1, $2, $3);
27 my $arg_array = PVE::Tools::split_args($4);
28 my $dev_number = 1; # default
29
30 # check for parallel devices
31 if ($dev_name =~ m/^(\w+)(:(\d+))?/) {
32 $dev_name = $1;
33 $dev_number = $3 if $3;
34 }
35
36 if ($command eq "device") {
37 my $dev = $config->{$dev_name} || {};
38
39 die "device '$dev_name:$dev_number' already declared\n"
40 if $dev && $dev->{sub_devs}->{$dev_number};
41
42 $dev->{sub_devs}->{$dev_number} = {
43 agent => $target,
44 args => $arg_array,
45 };
46 $dev->{priority} = $priority++ if !$dev->{priority};
47
48 $config->{$dev_name} = $dev;
49
50 } else { # connect nodes to devices
51 die "device '$dev_name' must be declared before you can connect to it\n"
52 if !$config->{$dev_name};
53
54 die "No parallel device '$dev_name:$dev_number' found\n"
55 if !$config->{$dev_name}->{sub_devs}->{$dev_number};
56
57 my $sdev = $config->{$dev_name}->{sub_devs}->{$dev_number};
58
59 my ($node) = $target =~ /node=(\w+)/;
60 die "node=nodename needed to connect device '$dev_name' to node\n"
61 if !$node;
62
63 die "node '$node' already connected to device '$dev_name:$dev_number'\n"
64 if $sdev->{node_args}->{$node};
65
66 $sdev->{node_args}->{$node} = $arg_array;
67
68 $config->{$dev_name}->{sub_devs}->{$dev_number} = $sdev;
69 }
70 };
71
72 while ($raw =~ /^\h*(.*?)\h*$/gm) {
73 my $line = $1;
74 $lineno++;
75 next if !$line || $line =~ /^#/;
76
77 eval { $parse_line->($line) };
78 if (my $err = $@) {
79 $parse_errors .= "line $lineno: $err";
80 }
81 }
82 die "Encountered error(s) on parsing '$fn':\n$parse_errors" if $parse_errors;
83
84 return $config;
85 }
86
87 sub write_config {
88 my ($fn, $data) = @_;
89
90 my $raw = '';
91
92 my $prev_priority = -1;
93
94 foreach my $dev_name (sort {$data->{$a}->{priority} <=> $data->{$b}->{priority}} keys %$data) {
95 my $d = $data->{$dev_name};
96
97 die "Device '$dev_name' reuses priority! Priorities must be unique\n"
98 if $prev_priority == $d->{priority};
99
100 $prev_priority = $d->{priority};
101
102 foreach my $sub_dev_nr (sort keys %{$d->{sub_devs}}) {
103 my $sub_dev = $d->{sub_devs}->{$sub_dev_nr};
104 my $dev_arg_str = PVE::Tools::cmd2string($sub_dev->{args});
105
106 $raw .= "\ndevice $dev_name:$sub_dev_nr $sub_dev->{agent} $dev_arg_str\n";
107
108 foreach my $node (sort keys %{$sub_dev->{node_args}}) {
109 my $node_arg_str = join (' ', @{$sub_dev->{node_args}->{$node}});
110
111 $raw .= "connect $dev_name:$sub_dev_nr node=$node $node_arg_str\n";
112 }
113 }
114 }
115
116 return $raw;
117 }
118
119
120
121 sub gen_arg_str {
122 my (@arguments) = @_;
123
124 my @shell_args = ();
125 foreach my $arg (@arguments) {
126 my ($key, $val) = split /=/, $arg;
127 # we need to differ long and short opts!
128 if (length($key) == 1) {
129 push @shell_args, "-${key}";
130 push @shell_args, PVE::Tools::shellquote($val) if defined($val);
131 } else {
132 $key .= '='. PVE::Tools::shellquote($val) if defined($val);
133 push @shell_args, "--$key";
134 }
135 }
136
137 return join (' ', @shell_args);
138 }
139
140
141 # returns command list to execute,
142 # can be more than one command if parallel devices are configured
143 # 'try' denotes the number of devices we should skip and normaly equals to
144 # failed fencing tries
145 sub get_commands {
146 my ($node, $try, $config) = @_;
147
148 return undef if !$node || !$config;
149
150 $try = 0 if !$try || $try<0;
151
152 foreach my $device (sort {$a->{priority} <=> $b->{priority}} values %$config) {
153 my @commands;
154
155 #foreach my $sub_dev (values %{$device->{sub_devs}}) {
156 foreach my $sub_dev_nr (sort keys %{$device->{sub_devs}}) {
157 my $sub_dev = $device->{sub_devs}->{$sub_dev_nr};
158
159 if (my $node_args = $sub_dev->{node_args}->{$node}) {
160 push @commands, { agent=>$sub_dev->{agent},
161 sub_dev => $sub_dev_nr,
162 param => [@{$sub_dev->{args}}, @{$node_args}]};
163 }
164
165 }
166
167 if (@commands>0) {
168 $try--;
169 return [ @commands ] if $try<0;
170 }
171 }
172
173 # out of tries or no device for this node
174 return undef;
175 }
176
177
178 sub count_devices {
179 my ($node, $config) = @_;
180
181 my $count = 0;
182
183 return 0 if !$config;
184
185 foreach my $device (values %$config) {
186 foreach my $sub_dev (values %{$device->{sub_devs}}) {
187 if ($sub_dev->{node_args}->{$node}) {
188 $count++;
189 last; # no need to count parallel devices
190 }
191 }
192 }
193
194 return $count;
195 }
196
197 1;