]> git.proxmox.com Git - pve-ha-manager.git/blame - src/PVE/HA/Tools.pm
service_check_ha_state: improve backward compatibility
[pve-ha-manager.git] / src / PVE / HA / Tools.pm
CommitLineData
54fc75de
DM
1package PVE::HA::Tools;
2
3use strict;
4use warnings;
a223f4cc
DM
5use JSON;
6use PVE::JSONSchema;
54fc75de 7use PVE::Tools;
427e33b6 8use PVE::Cluster;
33783485 9use PVE::ProcFSTools;
54fc75de 10
80ec7386
TL
11# return codes used in the ha environment
12# mainly by the resource agents
13use 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
25use base 'Exporter';
26our @EXPORT_OK = qw(SUCCESS ERROR EWRONG_NODE EUNKNOWN_SERVICE_TYPE
aaabde6a 27 EUNKNOWN_COMMAND EINVALID_PARAMETER ETRY_AGAIN EUNKNOWN_SERVICE);
80ec7386
TL
28our %EXPORT_TAGS = ( 'exit_codes' => [@EXPORT_OK] );
29
95ea5d67
DM
30PVE::JSONSchema::register_format('pve-ha-resource-id', \&pve_verify_ha_resource_id);
31sub 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
41PVE::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
47PVE::JSONSchema::register_format('pve-ha-resource-or-vm-id', \&pve_verify_ha_resource_or_vm_id);
48sub 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
58PVE::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
64PVE::JSONSchema::register_format('pve-ha-group-node', \&pve_verify_ha_group_node);
65sub 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
75PVE::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
81PVE::JSONSchema::register_standard_option('pve-ha-group-id', {
82 description => "The HA group identifier.",
83 type => 'string', format => 'pve-configid',
84});
85
b47a7a1b
DM
86sub 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
118sub 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
142sub 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
150sub 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
165sub 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
185sub 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
198sub 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
236sub 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
250sub 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
264sub 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 2821;