151 lines
5.5 KiB
Python
Executable file
151 lines
5.5 KiB
Python
Executable file
#!/usr/bin/python3
|
|
# This file is part of Cockpit.
|
|
#
|
|
# Copyright (C) 2013 Red Hat, Inc.
|
|
#
|
|
# Cockpit is free software; you can redistribute it and/or modify it
|
|
# under the terms of the GNU Lesser General Public License as published by
|
|
# the Free Software Foundation; either version 2.1 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Cockpit is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import argparse
|
|
import errno
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import re
|
|
|
|
from machine import testvm
|
|
|
|
BOTS = os.path.abspath(os.path.dirname(__file__))
|
|
|
|
NETWORK_SCRIPT=b"""
|
|
set -ex
|
|
virsh net-destroy cockpit1 || true
|
|
virsh net-undefine cockpit1 || true
|
|
virsh net-define /dev/stdin <<EOF
|
|
|
|
<network ipv6='yes'>
|
|
<name>cockpit1</name>
|
|
<uuid>f3605fa4-0763-41ea-8143-49da3bf73263</uuid>
|
|
<forward mode='nat'>
|
|
<nat>
|
|
<port start='1024' end='65535'/>
|
|
</nat>
|
|
</forward>
|
|
<bridge name='cockpit1' stp='on' delay='0' />
|
|
<domain name='cockpit.lan'/>
|
|
<ip address='10.111.112.1' netmask='255.255.240.0'>
|
|
<dhcp xmlns:cockpit="urn:cockpit-project.org:cockpit">
|
|
<range start="10.111.112.2" end="10.111.127.254" />
|
|
</dhcp>
|
|
</ip>
|
|
<ip family="ipv6" address="fd00:111:112::1" prefix="64"/>
|
|
</network>
|
|
EOF
|
|
|
|
if [ ! -u /usr/libexec/qemu-bridge-helper ]; then
|
|
chmod -v u+s /usr/libexec/qemu-bridge-helper
|
|
fi
|
|
|
|
for qemu_config in /etc/qemu-kvm/bridge.conf /etc/qemu/bridge.conf; do
|
|
if [ -e "$qemu_config" ] && ! grep -qF cockpit1 "$qemu_config"; then
|
|
echo "allow cockpit1" >> "$qemu_config"
|
|
fi
|
|
done
|
|
|
|
virsh net-autostart cockpit1
|
|
virsh net-start cockpit1
|
|
"""
|
|
|
|
parser = argparse.ArgumentParser(description='Run a test machine')
|
|
parser.add_argument('-v', '--verbose', action='store_true', help='Display verbose details')
|
|
parser.add_argument('-m', '--maintain', action='store_true', help='Changes are permanent')
|
|
parser.add_argument('-M', '--memory', default=None, type=int, help='Memory (in MiB) of the target machine')
|
|
parser.add_argument('-G', '--graphics', action='store_true', help='Display a graphics console')
|
|
parser.add_argument('-C', '--cpus', default=None, type=int, help='Number of cpus in the target machine')
|
|
parser.add_argument('-S', '--storage', default=None, type=str, help='Add a second qcow2 disk at this path')
|
|
parser.add_argument('--network', action='store_true', help='Setup a bridged network for running machines')
|
|
parser.add_argument('--no-network', action='store_true', help='Do not connect the machine to the Internet')
|
|
|
|
parser.add_argument('image', help='The image to run')
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
if args.network:
|
|
proc = subprocess.Popen(["sudo", "/bin/sh"], stdin=subprocess.PIPE)
|
|
proc.communicate(NETWORK_SCRIPT)
|
|
if proc.returncode != 0:
|
|
sys.stderr.write("vm-run: failed to create cockpit1 network\n")
|
|
sys.exit(1)
|
|
|
|
bridge=None
|
|
with open(os.devnull, 'w') as fp:
|
|
if subprocess.call(["ip", "address", "show", "dev", "cockpit1"], stdout=fp, stderr=fp) == 0:
|
|
bridge = "cockpit1"
|
|
|
|
# Lets make sure Windows has enough memory to be productive
|
|
memory = args.memory
|
|
if "windows" in args.image and memory is None:
|
|
memory = 4096
|
|
|
|
graphics = args.graphics or 'windows' in args.image
|
|
network = testvm.VirtNetwork(0, bridge=bridge, image=args.image)
|
|
|
|
machine = testvm.VirtMachine(verbose=args.verbose, image=args.image, maintain=args.maintain,
|
|
networking=network.host(restrict=args.no_network),
|
|
memory_mb=memory, cpus=args.cpus, graphics=graphics)
|
|
|
|
# Hack to make things easier for users who don't know about kubeconfig
|
|
if 'openshift' in args.image:
|
|
kubeconfig = os.path.join(os.path.expanduser("~"), ".kube", "config")
|
|
if not os.path.lexists(kubeconfig):
|
|
d = os.path.dirname(kubeconfig)
|
|
src = os.path.abspath(os.path.join(BOTS, "images", "files", "openshift.kubeconfig"))
|
|
os.makedirs(d, exist_ok=True)
|
|
sys.stderr.write("image-run: linking kubeconfig into ~/.kube/config\n")
|
|
os.symlink(src, kubeconfig)
|
|
|
|
# Check that image is downloaded
|
|
if not os.path.exists(machine.image_file):
|
|
try:
|
|
ret = subprocess.call([ os.path.join(BOTS, "image-download"), args.image])
|
|
except OSError as ex:
|
|
if ex.errno != errno.ENOENT:
|
|
raise
|
|
else:
|
|
if ret != 0:
|
|
sys.exit(ret)
|
|
|
|
machine.start()
|
|
|
|
if args.storage:
|
|
machine.add_disk(path=args.storage, type='qcow2')
|
|
|
|
# for a bridged network, up its interface with DHCP and show it in the console message
|
|
message = ""
|
|
if args.network and bridge and 'windows' not in args.image:
|
|
machine.execute("nmcli connection modify 'System eth1' ipv4.method auto && nmcli connection up 'System eth1'")
|
|
output = machine.execute("ip -4 a show dev eth1")
|
|
ip = re.search("inet ([^/]+)", output).group(1)
|
|
message = "\nBRIDGE IP FROM HOST\n %s\n" % ip
|
|
|
|
# Graphics console necessary
|
|
if graphics:
|
|
machine.graphics_console()
|
|
|
|
# No graphics console necessary
|
|
else:
|
|
machine.qemu_console(message)
|
|
|
|
except testvm.Failure as ex:
|
|
sys.stderr.write("vm-run: %s\n" % ex)
|
|
sys.exit(1)
|