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