]>
Commit | Line | Data |
---|---|---|
54fc75de DM |
1 | package PVE::HA::Tools; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
a223f4cc DM |
5 | use JSON; |
6 | use PVE::JSONSchema; | |
54fc75de | 7 | use PVE::Tools; |
427e33b6 | 8 | use PVE::Cluster; |
33783485 | 9 | use PVE::ProcFSTools; |
54fc75de | 10 | |
80ec7386 TL |
11 | # return codes used in the ha environment |
12 | # mainly by the resource agents | |
13 | use constant { | |
14 | SUCCESS => 0, # action finished as expected | |
15 | ERROR => 1, # action was erroneous | |
16 | ETRY_AGAIN => 2, # action was erroneous and needs to be repeated | |
17 | EWRONG_NODE => 3, # needs to fixup the service location | |
18 | EUNKNOWN_SERVICE_TYPE => 4, # no plugin for this type service found | |
19 | EUNKNOWN_COMMAND => 5, | |
20 | EINVALID_PARAMETER => 6, | |
aaabde6a | 21 | EUNKNOWN_SERVICE => 7, # service not found |
80ec7386 TL |
22 | }; |
23 | ||
24 | # get constants out of package in a somewhat easy way | |
25 | use base 'Exporter'; | |
26 | our @EXPORT_OK = qw(SUCCESS ERROR EWRONG_NODE EUNKNOWN_SERVICE_TYPE | |
aaabde6a | 27 | EUNKNOWN_COMMAND EINVALID_PARAMETER ETRY_AGAIN EUNKNOWN_SERVICE); |
80ec7386 TL |
28 | our %EXPORT_TAGS = ( 'exit_codes' => [@EXPORT_OK] ); |
29 | ||
95ea5d67 DM |
30 | PVE::JSONSchema::register_format('pve-ha-resource-id', \&pve_verify_ha_resource_id); |
31 | sub pve_verify_ha_resource_id { | |
32 | my ($sid, $noerr) = @_; | |
33 | ||
34 | if ($sid !~ m/^[a-z]+:\S+$/) { | |
35 | return undef if $noerr; | |
36 | die "value does not look like a valid ha resource id\n"; | |
37 | } | |
38 | return $sid; | |
39 | } | |
40 | ||
41 | PVE::JSONSchema::register_standard_option('pve-ha-resource-id', { | |
427e33b6 | 42 | description => "HA resource ID. This consists of a resource type followed by a resource specific name, separated with colon (example: vm:100 / ct:100).", |
95ea5d67 DM |
43 | typetext => "<type>:<name>", |
44 | type => 'string', format => 'pve-ha-resource-id', | |
45 | }); | |
46 | ||
6ca2edcd DM |
47 | PVE::JSONSchema::register_format('pve-ha-resource-or-vm-id', \&pve_verify_ha_resource_or_vm_id); |
48 | sub pve_verify_ha_resource_or_vm_id { | |
49 | my ($sid, $noerr) = @_; | |
50 | ||
51 | if ($sid !~ m/^([a-z]+:\S+|\d+)$/) { | |
52 | return undef if $noerr; | |
53 | die "value does not look like a valid ha resource id\n"; | |
54 | } | |
55 | return $sid; | |
56 | } | |
57 | ||
58 | PVE::JSONSchema::register_standard_option('pve-ha-resource-or-vm-id', { | |
427e33b6 | 59 | description => "HA resource ID. This consists of a resource type followed by a resource specific name, separated with colon (example: vm:100 / ct:100). For virtual machines and containers, you can simply use the VM or CT id as a shortcut (example: 100).", |
6ca2edcd DM |
60 | typetext => "<type>:<name>", |
61 | type => 'string', format => 'pve-ha-resource-or-vm-id', | |
62 | }); | |
63 | ||
a223f4cc DM |
64 | PVE::JSONSchema::register_format('pve-ha-group-node', \&pve_verify_ha_group_node); |
65 | sub pve_verify_ha_group_node { | |
66 | my ($node, $noerr) = @_; | |
67 | ||
68 | if ($node !~ m/^([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)(:\d+)?$/) { | |
69 | return undef if $noerr; | |
70 | die "value does not look like a valid ha group node\n"; | |
71 | } | |
72 | return $node; | |
73 | } | |
74 | ||
75 | PVE::JSONSchema::register_standard_option('pve-ha-group-node-list', { | |
63f6a08c | 76 | description => "List of cluster node names with optional priority. We use priority '0' as default. The CRM tries to run services on the node with highest priority (also see option 'nofailback').", |
a223f4cc DM |
77 | type => 'string', format => 'pve-ha-group-node-list', |
78 | typetext => '<node>[:<pri>]{,<node>[:<pri>]}*', | |
79 | }); | |
80 | ||
81 | PVE::JSONSchema::register_standard_option('pve-ha-group-id', { | |
82 | description => "The HA group identifier.", | |
83 | type => 'string', format => 'pve-configid', | |
84 | }); | |
85 | ||
b47a7a1b DM |
86 | sub parse_sid { |
87 | my ($sid) = @_; | |
88 | ||
6ca2edcd DM |
89 | my ($type, $name); |
90 | ||
91 | if ($sid =~ m/^(\d+)$/) { | |
92 | $name = $1; | |
427e33b6 AG |
93 | my $vmlist = PVE::Cluster::get_vmlist(); |
94 | if (defined($vmlist->{ids}->{$name})) { | |
95 | my $vm_type = $vmlist->{ids}->{$name}->{type}; | |
96 | if ($vm_type eq 'lxc') { | |
97 | $type = 'ct'; | |
98 | } elsif ($vm_type eq 'qemu') { | |
99 | $type = 'vm'; | |
100 | } else { | |
101 | die "internal error"; | |
102 | } | |
103 | $sid = "$type:$name"; | |
104 | } | |
105 | else { | |
106 | die "unable do add resource - VM/CT $1 does not exist\n"; | |
107 | } | |
6ca2edcd DM |
108 | } elsif ($sid =~m/^(\S+):(\S+)$/) { |
109 | $name = $2; | |
110 | $type = $1; | |
111 | } else { | |
112 | die "unable to parse service id '$sid'\n"; | |
113 | } | |
b47a7a1b | 114 | |
6ca2edcd | 115 | return wantarray ? ($sid, $type, $name) : $sid; |
b47a7a1b DM |
116 | } |
117 | ||
54fc75de DM |
118 | sub read_json_from_file { |
119 | my ($filename, $default) = @_; | |
120 | ||
121 | my $data; | |
122 | ||
123 | if (defined($default) && (! -f $filename)) { | |
124 | $data = $default; | |
125 | } else { | |
3b8cba06 DM |
126 | my $raw; |
127 | # workaround for bug #775 | |
128 | if ($filename =~ m|^/etc/pve/|) { | |
129 | $filename =~ s|^/etc/pve/+||; | |
130 | $raw = PVE::Cluster::get_config($filename); | |
131 | die "unable to read file '/etc/pve/$filename'\n" | |
132 | if !defined($raw); | |
133 | } else { | |
134 | $raw = PVE::Tools::file_get_contents($filename); | |
135 | } | |
54fc75de DM |
136 | $data = decode_json($raw); |
137 | } | |
138 | ||
139 | return $data; | |
140 | } | |
141 | ||
142 | sub write_json_to_file { | |
143 | my ($filename, $data) = @_; | |
144 | ||
145 | my $raw = encode_json($data); | |
a223f4cc | 146 | |
54fc75de DM |
147 | PVE::Tools::file_set_contents($filename, $raw); |
148 | } | |
149 | ||
dab485b3 TL |
150 | sub has_services { |
151 | my ($haenv, $node) = @_; | |
152 | ||
dab485b3 TL |
153 | my $conf = $haenv->read_service_config(); |
154 | ||
155 | # if no node defined any service count is fine | |
ff8e2945 | 156 | return scalar(%{$conf}) if !defined($node); |
dab485b3 TL |
157 | |
158 | foreach my $d (values %$conf) { | |
159 | return 1 if $d->{node} eq $node; | |
160 | } | |
161 | ||
162 | return undef; | |
163 | } | |
164 | ||
49777d09 DM |
165 | sub count_fenced_services { |
166 | my ($ss, $node) = @_; | |
167 | ||
168 | my $count = 0; | |
169 | ||
170 | foreach my $sid (keys %$ss) { | |
171 | my $sd = $ss->{$sid}; | |
172 | next if !$sd->{node}; | |
173 | next if $sd->{node} ne $node; | |
174 | my $req_state = $sd->{state}; | |
175 | next if !defined($req_state); | |
176 | if ($req_state eq 'fence') { | |
177 | $count++; | |
178 | next; | |
179 | } | |
180 | } | |
181 | ||
182 | return $count; | |
183 | } | |
54fc75de | 184 | |
33783485 TL |
185 | sub upid_wait { |
186 | my ($upid, $haenv) = @_; | |
187 | ||
188 | my $waitfunc = sub { | |
189 | my $task = PVE::Tools::upid_encode(shift); | |
190 | $haenv->log('info', "Task '$task' still active, waiting"); | |
191 | }; | |
192 | ||
193 | PVE::ProcFSTools::upid_wait($upid, $waitfunc, 5); | |
194 | } | |
195 | ||
cdf272a0 TL |
196 | # bash auto completion helper |
197 | ||
198 | sub complete_sid { | |
bf119a50 | 199 | my ($cmd, $pname, $cur) = @_; |
cdf272a0 | 200 | |
bf119a50 | 201 | my $cfg = PVE::HA::Config::read_resources_config(); |
cdf272a0 TL |
202 | |
203 | my $res = []; | |
cdf272a0 | 204 | |
bf119a50 | 205 | if ($cmd eq 'add') { |
cdf272a0 | 206 | |
bf119a50 DM |
207 | my $vmlist = PVE::Cluster::get_vmlist(); |
208 | ||
209 | while (my ($vmid, $info) = each %{$vmlist->{ids}}) { | |
210 | ||
211 | my $sid; | |
212 | ||
213 | if ($info->{type} eq 'lxc') { | |
214 | $sid = "ct:$vmid"; | |
215 | } elsif ($info->{type} eq 'qemu') { | |
216 | $sid = "vm:$vmid"; | |
217 | } else { | |
218 | next; # should not happen | |
219 | } | |
220 | ||
221 | next if $cfg->{ids}->{$sid}; | |
222 | ||
223 | push @$res, $sid; | |
224 | } | |
225 | ||
226 | } else { | |
227 | ||
228 | foreach my $sid (keys %{$cfg->{ids}}) { | |
229 | push @$res, $sid; | |
cdf272a0 | 230 | } |
bf119a50 | 231 | } |
cdf272a0 | 232 | |
bf119a50 DM |
233 | return $res; |
234 | } | |
235 | ||
236 | sub complete_enabled_sid { | |
237 | ||
238 | my $cfg = PVE::HA::Config::read_resources_config(); | |
cdf272a0 | 239 | |
bf119a50 DM |
240 | my $res = []; |
241 | foreach my $sid (keys %{$cfg->{ids}}) { | |
bb07bd2c TL |
242 | my $state = $cfg->{ids}->{$sid}->{state} // 'started'; |
243 | next if $state ne 'started'; | |
cdf272a0 | 244 | push @$res, $sid; |
bf119a50 | 245 | } |
cdf272a0 | 246 | |
bf119a50 DM |
247 | return $res; |
248 | } | |
249 | ||
250 | sub complete_disabled_sid { | |
251 | ||
252 | my $cfg = PVE::HA::Config::read_resources_config(); | |
253 | ||
254 | my $res = []; | |
255 | foreach my $sid (keys %{$cfg->{ids}}) { | |
bb07bd2c TL |
256 | my $state = $cfg->{ids}->{$sid}->{state} // 'started'; |
257 | next if $state eq 'started'; | |
bf119a50 | 258 | push @$res, $sid; |
cdf272a0 TL |
259 | } |
260 | ||
261 | return $res; | |
262 | } | |
263 | ||
264 | sub complete_group { | |
bc544941 | 265 | my ($cmd, $pname, $cur) = @_; |
cdf272a0 TL |
266 | |
267 | my $cfg = PVE::HA::Config::read_group_config(); | |
268 | ||
269 | my $res = []; | |
bc544941 TL |
270 | if ($cmd ne 'groupadd') { |
271 | ||
272 | foreach my $group (keys %{$cfg->{ids}}) { | |
273 | push @$res, $group; | |
274 | } | |
275 | ||
cdf272a0 TL |
276 | } |
277 | ||
278 | return $res; | |
279 | } | |
280 | ||
281 | ||
54fc75de | 282 | 1; |