]>
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; |
54fc75de | 9 | |
95ea5d67 DM |
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', { | |
427e33b6 | 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).", |
95ea5d67 DM |
23 | typetext => "<type>:<name>", |
24 | type => 'string', format => 'pve-ha-resource-id', | |
25 | }); | |
26 | ||
6ca2edcd DM |
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', { | |
427e33b6 | 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).", |
6ca2edcd DM |
40 | typetext => "<type>:<name>", |
41 | type => 'string', format => 'pve-ha-resource-or-vm-id', | |
42 | }); | |
43 | ||
a223f4cc DM |
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', { | |
63f6a08c | 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').", |
a223f4cc DM |
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 | ||
b47a7a1b DM |
66 | sub parse_sid { |
67 | my ($sid) = @_; | |
68 | ||
6ca2edcd DM |
69 | my ($type, $name); |
70 | ||
71 | if ($sid =~ m/^(\d+)$/) { | |
72 | $name = $1; | |
427e33b6 AG |
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 | } | |
6ca2edcd DM |
88 | } elsif ($sid =~m/^(\S+):(\S+)$/) { |
89 | $name = $2; | |
90 | $type = $1; | |
91 | } else { | |
92 | die "unable to parse service id '$sid'\n"; | |
93 | } | |
b47a7a1b | 94 | |
6ca2edcd | 95 | return wantarray ? ($sid, $type, $name) : $sid; |
b47a7a1b DM |
96 | } |
97 | ||
54fc75de DM |
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 { | |
3b8cba06 DM |
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 | } | |
54fc75de DM |
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); | |
a223f4cc | 126 | |
54fc75de DM |
127 | PVE::Tools::file_set_contents($filename, $raw); |
128 | } | |
129 | ||
49777d09 DM |
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 | } | |
54fc75de | 149 | |
cdf272a0 TL |
150 | # bash auto completion helper |
151 | ||
152 | sub complete_sid { | |
bf119a50 | 153 | my ($cmd, $pname, $cur) = @_; |
cdf272a0 | 154 | |
bf119a50 | 155 | my $cfg = PVE::HA::Config::read_resources_config(); |
cdf272a0 TL |
156 | |
157 | my $res = []; | |
cdf272a0 | 158 | |
bf119a50 | 159 | if ($cmd eq 'add') { |
cdf272a0 | 160 | |
bf119a50 DM |
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; | |
cdf272a0 | 184 | } |
bf119a50 | 185 | } |
cdf272a0 | 186 | |
bf119a50 DM |
187 | return $res; |
188 | } | |
189 | ||
190 | sub complete_enabled_sid { | |
191 | ||
192 | my $cfg = PVE::HA::Config::read_resources_config(); | |
cdf272a0 | 193 | |
bf119a50 DM |
194 | my $res = []; |
195 | foreach my $sid (keys %{$cfg->{ids}}) { | |
196 | my $state = $cfg->{ids}->{$sid}->{state} // 'enabled'; | |
197 | next if $state ne 'enabled'; | |
cdf272a0 | 198 | push @$res, $sid; |
bf119a50 | 199 | } |
cdf272a0 | 200 | |
bf119a50 DM |
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; | |
cdf272a0 TL |
213 | } |
214 | ||
215 | return $res; | |
216 | } | |
217 | ||
218 | sub complete_group { | |
bc544941 | 219 | my ($cmd, $pname, $cur) = @_; |
cdf272a0 TL |
220 | |
221 | my $cfg = PVE::HA::Config::read_group_config(); | |
222 | ||
223 | my $res = []; | |
bc544941 TL |
224 | if ($cmd ne 'groupadd') { |
225 | ||
226 | foreach my $group (keys %{$cfg->{ids}}) { | |
227 | push @$res, $group; | |
228 | } | |
229 | ||
cdf272a0 TL |
230 | } |
231 | ||
232 | return $res; | |
233 | } | |
234 | ||
235 | ||
54fc75de | 236 | 1; |