]>
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; | |
17 | ||
45e8e7b1 DM |
18 | eval { |
19 | while ($raw =~ /^\h*(.*?)\h*$/gm) { | |
20 | my $line = $1; | |
21 | $lineno++; | |
f83e7e6e | 22 | |
45e8e7b1 | 23 | next if !$line || $line =~ /^#/; |
f83e7e6e | 24 | |
45e8e7b1 DM |
25 | if ($line =~ m/^(device|connect)\s+(\S+)\s+(\S+)\s+(.+)$/) { |
26 | my ($command, $dev_name, $target) = ($1, $2, $3); | |
f83e7e6e | 27 | |
9742f408 | 28 | my $arg_array = PVE::Tools::split_args($4); |
f83e7e6e | 29 | |
45e8e7b1 | 30 | my $dev_number = 1; # default |
f83e7e6e | 31 | |
45e8e7b1 DM |
32 | # check for parallel devices |
33 | if ($dev_name =~ m/^(\w+)(:(\d+))?/) { | |
34 | $dev_name = $1; | |
35 | $dev_number = $3 if $3; | |
36 | } | |
f83e7e6e | 37 | |
45e8e7b1 | 38 | if ($command eq "device") { |
f83e7e6e | 39 | |
45e8e7b1 | 40 | my $dev = $config->{$dev_name} || {}; |
f83e7e6e | 41 | |
45e8e7b1 DM |
42 | die "device '$dev_name:$dev_number' already declared\n" |
43 | if $dev && $dev->{sub_devs}->{$dev_number}; | |
f83e7e6e | 44 | |
45e8e7b1 DM |
45 | $dev->{sub_devs}->{$dev_number} = { |
46 | agent => $target, | |
9742f408 | 47 | args => $arg_array, |
45e8e7b1 DM |
48 | }; |
49 | $dev->{priority} = $priority++ if !$dev->{priority}; | |
f83e7e6e | 50 | |
45e8e7b1 | 51 | $config->{$dev_name} = $dev; |
f83e7e6e | 52 | |
45e8e7b1 | 53 | } else { # connect nodes to devices |
f83e7e6e | 54 | |
45e8e7b1 DM |
55 | die "device '$dev_name' must be declared before you can connect to it\n" |
56 | if !$config->{$dev_name}; | |
f83e7e6e | 57 | |
45e8e7b1 DM |
58 | die "No parallel device '$dev_name:$dev_number' found\n" |
59 | if !$config->{$dev_name}->{sub_devs}->{$dev_number}; | |
f83e7e6e | 60 | |
45e8e7b1 | 61 | my $sdev = $config->{$dev_name}->{sub_devs}->{$dev_number}; |
f83e7e6e | 62 | |
45e8e7b1 DM |
63 | my ($node) = $target =~ /node=(\w+)/; |
64 | die "node=nodename needed to connect device '$dev_name' to node\n" | |
65 | if !$node; | |
f83e7e6e | 66 | |
45e8e7b1 DM |
67 | die "node '$node' already connected to device '$dev_name:$dev_number'\n" |
68 | if $sdev->{node_args}->{$node}; | |
f83e7e6e | 69 | |
9742f408 | 70 | $sdev->{node_args}->{$node} = $arg_array; |
f83e7e6e | 71 | |
45e8e7b1 DM |
72 | $config->{$dev_name}->{sub_devs}->{$dev_number} = $sdev; |
73 | } | |
f83e7e6e | 74 | |
45e8e7b1 DM |
75 | } else { |
76 | warn "$fn ignore line $lineno: $line\n" | |
f83e7e6e | 77 | } |
f83e7e6e | 78 | } |
45e8e7b1 DM |
79 | }; |
80 | if (my $err = $@) { | |
81 | die "error in '$fn' at $lineno: $err"; | |
f83e7e6e TL |
82 | } |
83 | ||
84 | return $config; | |
f83e7e6e TL |
85 | } |
86 | ||
87 | sub write_config { | |
88 | my ($fn, $data) = @_; | |
89 | ||
90 | my $raw = ''; | |
91 | ||
b6056542 TL |
92 | my $prev_priority = -1; |
93 | ||
9d99786e | 94 | foreach my $dev_name (sort {$data->{$a}->{priority} <=> $data->{$b}->{priority}} keys %$data) { |
f83e7e6e TL |
95 | my $d = $data->{$dev_name}; |
96 | ||
b6056542 TL |
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 | ||
f83e7e6e TL |
102 | foreach my $sub_dev_nr (sort keys %{$d->{sub_devs}}) { |
103 | my $sub_dev = $d->{sub_devs}->{$sub_dev_nr}; | |
9d99786e | 104 | my $dev_arg_str = PVE::Tools::cmd2string($sub_dev->{args}); |
f83e7e6e TL |
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 | ||
9d99786e | 111 | $raw .= "connect $dev_name:$sub_dev_nr node=$node $node_arg_str\n"; |
f83e7e6e TL |
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) { | |
9742f408 | 126 | my ($key, $val) = split /=/, $arg; |
f83e7e6e | 127 | # we need to differ long and short opts! |
7488b3cc TL |
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 | } | |
f83e7e6e TL |
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; |