]> git.proxmox.com Git - pve-ha-manager.git/blob - src/PVE/HA/Tools.pm
add workaround for bug #775
[pve-ha-manager.git] / src / PVE / HA / Tools.pm
1 package PVE::HA::Tools;
2
3 use strict;
4 use warnings;
5 use JSON;
6 use PVE::JSONSchema;
7 use PVE::Tools;
8 use PVE::Cluster;
9
10 PVE::JSONSchema::register_format('pve-ha-resource-id', \&pve_verify_ha_resource_id);
11 sub pve_verify_ha_resource_id {
12 my ($sid, $noerr) = @_;
13
14 if ($sid !~ m/^[a-z]+:\S+$/) {
15 return undef if $noerr;
16 die "value does not look like a valid ha resource id\n";
17 }
18 return $sid;
19 }
20
21 PVE::JSONSchema::register_standard_option('pve-ha-resource-id', {
22 description => "HA resource ID. This consists of a resource type followed by a resource specific name, separated with colon (example: vm:100 / ct:100).",
23 typetext => "<type>:<name>",
24 type => 'string', format => 'pve-ha-resource-id',
25 });
26
27 PVE::JSONSchema::register_format('pve-ha-resource-or-vm-id', \&pve_verify_ha_resource_or_vm_id);
28 sub pve_verify_ha_resource_or_vm_id {
29 my ($sid, $noerr) = @_;
30
31 if ($sid !~ m/^([a-z]+:\S+|\d+)$/) {
32 return undef if $noerr;
33 die "value does not look like a valid ha resource id\n";
34 }
35 return $sid;
36 }
37
38 PVE::JSONSchema::register_standard_option('pve-ha-resource-or-vm-id', {
39 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).",
40 typetext => "<type>:<name>",
41 type => 'string', format => 'pve-ha-resource-or-vm-id',
42 });
43
44 PVE::JSONSchema::register_format('pve-ha-group-node', \&pve_verify_ha_group_node);
45 sub pve_verify_ha_group_node {
46 my ($node, $noerr) = @_;
47
48 if ($node !~ m/^([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)(:\d+)?$/) {
49 return undef if $noerr;
50 die "value does not look like a valid ha group node\n";
51 }
52 return $node;
53 }
54
55 PVE::JSONSchema::register_standard_option('pve-ha-group-node-list', {
56 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').",
57 type => 'string', format => 'pve-ha-group-node-list',
58 typetext => '<node>[:<pri>]{,<node>[:<pri>]}*',
59 });
60
61 PVE::JSONSchema::register_standard_option('pve-ha-group-id', {
62 description => "The HA group identifier.",
63 type => 'string', format => 'pve-configid',
64 });
65
66 sub parse_sid {
67 my ($sid) = @_;
68
69 my ($type, $name);
70
71 if ($sid =~ m/^(\d+)$/) {
72 $name = $1;
73 my $vmlist = PVE::Cluster::get_vmlist();
74 if (defined($vmlist->{ids}->{$name})) {
75 my $vm_type = $vmlist->{ids}->{$name}->{type};
76 if ($vm_type eq 'lxc') {
77 $type = 'ct';
78 } elsif ($vm_type eq 'qemu') {
79 $type = 'vm';
80 } else {
81 die "internal error";
82 }
83 $sid = "$type:$name";
84 }
85 else {
86 die "unable do add resource - VM/CT $1 does not exist\n";
87 }
88 } elsif ($sid =~m/^(\S+):(\S+)$/) {
89 $name = $2;
90 $type = $1;
91 } else {
92 die "unable to parse service id '$sid'\n";
93 }
94
95 return wantarray ? ($sid, $type, $name) : $sid;
96 }
97
98 sub read_json_from_file {
99 my ($filename, $default) = @_;
100
101 my $data;
102
103 if (defined($default) && (! -f $filename)) {
104 $data = $default;
105 } else {
106 my $raw;
107 # workaround for bug #775
108 if ($filename =~ m|^/etc/pve/|) {
109 $filename =~ s|^/etc/pve/+||;
110 $raw = PVE::Cluster::get_config($filename);
111 die "unable to read file '/etc/pve/$filename'\n"
112 if !defined($raw);
113 } else {
114 $raw = PVE::Tools::file_get_contents($filename);
115 }
116 $data = decode_json($raw);
117 }
118
119 return $data;
120 }
121
122 sub write_json_to_file {
123 my ($filename, $data) = @_;
124
125 my $raw = encode_json($data);
126
127 PVE::Tools::file_set_contents($filename, $raw);
128 }
129
130 sub count_fenced_services {
131 my ($ss, $node) = @_;
132
133 my $count = 0;
134
135 foreach my $sid (keys %$ss) {
136 my $sd = $ss->{$sid};
137 next if !$sd->{node};
138 next if $sd->{node} ne $node;
139 my $req_state = $sd->{state};
140 next if !defined($req_state);
141 if ($req_state eq 'fence') {
142 $count++;
143 next;
144 }
145 }
146
147 return $count;
148 }
149
150 # bash auto completion helper
151
152 sub complete_sid {
153 my ($cmd, $pname, $cur) = @_;
154
155 my $cfg = PVE::HA::Config::read_resources_config();
156
157 my $res = [];
158
159 if ($cmd eq 'add') {
160
161 my $vmlist = PVE::Cluster::get_vmlist();
162
163 while (my ($vmid, $info) = each %{$vmlist->{ids}}) {
164
165 my $sid;
166
167 if ($info->{type} eq 'lxc') {
168 $sid = "ct:$vmid";
169 } elsif ($info->{type} eq 'qemu') {
170 $sid = "vm:$vmid";
171 } else {
172 next; # should not happen
173 }
174
175 next if $cfg->{ids}->{$sid};
176
177 push @$res, $sid;
178 }
179
180 } else {
181
182 foreach my $sid (keys %{$cfg->{ids}}) {
183 push @$res, $sid;
184 }
185 }
186
187 return $res;
188 }
189
190 sub complete_enabled_sid {
191
192 my $cfg = PVE::HA::Config::read_resources_config();
193
194 my $res = [];
195 foreach my $sid (keys %{$cfg->{ids}}) {
196 my $state = $cfg->{ids}->{$sid}->{state} // 'enabled';
197 next if $state ne 'enabled';
198 push @$res, $sid;
199 }
200
201 return $res;
202 }
203
204 sub complete_disabled_sid {
205
206 my $cfg = PVE::HA::Config::read_resources_config();
207
208 my $res = [];
209 foreach my $sid (keys %{$cfg->{ids}}) {
210 my $state = $cfg->{ids}->{$sid}->{state} // 'enabled';
211 next if $state eq 'enabled';
212 push @$res, $sid;
213 }
214
215 return $res;
216 }
217
218 sub complete_group {
219 my ($cmd, $pname, $cur) = @_;
220
221 my $cfg = PVE::HA::Config::read_group_config();
222
223 my $res = [];
224 if ($cmd ne 'groupadd') {
225
226 foreach my $group (keys %{$cfg->{ids}}) {
227 push @$res, $group;
228 }
229
230 }
231
232 return $res;
233 }
234
235
236 1;