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',
87 sub read_json_from_file
{
88 my ($filename, $default) = @_;
92 if (defined($default) && (! -f
$filename)) {
95 my $raw = PVE
::Tools
::file_get_contents
($filename);
96 $data = decode_json
($raw);
102 sub write_json_to_file
{
103 my ($filename, $data) = @_;
105 my $raw = encode_json
($data);
107 PVE
::Tools
::file_set_contents
($filename, $raw);
110 sub count_fenced_services
{
111 my ($ss, $node) = @_;
115 foreach my $sid (keys %$ss) {
116 my $sd = $ss->{$sid};
117 next if !$sd->{node
};
118 next if $sd->{node
} ne $node;
119 my $req_state = $sd->{state};
120 next if !defined($req_state);
121 if ($req_state eq 'fence') {
130 sub get_verbose_service_state
{
131 my ($service_state, $service_conf) = @_;
133 my $req = $service_conf->{state} // 'ignored';
134 return 'ignored' if $req eq 'ignored';
136 return 'not found' if !defined($service_conf->{node
});
138 # service not yet processed by manager
139 return 'queued' if !defined($service_state);
140 my $cur = $service_state->{state};
142 # give fast feedback to the user
144 if (!defined($cur)) {
146 } elsif ($cur eq 'stopped') {
147 if ($req eq 'started') {
149 } elsif ($req eq 'disabled') {
152 } elsif ($cur eq 'started') {
153 if ($req eq 'stopped' || $req eq 'disabled') {
156 $state = 'starting' if !$service_state->{running
};
157 } elsif ($cur eq 'error') {
158 if ($req eq 'disabled') {
159 $state = 'clearing error flag';
167 my ($upid, $haenv) = @_;
170 my $task = PVE
::Tools
::upid_encode
(shift);
171 $haenv->log('info', "Task '$task' still active, waiting");
174 PVE
::ProcFSTools
::upid_wait
($upid, $waitfunc, 5);
177 # bash auto completion helper
179 # NOTE: we use PVE::HA::Config here without declaring an 'use' clause above as
180 # an hack. It uses the PVE::Cluster module from pve-cluster, which we do not
181 # have nor want as dependency in the simulator - where the completion helpers
182 # are never called. The PVE::CLI::ha_manager package pulls it in for us.
185 my ($cmd, $pname, $cur) = @_;
187 my $cfg = PVE
::HA
::Config
::read_resources_config
();
193 my $vmlist = PVE
::Cluster
::get_vmlist
();
195 while (my ($vmid, $info) = each %{$vmlist->{ids
}}) {
199 if ($info->{type
} eq 'lxc') {
201 } elsif ($info->{type
} eq 'qemu') {
204 next; # should not happen
207 next if $cfg->{ids
}->{$sid};
214 foreach my $sid (keys %{$cfg->{ids
}}) {
222 sub complete_enabled_sid
{
223 my $cfg = PVE
::HA
::Config
::read_resources_config
();
226 foreach my $sid (keys %{$cfg->{ids
}}) {
227 my $state = $cfg->{ids
}->{$sid}->{state} // 'started';
228 next if $state ne 'started';
235 sub complete_disabled_sid
{
236 my $cfg = PVE
::HA
::Config
::read_resources_config
();
239 foreach my $sid (keys %{$cfg->{ids
}}) {
240 my $state = $cfg->{ids
}->{$sid}->{state} // 'started';
241 next if $state eq 'started';
249 my ($cmd, $pname, $cur) = @_;
251 my $cfg = PVE
::HA
::Config
::read_group_config
();
254 if ($cmd ne 'groupadd') {
256 foreach my $group (keys %{$cfg->{ids
}}) {