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);
152 my ($haenv, $node) = @_;
154 my $conf = $haenv->read_service_config();
156 # if no node defined any service count is fine
157 return scalar(%{$conf}) if !defined($node);
159 foreach my $d (values %$conf) {
160 return 1 if $d->{node
} eq $node;
166 sub count_fenced_services
{
167 my ($ss, $node) = @_;
171 foreach my $sid (keys %$ss) {
172 my $sd = $ss->{$sid};
173 next if !$sd->{node
};
174 next if $sd->{node
} ne $node;
175 my $req_state = $sd->{state};
176 next if !defined($req_state);
177 if ($req_state eq 'fence') {
187 my ($upid, $haenv) = @_;
190 my $task = PVE
::Tools
::upid_encode
(shift);
191 $haenv->log('info', "Task '$task' still active, waiting");
194 PVE
::ProcFSTools
::upid_wait
($upid, $waitfunc, 5);
197 # bash auto completion helper
200 my ($cmd, $pname, $cur) = @_;
202 my $cfg = PVE
::HA
::Config
::read_resources_config
();
208 my $vmlist = PVE
::Cluster
::get_vmlist
();
210 while (my ($vmid, $info) = each %{$vmlist->{ids
}}) {
214 if ($info->{type
} eq 'lxc') {
216 } elsif ($info->{type
} eq 'qemu') {
219 next; # should not happen
222 next if $cfg->{ids
}->{$sid};
229 foreach my $sid (keys %{$cfg->{ids
}}) {
237 sub complete_enabled_sid
{
239 my $cfg = PVE
::HA
::Config
::read_resources_config
();
242 foreach my $sid (keys %{$cfg->{ids
}}) {
243 my $state = $cfg->{ids
}->{$sid}->{state} // 'started';
244 next if $state ne 'started';
251 sub complete_disabled_sid
{
253 my $cfg = PVE
::HA
::Config
::read_resources_config
();
256 foreach my $sid (keys %{$cfg->{ids
}}) {
257 my $state = $cfg->{ids
}->{$sid}->{state} // 'started';
258 next if $state eq 'started';
266 my ($cmd, $pname, $cur) = @_;
268 my $cfg = PVE
::HA
::Config
::read_group_config
();
271 if ($cmd ne 'groupadd') {
273 foreach my $group (keys %{$cfg->{ids
}}) {