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