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