1 package PVE
::HA
::Tools
;
11 # return codes used in the ha environment
12 # mainly by the resource agents
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 EUNKNOWN_SERVICE
=> 7, # service not found
24 # get constants out of package in a somewhat easy way
26 our @EXPORT_OK = qw(SUCCESS ERROR EWRONG_NODE EUNKNOWN_SERVICE_TYPE
27 EUNKNOWN_COMMAND EINVALID_PARAMETER ETRY_AGAIN EUNKNOWN_SERVICE);
28 our %EXPORT_TAGS = ( 'exit_codes' => [@EXPORT_OK] );
30 PVE
::JSONSchema
::register_format
('pve-ha-resource-id', \
&pve_verify_ha_resource_id
);
31 sub pve_verify_ha_resource_id
{
32 my ($sid, $noerr) = @_;
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";
41 PVE
::JSONSchema
::register_standard_option
('pve-ha-resource-id', {
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).",
43 typetext
=> "<type>:<name>",
44 type
=> 'string', format
=> 'pve-ha-resource-id',
47 PVE
::JSONSchema
::register_format
('pve-ha-resource-or-vm-id', \
&pve_verify_ha_resource_or_vm_id
);
48 sub pve_verify_ha_resource_or_vm_id
{
49 my ($sid, $noerr) = @_;
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";
58 PVE
::JSONSchema
::register_standard_option
('pve-ha-resource-or-vm-id', {
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).",
60 typetext
=> "<type>:<name>",
61 type
=> 'string', format
=> 'pve-ha-resource-or-vm-id',
64 PVE
::JSONSchema
::register_format
('pve-ha-group-node', \
&pve_verify_ha_group_node
);
65 sub pve_verify_ha_group_node
{
66 my ($node, $noerr) = @_;
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";
75 PVE
::JSONSchema
::register_standard_option
('pve-ha-group-node-list', {
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.",
78 type
=> 'string', format
=> 'pve-ha-group-node-list',
79 typetext
=> '<node>[:<pri>]{,<node>[:<pri>]}*',
82 PVE
::JSONSchema
::register_standard_option
('pve-ha-group-id', {
83 description
=> "The HA group identifier.",
84 type
=> 'string', format
=> 'pve-configid',
92 if ($sid =~ m/^(\d+)$/) {
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') {
99 } elsif ($vm_type eq 'qemu') {
102 die "internal error";
104 $sid = "$type:$name";
107 die "unable do add resource - VM/CT $1 does not exist\n";
109 } elsif ($sid =~m/^(\S+):(\S+)$/) {
113 die "unable to parse service id '$sid'\n";
116 return wantarray ?
($sid, $type, $name) : $sid;
119 sub read_json_from_file
{
120 my ($filename, $default) = @_;
124 if (defined($default) && (! -f
$filename)) {
128 # workaround for bug #775
129 if ($filename =~ m
|^/etc/pve
/|) {
130 $filename =~ s
|^/etc/pve
/+||;
131 $raw = PVE
::Cluster
::get_config
($filename);
132 die "unable to read file '/etc/pve/$filename'\n"
135 $raw = PVE
::Tools
::file_get_contents
($filename);
137 $data = decode_json
($raw);
143 sub write_json_to_file
{
144 my ($filename, $data) = @_;
146 my $raw = encode_json
($data);
148 PVE
::Tools
::file_set_contents
($filename, $raw);
151 sub count_fenced_services
{
152 my ($ss, $node) = @_;
156 foreach my $sid (keys %$ss) {
157 my $sd = $ss->{$sid};
158 next if !$sd->{node
};
159 next if $sd->{node
} ne $node;
160 my $req_state = $sd->{state};
161 next if !defined($req_state);
162 if ($req_state eq 'fence') {
171 sub get_verbose_service_state
{
172 my ($service_state, $service_conf) = @_;
174 my $req = $service_conf->{state} // 'ignored';
175 return 'ignored' if $req eq 'ignored';
177 # service not yet processed by manager
178 return 'queued' if !defined($service_state);
179 my $cur = $service_state->{state};
181 # give fast feedback to the user
183 if (!defined($cur)) {
185 } elsif ($cur eq 'stopped') {
186 if ($req eq 'started') {
188 } elsif ($req eq 'disabled') {
191 } elsif ($cur eq 'started') {
192 if ($req eq 'stopped' || $req eq 'disabled') {
195 $state = 'starting' if !$service_state->{running
};
196 } elsif ($cur eq 'error') {
197 if ($req eq 'disabled') {
198 $state = 'clearing error flag';
206 my ($upid, $haenv) = @_;
209 my $task = PVE
::Tools
::upid_encode
(shift);
210 $haenv->log('info', "Task '$task' still active, waiting");
213 PVE
::ProcFSTools
::upid_wait
($upid, $waitfunc, 5);
216 # bash auto completion helper
219 my ($cmd, $pname, $cur) = @_;
221 my $cfg = PVE
::HA
::Config
::read_resources_config
();
227 my $vmlist = PVE
::Cluster
::get_vmlist
();
229 while (my ($vmid, $info) = each %{$vmlist->{ids
}}) {
233 if ($info->{type
} eq 'lxc') {
235 } elsif ($info->{type
} eq 'qemu') {
238 next; # should not happen
241 next if $cfg->{ids
}->{$sid};
248 foreach my $sid (keys %{$cfg->{ids
}}) {
256 sub complete_enabled_sid
{
258 my $cfg = PVE
::HA
::Config
::read_resources_config
();
261 foreach my $sid (keys %{$cfg->{ids
}}) {
262 my $state = $cfg->{ids
}->{$sid}->{state} // 'started';
263 next if $state ne 'started';
270 sub complete_disabled_sid
{
272 my $cfg = PVE
::HA
::Config
::read_resources_config
();
275 foreach my $sid (keys %{$cfg->{ids
}}) {
276 my $state = $cfg->{ids
}->{$sid}->{state} // 'started';
277 next if $state eq 'started';
285 my ($cmd, $pname, $cur) = @_;
287 my $cfg = PVE
::HA
::Config
::read_group_config
();
290 if ($cmd ne 'groupadd') {
292 foreach my $group (keys %{$cfg->{ids
}}) {