1
by knielsen at knielsen-hq
Import the runvm tool. |
1 |
#! /usr/bin/perl
|
2 |
||
2
by knielsen at knielsen-hq
Add licence header. |
3 |
# runvm: Run a list of commands inside a KVM virtual machine.
|
4 |
# Copyright (C) 2009 Kristian Nielsen and Monty Program AB.
|
|
5 |
#
|
|
6 |
# This program is free software; you can redistribute it and/or modify
|
|
7 |
# it under the terms of the GNU General Public License as published by
|
|
8 |
# the Free Software Foundation; either version 2 of the License, or
|
|
9 |
# (at your option) any later version.
|
|
10 |
#
|
|
11 |
# This program is distributed in the hope that it will be useful,
|
|
12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14 |
# GNU General Public License for more details.
|
|
15 |
#
|
|
16 |
# You should have received a copy of the GNU General Public License along
|
|
17 |
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
18 |
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
1
by knielsen at knielsen-hq
Import the runvm tool. |
19 |
|
20 |
use strict; |
|
21 |
use warnings; |
|
22 |
||
23 |
use POSIX; |
|
24 |
use Socket; |
|
25 |
||
26 |
use Getopt::Long; |
|
27 |
||
28 |
my $ssh_exec= 'ssh'; |
|
29 |
my $kvm_exec= 'kvm'; |
|
30 |
my $qimg_exe= 'qemu-img'; |
|
31 |
||
32 |
||
33 |
my @cpu_fix; |
|
34 |
||
35 |
my $opt_port= 2222; |
|
36 |
#my $opt_background= undef;
|
|
349.1.1
by Kristian Nielsen
Buildbot: Increase runvm default VM memory to 3GB. |
37 |
my $opt_memory= 3072; |
1
by knielsen at knielsen-hq
Import the runvm tool. |
38 |
my $opt_smp= 2; |
39 |
# This workaround (-nx) is needed to boot Ubuntu Jaunty i386 guest on
|
|
40 |
# Jaunty i386 host. For now we just include it everywhere by default,
|
|
41 |
# better safe than sorry.
|
|
42 |
my $opt_cpu= 'qemu32,-nx'; |
|
43 |
my $opt_netdev= 'virtio'; |
|
44 |
#my $opt_shutdown= undef;
|
|
45 |
my $opt_initial_sleep= 15; |
|
46 |
my $opt_startup_timeout= 300; |
|
47 |
my $opt_shutdown_timeout= 120; |
|
48 |
my $opt_max_retries= 3; |
|
49 |
my $opt_kvm_logfile= '/dev/null'; |
|
50 |
my $opt_user= undef; |
|
51 |
my $opt_extra_kvm= []; |
|
52 |
my $opt_baseimage= undef; |
|
53 |
my @user_cmd_opt; |
|
94
by knielsen at knielsen-hq
Implement --work-image option. This allows to run with the temporary VM image |
54 |
my $opt_work_image= undef; |
40.1.1
by knielsen at knielsen-hq
Add runvm --windows option to use correct shutdown cmd. |
55 |
my $opt_win; |
1
by knielsen at knielsen-hq
Import the runvm tool. |
56 |
|
4.1.3
by knielsen at knielsen-hq
Disable ssh host key checks when ssh'ing into guest (as they are annoying and not useful). |
57 |
# Disable host key checking for ssh.
|
58 |
# This is a bit convoluted due to OpenSSH's slight security-paranoia.
|
|
59 |
# Without this, we would get a login failure if using another VM image
|
|
60 |
# (with different host key) on the same port, which is annoying.
|
|
61 |
# An alternative would be to use CheckHostIP=no and HostKeyAlias=<img.qcow2>
|
|
62 |
# to get ssh to check a different key for each image. But that would still
|
|
63 |
# cause an error if re-generating an image (with new ssh host key), and it
|
|
64 |
# doesn't really give any additional security.
|
|
219
by Daniel Bartholomew
fix typo in ssh_exec command |
65 |
my @ssh_cmd_prefix= ($ssh_exec, '-t', '-t', |
4.1.3
by knielsen at knielsen-hq
Disable ssh host key checks when ssh'ing into guest (as they are annoying and not useful). |
66 |
'-o', 'UserKnownHostsFile=/dev/null', |
67 |
'-o', 'StrictHostKeyChecking=no', |
|
68 |
'-o', 'LogLevel=ERROR'); |
|
69 |
||
1
by knielsen at knielsen-hq
Import the runvm tool. |
70 |
my $image; |
71 |
my $pidfile; |
|
72 |
||
73 |
sub usage { |
|
74 |
print <<END; |
|
75 |
Usage: $0 <options> image.qcow2 [command ...]
|
|
4.1.2
by knielsen at knielsen-hq
Add documentation of runvm and all options to `runvm --help`. |
76 |
|
77 |
Boot the given KVM virtual machine image and wait for it to come up.
|
|
78 |
Run the list of commands one at a time, aborting on receiving an error.
|
|
79 |
When all commands are run (or one of them failed), shutdown the virtual
|
|
80 |
machine and exit.
|
|
81 |
||
82 |
Commands are by default run inside the virtual machine using ssh(1). By
|
|
83 |
prefixing a command with an equals sign '=', it will instead be run on the
|
|
84 |
host system (for example to copy files into or out of the virtual machine
|
|
90
by knielsen at knielsen-hq
Add ! command prefix to force commands to run even if a previous command fails. |
85 |
using scp(1)). By prefixing with an exclamation sign '!' it will be run
|
86 |
even if a previous command fails (normal commands are not processed after
|
|
87 |
failure of a previous command). The '=' and '!' prefixes may be combined.
|
|
4.1.2
by knielsen at knielsen-hq
Add documentation of runvm and all options to `runvm --help`. |
88 |
|
89 |
Some care is taken to ensure that the virtual machine is shutdown
|
|
90 |
gracefully and not left running even in case the controlling tty is
|
|
91 |
closed or the parent process killed. If a previous virtual machine is
|
|
92 |
already running on a conflicting port, an attempt is made to shut it
|
|
93 |
down first. For this purpose, a PID file is created in \$HOME/.runvm/
|
|
94 |
||
95 |
Available options:
|
|
96 |
||
97 |
-p, --port=N Forward this port on the host side to the ssh port (port
|
|
98 |
22) on the guest side. Must be different for each runvm
|
|
99 |
instance running in parallel to avoid conflicts. The
|
|
100 |
default is $opt_port.
|
|
101 |
To copy files in/out of the guest use a command prefixed
|
|
102 |
with '=' calling scp(1) with the -P option using the port
|
|
103 |
specified here, like this:
|
|
104 |
runvm img.qcow2 "=scp -P 2222 file.txt localhost:"
|
|
105 |
-u, --user=USER Name of the account to ssh into in the guest. Defaults to
|
|
106 |
the name of the user invoking runvm on the host.
|
|
107 |
-m, --memory=N Amount of memory (in megabytes) to allocate to the guest.
|
|
108 |
Defaults to $opt_memory.
|
|
109 |
--smp=N Number of CPU cores to allocate to the guest.
|
|
110 |
Defaults to $opt_smp.
|
|
111 |
-c, --cpu=NAME Type of CPU to emulate for KVM, see qemu(1) for details.
|
|
112 |
For example:
|
|
113 |
--cpu=qemu64 For 64-bit amd64 emulation
|
|
114 |
--cpu=qemu32 For 32-bit x86 emulation
|
|
115 |
--cpu=qemu32,-nx 32-bit and disable "no-execute"
|
|
116 |
The default is $opt_cpu
|
|
117 |
--netdev=NAME Network device to emulate. The 'virtio' device has good
|
|
118 |
performance but may not have driver support in all
|
|
119 |
operating systems, if so another can be specified.
|
|
120 |
The default is $opt_netdev.
|
|
121 |
--kvm=OPT Pass additional option OPT to kvm. Specify multiple times
|
|
122 |
to pass more than one option. For example
|
|
123 |
runvm --kvm=-cdrom --kvm=mycd.iso img.qcow2 ...
|
|
124 |
--initial-sleep=SECS
|
|
125 |
Wait this many seconds before starting to poll the guest
|
|
126 |
ssh port for it to be up. Default $opt_initial_sleep.
|
|
127 |
--startup-timeout=SECS
|
|
128 |
Wait at most this many seconds for the guest OS to respond
|
|
129 |
to ssh. If this time is exceeded assume it has failed to
|
|
130 |
boot correctly. Default $opt_startup_timeout.
|
|
131 |
--shutdown-timeout=SECS
|
|
132 |
Wait at most this many seconds for the guest OS to
|
|
133 |
shutdown gracefully after sending a shutdown command. If
|
|
134 |
this time is exceeded, assume the guest has failed to
|
|
135 |
shutdown gracefully and kill it forcibly. Default $opt_shutdown_timeout.
|
|
136 |
--kvm-retries=N If the guest fails to come up, retry the boot this many
|
|
137 |
times before giving up. This helps if the virtual machine
|
|
138 |
sometimes crashes during boot. Default $opt_max_retries.
|
|
139 |
-l, --logfile=FILE File to redirect the output from kvm into. This includes
|
|
140 |
any (error) messages from kvm, and also includes anything
|
|
141 |
the guest writes to the kvm emulated serial port (it can
|
|
142 |
be useful to set the guest to send boot loader and kernel
|
|
143 |
messages to the serial console and log them with this
|
|
144 |
option). Default is to not log this output anywhere.
|
|
145 |
-b, --base-image=IMG
|
|
146 |
Instead of booting an existing image, create a new
|
|
147 |
copy-on-write image based on IMG. This uses the -b option
|
|
148 |
of qemu-img(1). IMG is not modified in any way. This way,
|
|
149 |
the booted image can be discarded after use, so that each
|
|
150 |
use of IMG is using the same reference image with no risk
|
|
151 |
of "polution" between different invocations.
|
|
152 |
Note that this DELETES any existing image of the same
|
|
153 |
name as the one specified on the command line to boot! It
|
|
154 |
will be replaced with the image created as a copy of IMG,
|
|
155 |
with any modifications done during the runvm session.
|
|
94
by knielsen at knielsen-hq
Implement --work-image option. This allows to run with the temporary VM image |
156 |
--work-image=<file> Use <file> for the new copy-on-write-image while running,
|
157 |
and afterwards move it back to the specified image.qcow2
|
|
158 |
location. Used with eg. /dev/shm/ to save I/O. Only
|
|
159 |
applicable when --base-image is used.
|
|
40.1.1
by knielsen at knielsen-hq
Add runvm --windows option to use correct shutdown cmd. |
160 |
--windows The guest is Windows, not Linux.
|
1
by knielsen at knielsen-hq
Import the runvm tool. |
161 |
END
|
162 |
exit 1; |
|
163 |
};
|
|
164 |
||
165 |
# Quote and escape meta-characters as necessary.
|
|
166 |
# Don't have to do this perfectly, as it's just for printing, but
|
|
167 |
# doing at least some effort is nice for copy-paste ability.
|
|
168 |
sub quote_for_print { |
|
169 |
my @print_args= @_; |
|
170 |
for (@print_args) { |
|
171 |
if (/[^-_\/\+=,.a-zA-Z0-9]/) { |
|
172 |
if (/[\']/) { |
|
173 |
s/\\/\\\\/g; |
|
174 |
s/\"/\\\"/g; |
|
175 |
s/\$/\\\$/g; |
|
176 |
s/\`/\\\`/g; |
|
177 |
$_= '"'. $_ .'"'; |
|
178 |
} else { |
|
179 |
$_= "'". $_ . "'"; |
|
180 |
}
|
|
181 |
}
|
|
182 |
}
|
|
183 |
return @print_args; |
|
184 |
}
|
|
185 |
||
186 |
sub exec_with_print { |
|
187 |
my @args= @_; |
|
188 |
||
189 |
print STDERR "+ ", join(" ", quote_for_print(@args)), "\n"; |
|
190 |
exec {$args[0]} @args |
|
191 |
or die "exec() failed: $!\n"; |
|
192 |
}
|
|
193 |
||
194 |
sub system_with_print { |
|
195 |
my @args= @_; |
|
196 |
||
197 |
print STDERR "+ ", join(" ", quote_for_print(@args)), "\n"; |
|
198 |
my $res= system {$args[0]} @args; |
|
199 |
return $res; |
|
200 |
}
|
|
201 |
||
202 |
sub is_port_used { |
|
203 |
socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp')) |
|
204 |
or die "socket() failed: $!\n"; |
|
205 |
my $addr= sockaddr_in($opt_port, inet_aton('localhost')); |
|
206 |
my $res= connect(SOCK, $addr); |
|
207 |
close SOCK; |
|
208 |
return $res; |
|
209 |
}
|
|
210 |
||
211 |
sub get_kvm_pid { |
|
212 |
open PIDFILE, '<', $pidfile |
|
213 |
or return undef; |
|
214 |
my $pid= <PIDFILE>; |
|
215 |
close PIDFILE; |
|
216 |
chomp($pid); |
|
217 |
if ($pid =~ /^[0-9]+$/) { |
|
218 |
return $pid; |
|
219 |
} else { |
|
220 |
return undef; |
|
221 |
}
|
|
222 |
}
|
|
223 |
||
94
by knielsen at knielsen-hq
Implement --work-image option. This allows to run with the temporary VM image |
224 |
# Copy back any working image to the permanently saved location.
|
225 |
# We do this here, so that we will not do it until kvm is shut down, and
|
|
226 |
# also minimise the risk that we leave the work image undeleted, eg. if
|
|
227 |
# the parent is killed.
|
|
228 |
sub copy_back_work_image { |
|
229 |
if (defined($opt_work_image) && defined($opt_baseimage)) { |
|
230 |
system('/bin/mv', $opt_work_image, $image); |
|
231 |
}
|
|
232 |
}
|
|
233 |
||
1
by knielsen at knielsen-hq
Import the runvm tool. |
234 |
# Start the KVM process.
|
235 |
#
|
|
236 |
# We want to avoid leaving stray KVM processes running, even when other things
|
|
237 |
# go wrong (Crashed Buildbot, master-slave connection breaks, etc).
|
|
238 |
#
|
|
239 |
# Further, Even if we do manage to leave a stray KVM, we want the next
|
|
240 |
# invocation to be able to succeed by first shutting down the old one if at
|
|
241 |
# all possible.
|
|
242 |
#
|
|
243 |
sub start_kvm { |
|
244 |
my $kvm_pid= get_kvm_pid(); |
|
245 |
# Don't attempt to use a stale pid file.
|
|
246 |
if (defined ($kvm_pid) && !kill(0, $kvm_pid)) { |
|
247 |
# No process associated with pid file (or if there is we do not
|
|
248 |
# have privileges to signal it).
|
|
249 |
$kvm_pid= undef; |
|
250 |
}
|
|
251 |
||
252 |
# If the port is unused, seems safer to leave any stray process
|
|
253 |
# running (it shouldn't really bother us) rather than trying to kill
|
|
254 |
# it in an unclean fashion.
|
|
255 |
if (is_port_used()) { |
|
256 |
shutdown_kvm($kvm_pid); |
|
257 |
}
|
|
258 |
||
259 |
if (is_port_used()) { |
|
260 |
die "Cannot start KVM. The port $opt_port is already in use, and we\n". |
|
261 |
"were not able to shutdown any existing KVM process properly to\n". |
|
262 |
"free up the port.\n"; |
|
263 |
}
|
|
264 |
||
265 |
# We fork() a management process in-between the main parent process and the
|
|
266 |
# KVM process. This process will attempt to cleanly shutdown the KVM process
|
|
267 |
# if the parent dies; this is better to preserve the integrity of the VM
|
|
268 |
# disk image (no fsck etc. on next boot).
|
|
269 |
#
|
|
270 |
# The stdin of the management process is made a pipe so it can easily detect
|
|
271 |
# parent exit by waiting for stdin to close.
|
|
272 |
#
|
|
273 |
# The stdin of KVM is redirected to /dev/null, and the output is
|
|
274 |
# sent to log file.
|
|
275 |
open KVM_LOG, '>', $opt_kvm_logfile |
|
276 |
or die "Failed to open '$opt_kvm_logfile' for writing: $!\n"; |
|
277 |
||
278 |
# We want the kvm startup command both in the normal stdout log and
|
|
279 |
# in the kernel log.
|
|
94
by knielsen at knielsen-hq
Implement --work-image option. This allows to run with the temporary VM image |
280 |
my $img= (defined($opt_baseimage) && defined($opt_work_image) ? |
281 |
$opt_work_image : $image); |
|
1
by knielsen at knielsen-hq
Import the runvm tool. |
282 |
my @kvm_cmdline= |
94
by knielsen at knielsen-hq
Implement --work-image option. This allows to run with the temporary VM image |
283 |
($kvm_exec, '-m', $opt_memory, '-hda', $img, |
208
by Daniel Bartholomew
Changed /bin/true to true to support systems where true is at /usr/bin/true |
284 |
'-boot', 'c', '-smp', $opt_smp, @cpu_fix, |
285 |
'-nographic', '-net', 'nic,model='. $opt_netdev, |
|
133
by knielsen at knielsen-hq
Only listen on interface localhost for guest ssh incoming connections. |
286 |
'-net', "user,hostfwd=tcp:127.0.0.1:${opt_port}-:22", |
1
by knielsen at knielsen-hq
Import the runvm tool. |
287 |
'-pidfile', $pidfile, |
50
by knielsen at knielsen-hq
runvm: add --localhost kvm options for windows guests to get correct time. |
288 |
($opt_win ? ('-localtime') : ()), |
1
by knielsen at knielsen-hq
Import the runvm tool. |
289 |
@$opt_extra_kvm); |
290 |
print STDERR "+ ", join(" ", quote_for_print(@kvm_cmdline)), "\n"; |
|
291 |
||
292 |
my $res= open(PIPE1, '|-'); |
|
293 |
if (!defined($res)) { |
|
294 |
die "fork() or pipe() failed: $!\n"; |
|
295 |
} elsif (!$res) { |
|
296 |
# Management process.
|
|
297 |
||
298 |
# Make us the process group leader, so that when the parent
|
|
299 |
# process group is signalled, we get time to do our own cleanup.
|
|
300 |
setpgrp(0,0); |
|
301 |
||
302 |
# Close not used file descriptors.
|
|
303 |
close PIPE1; |
|
304 |
||
305 |
# Set up a signal handler so that we can exit when the kvm child
|
|
306 |
# process does.
|
|
307 |
$SIG{'CHLD'}= sub { |
|
308 |
waitpid(-1, 0); |
|
94
by knielsen at knielsen-hq
Implement --work-image option. This allows to run with the temporary VM image |
309 |
my $status= $?; |
310 |
copy_back_work_image(); |
|
311 |
exit($status); |
|
1
by knielsen at knielsen-hq
Import the runvm tool. |
312 |
};
|
313 |
$res= fork(); |
|
314 |
if (!defined($res)) { |
|
315 |
die "fork() failed: $!\n"; |
|
316 |
} elsif (!$res) { |
|
317 |
# KVM child process.
|
|
318 |
# Kill stdin.
|
|
319 |
open STDIN, '<', '/dev/null' |
|
320 |
or die "Failed to redirect stdin: $!\n"; |
|
321 |
# Redirect STDOUT/STDERR to log file.
|
|
322 |
open STDOUT, '>&KVM_LOG' |
|
323 |
or die "Failed to redirect stdout: $!\n"; |
|
324 |
open STDERR, '>&STDOUT' |
|
325 |
or die "Failed to redirect stderr: $!\n"; |
|
326 |
exec_with_print @kvm_cmdline; |
|
327 |
# Not reached.
|
|
328 |
die "Unexpected failure to start kvm."; |
|
329 |
} else { |
|
330 |
# Management process after forking kvm child.
|
|
331 |
||
332 |
close KVM_LOG; |
|
333 |
||
334 |
# We just wait for the STDIN pipe from parent to close, indicating that
|
|
335 |
# the parent process has exited. Once this happens, we shutdown the KVM
|
|
336 |
# child process and exit.
|
|
337 |
||
338 |
scalar(<STDIN>); |
|
339 |
# Parent process exited.
|
|
340 |
print STDERR "Parent process exited, shutting down KVM...\n"; |
|
341 |
shutdown_kvm(get_kvm_pid()); |
|
342 |
waitpid($res, 0); |
|
94
by knielsen at knielsen-hq
Implement --work-image option. This allows to run with the temporary VM image |
343 |
my $status= $?; |
344 |
||
345 |
copy_back_work_image(); |
|
346 |
exit($status); |
|
1
by knielsen at knielsen-hq
Import the runvm tool. |
347 |
}
|
348 |
} else { |
|
349 |
# Parent process.
|
|
350 |
close KVM_LOG; |
|
351 |
}
|
|
352 |
}
|
|
353 |
||
354 |
sub check_if_still_running { |
|
355 |
my ($kvm_pid)= @_; |
|
356 |
return 1 if is_port_used(); |
|
357 |
return 1 if $kvm_pid && kill(0, $kvm_pid); |
|
358 |
return undef; |
|
359 |
}
|
|
360 |
||
361 |
# Shutdown kvm. Try nicely first, to protect disk images, but kill
|
|
362 |
# hard if necessary.
|
|
363 |
sub shutdown_kvm { |
|
364 |
my ($kvm_pid)= @_; |
|
365 |
||
366 |
my $pid; |
|
367 |
my $timeout= undef; |
|
368 |
$SIG{ALRM}= sub { |
|
369 |
kill 9, $pid |
|
370 |
if defined($pid); |
|
371 |
$timeout= 1; |
|
372 |
};
|
|
373 |
alarm($opt_shutdown_timeout); |
|
374 |
||
375 |
while (!$timeout) { |
|
376 |
$pid= fork(); |
|
377 |
if (!defined($pid)) { |
|
378 |
die "Fatal error: Cannot fork(): $!\n"; |
|
379 |
} elsif (!$pid) { |
|
380 |
# Child.
|
|
40.1.1
by knielsen at knielsen-hq
Add runvm --windows option to use correct shutdown cmd. |
381 |
if ($opt_win) { |
382 |
exec_with_print(@ssh_cmd_prefix, '-o', 'ConnectTimeout=4', '-p', $opt_port, |
|
383 |
@user_cmd_opt, 'localhost', |
|
384 |
'shutdown', '-s', '-f', '-t', '1'); |
|
848
by Daniel Bartholomew
Added test for FreeBSD VMs so that they are shutdown properly |
385 |
} elsif ($opt_baseimage =~ /freebsd/) { |
386 |
exec_with_print(@ssh_cmd_prefix, '-o', 'ConnectTimeout=4', '-p', $opt_port, |
|
387 |
@user_cmd_opt, 'localhost', |
|
388 |
'sudo', '/sbin/shutdown', '-p', 'now'); |
|
40.1.1
by knielsen at knielsen-hq
Add runvm --windows option to use correct shutdown cmd. |
389 |
} else { |
390 |
exec_with_print(@ssh_cmd_prefix, '-o', 'ConnectTimeout=4', '-p', $opt_port, |
|
391 |
@user_cmd_opt, 'localhost', |
|
392 |
'sudo', '/sbin/shutdown', '-h', 'now'); |
|
393 |
}
|
|
1
by knielsen at knielsen-hq
Import the runvm tool. |
394 |
} else { |
395 |
# Parent.
|
|
396 |
my $res= waitpid $pid, 0; |
|
397 |
$pid= undef; |
|
398 |
last unless $?; |
|
399 |
last if !check_if_still_running($kvm_pid); |
|
400 |
sleep 1; |
|
401 |
}
|
|
402 |
}
|
|
403 |
||
404 |
# See if it will come down by itself.
|
|
405 |
my $still_running; |
|
406 |
for(;;) { |
|
407 |
$still_running= check_if_still_running($kvm_pid); |
|
408 |
last if $timeout || !$still_running; |
|
409 |
sleep 1; |
|
410 |
}
|
|
411 |
||
412 |
alarm(0); |
|
413 |
$SIG{ALRM}= 'DEFAULT'; |
|
414 |
||
415 |
return unless $still_running; |
|
416 |
||
417 |
# Ok, it refuses to die, kill it the hard way.
|
|
418 |
print STDERR "Failed to gracefully shutdown KVM within ". |
|
419 |
"$opt_shutdown_timeout seconds\nTrying kill -9 ...\n"; |
|
420 |
kill 9, $kvm_pid; |
|
88
by knielsen at knielsen-hq
More robust force shutdown handling. |
421 |
for (1 .. 10) { |
422 |
sleep 1; |
|
423 |
last if !kill(0, $kvm_pid) && !is_port_used(); |
|
424 |
}
|
|
1
by knielsen at knielsen-hq
Import the runvm tool. |
425 |
# If that didn't work, there is not much else we can try.
|
426 |
print STDERR "Unable to kill kvm process (pid $kvm_pid).\n" |
|
427 |
if kill(0, $kvm_pid); |
|
428 |
}
|
|
429 |
||
430 |
# Wait for kvm to come up, with timeout for giving up.
|
|
431 |
# Return 0 on success, -1 on timeout, 1 on kvm process gone.
|
|
432 |
sub wait_for_up { |
|
433 |
my ($kvm_pid)= @_; |
|
434 |
||
435 |
# Set an alarm() timeout so we don't hang forever waiting for a broken KVM
|
|
436 |
# to come up.
|
|
437 |
my $pid; |
|
438 |
my $timeout= undef; |
|
439 |
$SIG{ALRM}= sub { |
|
440 |
kill 9, $pid |
|
441 |
if defined($pid); |
|
442 |
$timeout= 1; |
|
443 |
};
|
|
444 |
alarm($opt_startup_timeout); |
|
445 |
||
446 |
sleep ($opt_initial_sleep) |
|
447 |
if $opt_initial_sleep; |
|
448 |
||
449 |
my $ret= -1; |
|
91
by knielsen at knielsen-hq
Runvm: more robust wait for VM startup. |
450 |
# Occasionally we see ssh connection succeeding, then immediately
|
451 |
# after failing, then after a brief moment working again,
|
|
452 |
# permanently. Handle this by checking a few times with short
|
|
453 |
# interval that the connection is really working.
|
|
454 |
my $success_attempts= 0; |
|
1
by knielsen at knielsen-hq
Import the runvm tool. |
455 |
while (!$timeout) { |
456 |
$pid= fork(); |
|
457 |
if (!defined($pid)) { |
|
458 |
die "Fatal error: Cannot fork(): $!\n"; |
|
459 |
} elsif (!$pid) { |
|
460 |
# Child.
|
|
4.1.3
by knielsen at knielsen-hq
Disable ssh host key checks when ssh'ing into guest (as they are annoying and not useful). |
461 |
exec_with_print(@ssh_cmd_prefix, '-o', 'ConnectTimeout=4', '-p', $opt_port, |
208
by Daniel Bartholomew
Changed /bin/true to true to support systems where true is at /usr/bin/true |
462 |
@user_cmd_opt, 'localhost', 'true'); |
1
by knielsen at knielsen-hq
Import the runvm tool. |
463 |
} else { |
464 |
# Parent.
|
|
465 |
my $res= waitpid $pid, 0; |
|
466 |
$pid= undef; |
|
467 |
if ($? == 0) { |
|
91
by knielsen at knielsen-hq
Runvm: more robust wait for VM startup. |
468 |
if (++$success_attempts >= 3) { |
469 |
# Ok, KVM is up now!
|
|
470 |
$ret= 0; |
|
471 |
last; |
|
472 |
}
|
|
473 |
} else { |
|
474 |
$success_attempts= 0; |
|
1
by knielsen at knielsen-hq
Import the runvm tool. |
475 |
}
|
476 |
$kvm_pid= get_kvm_pid() |
|
477 |
unless defined($kvm_pid); |
|
478 |
if (!kill(0, $kvm_pid)) { |
|
479 |
# The KVM process seems to have died!
|
|
480 |
$ret= 1; |
|
481 |
last; |
|
482 |
}
|
|
91
by knielsen at knielsen-hq
Runvm: more robust wait for VM startup. |
483 |
# Wait a bit before retrying (select() is an easy way to get
|
484 |
# portable sub-second sleep).
|
|
485 |
select(undef, undef, undef, $success_attempts ? 0.33 : 2); |
|
1
by knielsen at knielsen-hq
Import the runvm tool. |
486 |
}
|
487 |
}
|
|
488 |
||
489 |
alarm(0); |
|
490 |
$SIG{ALRM}= 'DEFAULT'; |
|
491 |
return $ret; |
|
492 |
}
|
|
493 |
||
494 |
my $result= GetOptions |
|
495 |
( 'port|p=i' => \$opt_port, |
|
496 |
'user|u=s' => \$opt_user, |
|
497 |
# 'background|b' => \$opt_background,
|
|
498 |
'memory|m=i' => \$opt_memory, |
|
499 |
'smp=i' => \$opt_smp, |
|
500 |
'cpu|c=s' => \$opt_cpu, |
|
501 |
'netdev=s' => \$opt_netdev, |
|
502 |
'kvm=s' => $opt_extra_kvm, |
|
503 |
# 'shutdown|s' => \$opt_shutdown,
|
|
504 |
'initial-sleep=i' => \$opt_initial_sleep, |
|
505 |
'startup-timeout=i' => \$opt_startup_timeout, |
|
506 |
'shutdown-timeout=i' => \$opt_shutdown_timeout, |
|
507 |
'kvm-retries=i' => \$opt_max_retries, |
|
508 |
'logfile|l=s' => \$opt_kvm_logfile, |
|
509 |
'base-image|b=s' => \$opt_baseimage, |
|
94
by knielsen at knielsen-hq
Implement --work-image option. This allows to run with the temporary VM image |
510 |
'work-image=s' => \$opt_work_image, |
40.1.1
by knielsen at knielsen-hq
Add runvm --windows option to use correct shutdown cmd. |
511 |
'windows' => \$opt_win, |
1
by knielsen at knielsen-hq
Import the runvm tool. |
512 |
);
|
513 |
||
514 |
if (defined($opt_user)) { |
|
515 |
@user_cmd_opt= ('-l', $opt_user); |
|
516 |
}
|
|
517 |
||
518 |
if (@ARGV < 1) { |
|
519 |
print STDERR "No KVM/Qemu image specified, aborting.\n"; |
|
520 |
usage(); |
|
521 |
}
|
|
522 |
||
523 |
$image= shift @ARGV; |
|
524 |
||
525 |
$pidfile= $ENV{HOME} . "/.runvm"; |
|
526 |
system 'mkdir', '-p', $pidfile |
|
527 |
and die "Failed to create pidfile directory '$pidfile': $!\n"; |
|
528 |
$pidfile.= "kvm_$opt_port.pid"; |
|
529 |
||
209
by Daniel Bartholomew
fix @cpu_fix to support Ubuntu 13.04 "raring" amd64 VMs |
530 |
# Fix for Ubuntu 13.04 "raring" amd64 VMs
|
531 |
if ($opt_port == 2279) { |
|
532 |
@cpu_fix= ('-cpu', "$opt_cpu"); |
|
533 |
} else { |
|
534 |
@cpu_fix= ('-cpu', "$opt_cpu,-kvmclock"); |
|
535 |
}
|
|
1
by knielsen at knielsen-hq
Import the runvm tool. |
536 |
|
537 |
my $retries= 0; |
|
538 |
for (;;) { |
|
539 |
if (defined($opt_baseimage)) { |
|
94
by knielsen at knielsen-hq
Implement --work-image option. This allows to run with the temporary VM image |
540 |
my $img= (defined($opt_work_image) ? $opt_work_image : $image); |
331
by Daniel Bartholomew
change to ensure qemu-img creates images compatible with older kvm |
541 |
my $res= system_with_print($qimg_exe, 'create', '-o', 'compat=0.10', '-b', $opt_baseimage, '-f', 'qcow2', $img); |
1
by knielsen at knielsen-hq
Import the runvm tool. |
542 |
if ($res) { |
543 |
print STDERR "Failed to clone base image, aborting\n"; |
|
544 |
exit 1; |
|
545 |
}
|
|
546 |
}
|
|
547 |
||
548 |
start_kvm(); |
|
549 |
my $err= wait_for_up(); |
|
550 |
last unless $err; |
|
551 |
||
552 |
# Hm, we did not come up :-(. Retry until the limit.
|
|
553 |
$retries++; |
|
554 |
||
555 |
print "KVM does not seem to come up properly, shutting down and ", |
|
556 |
($retries < $opt_max_retries ? "retrying" : "aborting"), ".\n"; |
|
557 |
shutdown_kvm(get_kvm_pid()); |
|
558 |
exit 1 unless $retries < $opt_max_retries; |
|
559 |
}
|
|
560 |
||
561 |
my $ret= 0; |
|
562 |
for my $arg (@ARGV) { |
|
90
by knielsen at knielsen-hq
Add ! command prefix to force commands to run even if a previous command fails. |
563 |
my $always_run= undef; |
564 |
my $local_cmd= undef; |
|
565 |
# Leading exclamation mark means run even if an earlier command failed.
|
|
566 |
$always_run= 1 |
|
567 |
if $arg =~ s/^(=?)\!\s*/$1/; |
|
568 |
# A leading equals sign '=' means it is a host command, else guest.
|
|
569 |
$local_cmd= 1 |
|
570 |
if $arg =~ s/^=\s*//; |
|
571 |
# If a command already failed, only run commands prefixed with `!'.
|
|
572 |
next if $ret && !$always_run; |
|
573 |
||
1
by knielsen at knielsen-hq
Import the runvm tool. |
574 |
my $res; |
90
by knielsen at knielsen-hq
Add ! command prefix to force commands to run even if a previous command fails. |
575 |
if ($local_cmd) { |
1
by knielsen at knielsen-hq
Import the runvm tool. |
576 |
print STDERR "= $arg\n"; |
90
by knielsen at knielsen-hq
Add ! command prefix to force commands to run even if a previous command fails. |
577 |
$res= system($arg); |
1
by knielsen at knielsen-hq
Import the runvm tool. |
578 |
} else { |
579 |
print STDERR "+ $arg\n"; |
|
4.1.3
by knielsen at knielsen-hq
Disable ssh host key checks when ssh'ing into guest (as they are annoying and not useful). |
580 |
$res= system(@ssh_cmd_prefix, '-p', $opt_port, @user_cmd_opt, 'localhost', $arg); |
1
by knielsen at knielsen-hq
Import the runvm tool. |
581 |
}
|
582 |
if ($res < 0) { |
|
583 |
print STDERR "Could not spawn command: $!\n"; |
|
90
by knielsen at knielsen-hq
Add ! command prefix to force commands to run even if a previous command fails. |
584 |
$ret= 1 unless $ret; |
1
by knielsen at knielsen-hq
Import the runvm tool. |
585 |
} elsif ($res > 0) { |
586 |
my $exit_val= $res >> 8; |
|
587 |
my $core= (($res >> 7) & 1) ? " (core dumped)" : ""; |
|
588 |
my $sig= $res & 127; |
|
589 |
if ($core || $sig) { |
|
590 |
print STDERR "Terminated$core"; |
|
591 |
print STDERR ": got signal $sig" |
|
592 |
if $sig; |
|
593 |
print STDERR "\n"; |
|
594 |
} else { |
|
595 |
print STDERR "Command exit $exit_val\n"; |
|
596 |
}
|
|
90
by knielsen at knielsen-hq
Add ! command prefix to force commands to run even if a previous command fails. |
597 |
$ret= $exit_val || 1 unless $ret; |
1
by knielsen at knielsen-hq
Import the runvm tool. |
598 |
}
|
599 |
}
|
|
600 |
||
601 |
shutdown_kvm(get_kvm_pid()); |
|
602 |
||
603 |
exit $ret; |