]> git.proxmox.com Git - qemu-server.git/blame - PVE/QemuServer/Helpers.pm
schema: fix description of migrate_downtime parameter
[qemu-server.git] / PVE / QemuServer / Helpers.pm
CommitLineData
d036e418
SR
1package PVE::QemuServer::Helpers;
2
3use strict;
4use warnings;
5
babf613a 6use File::stat;
9c88e854 7use JSON;
babf613a 8
d036e418 9use PVE::INotify;
babf613a 10use PVE::ProcFSTools;
d036e418 11
2ea5fb7e
SR
12use base 'Exporter';
13our @EXPORT_OK = qw(
14min_version
2f18c84d 15config_aware_timeout
261b67e4 16parse_number_sets
238af88e 17windows_version
2ea5fb7e
SR
18);
19
d036e418
SR
20my $nodename = PVE::INotify::nodename();
21
22# Paths and directories
23
24our $var_run_tmpdir = "/var/run/qemu-server";
25mkdir $var_run_tmpdir;
26
27sub qmp_socket {
28 my ($vmid, $qga) = @_;
29 my $sockettype = $qga ? 'qga' : 'qmp';
30 return "${var_run_tmpdir}/$vmid.$sockettype";
31}
32
33sub pidfile_name {
34 my ($vmid) = @_;
35 return "${var_run_tmpdir}/$vmid.pid";
36}
37
38sub vnc_socket {
39 my ($vmid) = @_;
40 return "${var_run_tmpdir}/$vmid.vnc";
41}
42
babf613a
SR
43# Parse the cmdline of a running kvm/qemu process and return arguments as hash
44sub parse_cmdline {
45 my ($pid) = @_;
46
47 my $fh = IO::File->new("/proc/$pid/cmdline", "r");
48 if (defined($fh)) {
49 my $line = <$fh>;
50 $fh->close;
d1c1af4b 51 return if !$line;
babf613a
SR
52 my @param = split(/\0/, $line);
53
54 my $cmd = $param[0];
55 return if !$cmd || ($cmd !~ m|kvm$| && $cmd !~ m@(?:^|/)qemu-system-[^/]+$@);
56
57 my $phash = {};
58 my $pending_cmd;
59 for (my $i = 0; $i < scalar (@param); $i++) {
60 my $p = $param[$i];
61 next if !$p;
62
63 if ($p =~ m/^--?(.*)$/) {
64 if ($pending_cmd) {
65 $phash->{$pending_cmd} = {};
66 }
67 $pending_cmd = $1;
68 } elsif ($pending_cmd) {
69 $phash->{$pending_cmd} = { value => $p };
70 $pending_cmd = undef;
71 }
72 }
73
74 return $phash;
75 }
d1c1af4b 76 return;
babf613a
SR
77}
78
79sub vm_running_locally {
80 my ($vmid) = @_;
81
82 my $pidfile = pidfile_name($vmid);
83
84 if (my $fd = IO::File->new("<$pidfile")) {
85 my $st = stat($fd);
86 my $line = <$fd>;
87 close($fd);
88
89 my $mtime = $st->mtime;
90 if ($mtime > time()) {
91 warn "file '$pidfile' modified in future\n";
92 }
93
94 if ($line =~ m/^(\d+)$/) {
95 my $pid = $1;
96 my $cmdline = parse_cmdline($pid);
97 if ($cmdline && defined($cmdline->{pidfile}) && $cmdline->{pidfile}->{value}
98 && $cmdline->{pidfile}->{value} eq $pidfile) {
99 if (my $pinfo = PVE::ProcFSTools::check_process_running($pid)) {
100 return $pid;
101 }
102 }
103 }
104 }
105
d1c1af4b 106 return;
babf613a
SR
107}
108
2ea5fb7e 109sub min_version {
9471e48b 110 my ($verstr, $major, $minor, $pve) = @_;
2ea5fb7e 111
9471e48b
TL
112 if ($verstr =~ m/^(\d+)\.(\d+)(?:\.(\d+))?(?:\+pve(\d+))?/) {
113 return 1 if version_cmp($1, $major, $2, $minor, $4, $pve) >= 0;
2ea5fb7e
SR
114 return 0;
115 }
116
117 die "internal error: cannot check version of invalid string '$verstr'";
118}
119
120# gets in pairs the versions you want to compares, i.e.:
121# ($a-major, $b-major, $a-minor, $b-minor, $a-extra, $b-extra, ...)
122# returns 0 if same, -1 if $a is older than $b, +1 if $a is newer than $b
123sub version_cmp {
124 my @versions = @_;
125
126 my $size = scalar(@versions);
127
128 return 0 if $size == 0;
cbfff937
TL
129
130 if ($size & 1) {
131 my (undef, $fn, $line) = caller(0);
132 die "cannot compare odd count of versions, called from $fn:$line\n";
133 }
2ea5fb7e
SR
134
135 for (my $i = 0; $i < $size; $i += 2) {
136 my ($a, $b) = splice(@versions, 0, 2);
137 $a //= 0;
138 $b //= 0;
139
140 return 1 if $a > $b;
141 return -1 if $a < $b;
142 }
143 return 0;
144}
145
2f18c84d 146sub config_aware_timeout {
7f8c8087 147 my ($config, $memory, $is_suspended) = @_;
2f18c84d
TM
148 my $timeout = 30;
149
150 # Based on user reported startup time for vm with 512GiB @ 4-5 minutes
151 if (defined($memory) && $memory > 30720) {
152 $timeout = int($memory/1024);
153 }
154
95f1de68
FW
155 # When using PCI passthrough, users reported much higher startup times,
156 # growing with the amount of memory configured. Constant factor chosen
157 # based on user reports.
158 if (grep(/^hostpci[0-9]+$/, keys %$config)) {
159 $timeout *= 4;
160 }
161
2f18c84d
TM
162 if ($is_suspended && $timeout < 300) {
163 $timeout = 300;
164 }
165
166 if ($config->{hugepages} && $timeout < 150) {
167 $timeout = 150;
168 }
169
170 return $timeout;
171}
172
9c88e854
AD
173sub get_node_pvecfg_version {
174 my ($node) = @_;
175
176 my $nodes_version_info = PVE::Cluster::get_node_kv('version-info', $node);
177 return if !$nodes_version_info->{$node};
178
179 my $version_info = decode_json($nodes_version_info->{$node});
180 return $version_info->{version};
181}
182
183sub pvecfg_min_version {
184 my ($verstr, $major, $minor, $release) = @_;
185
186 return 0 if !$verstr;
187
56d28037 188 if ($verstr =~ m/^(\d+)\.(\d+)(?:[.-](\d+))?/) {
e17312c9 189 return 1 if version_cmp($1, $major, $2, $minor, $3 // 0, $release) >= 0;
9c88e854
AD
190 return 0;
191 }
192
193 die "internal error: cannot check version of invalid string '$verstr'";
194}
195
261b67e4
FE
196sub parse_number_sets {
197 my ($set) = @_;
198 my $res = [];
199 foreach my $part (split(/;/, $set)) {
200 if ($part =~ /^\s*(\d+)(?:-(\d+))?\s*$/) {
201 die "invalid range: $part ($2 < $1)\n" if defined($2) && $2 < $1;
202 push @$res, [ $1, $2 ];
203 } else {
204 die "invalid range: $part\n";
205 }
206 }
207 return $res;
208}
209
238af88e
DC
210sub windows_version {
211 my ($ostype) = @_;
212
213 return 0 if !$ostype;
214
215 my $winversion = 0;
216
217 if($ostype eq 'wxp' || $ostype eq 'w2k3' || $ostype eq 'w2k') {
218 $winversion = 5;
219 } elsif($ostype eq 'w2k8' || $ostype eq 'wvista') {
220 $winversion = 6;
221 } elsif ($ostype =~ m/^win(\d+)$/) {
222 $winversion = $1;
223 }
224
225 return $winversion;
226}
227
d036e418 2281;