]> git.proxmox.com Git - pve-ha-manager.git/blame - src/PVE/HA/Tools.pm
Tools/Config: refactor lrm status json reading
[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', {
8eac610f
DM
76 description => "List of cluster node names with optional priority.",
77 verbose_description => "List of cluster node members, where a priority can be given to each node. A resource bound to a group will run on the available nodes with the highest priority. If there are more nodes in the highest priority class, the services will get distributed to those nodes. The priorities have a relative meaning only.",
a223f4cc
DM
78 type => 'string', format => 'pve-ha-group-node-list',
79 typetext => '<node>[:<pri>]{,<node>[:<pri>]}*',
80});
81
82PVE::JSONSchema::register_standard_option('pve-ha-group-id', {
83 description => "The HA group identifier.",
84 type => 'string', format => 'pve-configid',
85});
86
b47a7a1b
DM
87sub parse_sid {
88 my ($sid) = @_;
89
6ca2edcd
DM
90 my ($type, $name);
91
92 if ($sid =~ m/^(\d+)$/) {
93 $name = $1;
427e33b6
AG
94 my $vmlist = PVE::Cluster::get_vmlist();
95 if (defined($vmlist->{ids}->{$name})) {
96 my $vm_type = $vmlist->{ids}->{$name}->{type};
97 if ($vm_type eq 'lxc') {
98 $type = 'ct';
99 } elsif ($vm_type eq 'qemu') {
100 $type = 'vm';
101 } else {
102 die "internal error";
103 }
104 $sid = "$type:$name";
105 }
106 else {
107 die "unable do add resource - VM/CT $1 does not exist\n";
108 }
6ca2edcd
DM
109 } elsif ($sid =~m/^(\S+):(\S+)$/) {
110 $name = $2;
111 $type = $1;
112 } else {
113 die "unable to parse service id '$sid'\n";
114 }
b47a7a1b 115
6ca2edcd 116 return wantarray ? ($sid, $type, $name) : $sid;
b47a7a1b
DM
117}
118
54fc75de
DM
119sub read_json_from_file {
120 my ($filename, $default) = @_;
121
122 my $data;
123
124 if (defined($default) && (! -f $filename)) {
125 $data = $default;
126 } else {
6529b6a4 127 my $raw = PVE::Tools::file_get_contents($filename);
54fc75de
DM
128 $data = decode_json($raw);
129 }
130
131 return $data;
132}
133
134sub write_json_to_file {
135 my ($filename, $data) = @_;
136
137 my $raw = encode_json($data);
a223f4cc 138
54fc75de
DM
139 PVE::Tools::file_set_contents($filename, $raw);
140}
141
49777d09
DM
142sub count_fenced_services {
143 my ($ss, $node) = @_;
144
145 my $count = 0;
146
147 foreach my $sid (keys %$ss) {
148 my $sd = $ss->{$sid};
149 next if !$sd->{node};
150 next if $sd->{node} ne $node;
151 my $req_state = $sd->{state};
152 next if !defined($req_state);
153 if ($req_state eq 'fence') {
154 $count++;
155 next;
156 }
157 }
158
159 return $count;
160}
54fc75de 161
aa68337a
TL
162sub get_verbose_service_state {
163 my ($service_state, $service_conf) = @_;
164
dab49a14
TL
165 my $req = $service_conf->{state} // 'ignored';
166 return 'ignored' if $req eq 'ignored';
167
aa68337a
TL
168 # service not yet processed by manager
169 return 'queued' if !defined($service_state);
170 my $cur = $service_state->{state};
171
aa68337a
TL
172 # give fast feedback to the user
173 my $state = $cur;
174 if (!defined($cur)) {
175 $state = 'queued';
176 } elsif ($cur eq 'stopped') {
177 if ($req eq 'started') {
178 $state = 'starting';
179 } elsif ($req eq 'disabled') {
180 $state = 'disabled';
181 }
182 } elsif ($cur eq 'started') {
183 if ($req eq 'stopped' || $req eq 'disabled') {
184 $state = 'stopping';
185 }
186 $state = 'starting' if !$service_state->{running};
187 } elsif ($cur eq 'error') {
188 if ($req eq 'disabled') {
189 $state = 'clearing error flag';
190 }
191 }
192
193 return $state;
194}
195
33783485
TL
196sub upid_wait {
197 my ($upid, $haenv) = @_;
198
199 my $waitfunc = sub {
200 my $task = PVE::Tools::upid_encode(shift);
201 $haenv->log('info', "Task '$task' still active, waiting");
202 };
203
204 PVE::ProcFSTools::upid_wait($upid, $waitfunc, 5);
205}
206
cdf272a0
TL
207# bash auto completion helper
208
209sub complete_sid {
bf119a50 210 my ($cmd, $pname, $cur) = @_;
cdf272a0 211
bf119a50 212 my $cfg = PVE::HA::Config::read_resources_config();
cdf272a0
TL
213
214 my $res = [];
cdf272a0 215
bf119a50 216 if ($cmd eq 'add') {
cdf272a0 217
bf119a50
DM
218 my $vmlist = PVE::Cluster::get_vmlist();
219
220 while (my ($vmid, $info) = each %{$vmlist->{ids}}) {
221
222 my $sid;
223
224 if ($info->{type} eq 'lxc') {
225 $sid = "ct:$vmid";
226 } elsif ($info->{type} eq 'qemu') {
227 $sid = "vm:$vmid";
228 } else {
229 next; # should not happen
230 }
231
232 next if $cfg->{ids}->{$sid};
233
234 push @$res, $sid;
235 }
236
237 } else {
238
239 foreach my $sid (keys %{$cfg->{ids}}) {
240 push @$res, $sid;
cdf272a0 241 }
bf119a50 242 }
cdf272a0 243
bf119a50
DM
244 return $res;
245}
246
247sub complete_enabled_sid {
248
249 my $cfg = PVE::HA::Config::read_resources_config();
cdf272a0 250
bf119a50
DM
251 my $res = [];
252 foreach my $sid (keys %{$cfg->{ids}}) {
bb07bd2c
TL
253 my $state = $cfg->{ids}->{$sid}->{state} // 'started';
254 next if $state ne 'started';
cdf272a0 255 push @$res, $sid;
bf119a50 256 }
cdf272a0 257
bf119a50
DM
258 return $res;
259}
260
261sub complete_disabled_sid {
262
263 my $cfg = PVE::HA::Config::read_resources_config();
264
265 my $res = [];
266 foreach my $sid (keys %{$cfg->{ids}}) {
bb07bd2c
TL
267 my $state = $cfg->{ids}->{$sid}->{state} // 'started';
268 next if $state eq 'started';
bf119a50 269 push @$res, $sid;
cdf272a0
TL
270 }
271
272 return $res;
273}
274
275sub complete_group {
bc544941 276 my ($cmd, $pname, $cur) = @_;
cdf272a0
TL
277
278 my $cfg = PVE::HA::Config::read_group_config();
279
280 my $res = [];
bc544941
TL
281 if ($cmd ne 'groupadd') {
282
283 foreach my $group (keys %{$cfg->{ids}}) {
284 push @$res, $group;
285 }
286
cdf272a0
TL
287 }
288
289 return $res;
290}
291
292
54fc75de 2931;