]> git.proxmox.com Git - qemu-server.git/blob - PVE/QemuServer/Helpers.pm
migration: test targetnode min version for cloudinit section
[qemu-server.git] / PVE / QemuServer / Helpers.pm
1 package PVE::QemuServer::Helpers;
2
3 use strict;
4 use warnings;
5
6 use File::stat;
7 use JSON;
8
9 use PVE::INotify;
10 use PVE::ProcFSTools;
11
12 use base 'Exporter';
13 our @EXPORT_OK = qw(
14 min_version
15 config_aware_timeout
16 );
17
18 my $nodename = PVE::INotify::nodename();
19
20 # Paths and directories
21
22 our $var_run_tmpdir = "/var/run/qemu-server";
23 mkdir $var_run_tmpdir;
24
25 sub qmp_socket {
26 my ($vmid, $qga) = @_;
27 my $sockettype = $qga ? 'qga' : 'qmp';
28 return "${var_run_tmpdir}/$vmid.$sockettype";
29 }
30
31 sub pidfile_name {
32 my ($vmid) = @_;
33 return "${var_run_tmpdir}/$vmid.pid";
34 }
35
36 sub vnc_socket {
37 my ($vmid) = @_;
38 return "${var_run_tmpdir}/$vmid.vnc";
39 }
40
41 # Parse the cmdline of a running kvm/qemu process and return arguments as hash
42 sub parse_cmdline {
43 my ($pid) = @_;
44
45 my $fh = IO::File->new("/proc/$pid/cmdline", "r");
46 if (defined($fh)) {
47 my $line = <$fh>;
48 $fh->close;
49 return if !$line;
50 my @param = split(/\0/, $line);
51
52 my $cmd = $param[0];
53 return if !$cmd || ($cmd !~ m|kvm$| && $cmd !~ m@(?:^|/)qemu-system-[^/]+$@);
54
55 my $phash = {};
56 my $pending_cmd;
57 for (my $i = 0; $i < scalar (@param); $i++) {
58 my $p = $param[$i];
59 next if !$p;
60
61 if ($p =~ m/^--?(.*)$/) {
62 if ($pending_cmd) {
63 $phash->{$pending_cmd} = {};
64 }
65 $pending_cmd = $1;
66 } elsif ($pending_cmd) {
67 $phash->{$pending_cmd} = { value => $p };
68 $pending_cmd = undef;
69 }
70 }
71
72 return $phash;
73 }
74 return;
75 }
76
77 sub vm_running_locally {
78 my ($vmid) = @_;
79
80 my $pidfile = pidfile_name($vmid);
81
82 if (my $fd = IO::File->new("<$pidfile")) {
83 my $st = stat($fd);
84 my $line = <$fd>;
85 close($fd);
86
87 my $mtime = $st->mtime;
88 if ($mtime > time()) {
89 warn "file '$pidfile' modified in future\n";
90 }
91
92 if ($line =~ m/^(\d+)$/) {
93 my $pid = $1;
94 my $cmdline = parse_cmdline($pid);
95 if ($cmdline && defined($cmdline->{pidfile}) && $cmdline->{pidfile}->{value}
96 && $cmdline->{pidfile}->{value} eq $pidfile) {
97 if (my $pinfo = PVE::ProcFSTools::check_process_running($pid)) {
98 return $pid;
99 }
100 }
101 }
102 }
103
104 return;
105 }
106
107 sub min_version {
108 my ($verstr, $major, $minor, $pve) = @_;
109
110 if ($verstr =~ m/^(\d+)\.(\d+)(?:\.(\d+))?(?:\+pve(\d+))?/) {
111 return 1 if version_cmp($1, $major, $2, $minor, $4, $pve) >= 0;
112 return 0;
113 }
114
115 die "internal error: cannot check version of invalid string '$verstr'";
116 }
117
118 # gets in pairs the versions you want to compares, i.e.:
119 # ($a-major, $b-major, $a-minor, $b-minor, $a-extra, $b-extra, ...)
120 # returns 0 if same, -1 if $a is older than $b, +1 if $a is newer than $b
121 sub version_cmp {
122 my @versions = @_;
123
124 my $size = scalar(@versions);
125
126 return 0 if $size == 0;
127
128 if ($size & 1) {
129 my (undef, $fn, $line) = caller(0);
130 die "cannot compare odd count of versions, called from $fn:$line\n";
131 }
132
133 for (my $i = 0; $i < $size; $i += 2) {
134 my ($a, $b) = splice(@versions, 0, 2);
135 $a //= 0;
136 $b //= 0;
137
138 return 1 if $a > $b;
139 return -1 if $a < $b;
140 }
141 return 0;
142 }
143
144 sub config_aware_timeout {
145 my ($config, $is_suspended) = @_;
146 my $memory = $config->{memory};
147 my $timeout = 30;
148
149 # Based on user reported startup time for vm with 512GiB @ 4-5 minutes
150 if (defined($memory) && $memory > 30720) {
151 $timeout = int($memory/1024);
152 }
153
154 if ($is_suspended && $timeout < 300) {
155 $timeout = 300;
156 }
157
158 if ($config->{hugepages} && $timeout < 150) {
159 $timeout = 150;
160 }
161
162 return $timeout;
163 }
164
165 sub get_node_pvecfg_version {
166 my ($node) = @_;
167
168 my $nodes_version_info = PVE::Cluster::get_node_kv('version-info', $node);
169 return if !$nodes_version_info->{$node};
170
171 my $version_info = decode_json($nodes_version_info->{$node});
172 return $version_info->{version};
173 }
174
175 sub pvecfg_min_version {
176 my ($verstr, $major, $minor, $release) = @_;
177
178 return 0 if !$verstr;
179
180 if ($verstr =~ m/^(\d+)\.(\d+)-(\d+)/) {
181 return 1 if version_cmp($1, $major, $2, $minor, $3, $release) >= 0;
182 return 0;
183 }
184
185 die "internal error: cannot check version of invalid string '$verstr'";
186 }
187
188 1;