From 83dacb8c7cc85720a51fd275378228911d1d0501 Mon Sep 17 00:00:00 2001 From: Olivier Gayot Date: Wed, 27 Apr 2022 19:55:55 +0200 Subject: [PATCH 1/4] kvm-test: don't execute code if module is imported Signed-off-by: Olivier Gayot --- scripts/kvm-test.py | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/scripts/kvm-test.py b/scripts/kvm-test.py index 9516c85d..0e1206f7 100755 --- a/scripts/kvm-test.py +++ b/scripts/kvm-test.py @@ -456,21 +456,27 @@ def help(): sys.exit(1) -try: - ctx = parse_args() -except TypeError: - help() +def main() -> None: + """ Entry point. """ + try: + ctx = parse_args() + except TypeError: + help() -if ctx.args.base and ctx.args.build: - raise Exception('cannot use base iso and build') + if ctx.args.base and ctx.args.build: + raise Exception('cannot use base iso and build') -os.makedirs('/tmp/kvm-test', exist_ok=True) + os.makedirs('/tmp/kvm-test', exist_ok=True) -if ctx.args.build: - build(ctx) -if ctx.args.install: - install(ctx) -if ctx.args.boot: - boot(ctx) -if True not in (ctx.args.build, ctx.args.install, ctx.args.boot): - parser.print_help() + if ctx.args.build: + build(ctx) + if ctx.args.install: + install(ctx) + if ctx.args.boot: + boot(ctx) + if True not in (ctx.args.build, ctx.args.install, ctx.args.boot): + parser.print_help() + + +if __name__ == "__main__": + main() From 6165c0a423a60eda2e44fb81ee3c01bddb12c36b Mon Sep 17 00:00:00 2001 From: Olivier Gayot Date: Wed, 27 Apr 2022 19:59:17 +0200 Subject: [PATCH 2/4] kvm-test: don't require LIVEFS_EDITOR if not building Signed-off-by: Olivier Gayot --- scripts/kvm-test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/kvm-test.py b/scripts/kvm-test.py index 0e1206f7..4942c8e9 100755 --- a/scripts/kvm-test.py +++ b/scripts/kvm-test.py @@ -186,7 +186,7 @@ def parse_args(): ctx.args.save = True ctx.livefs_editor = os.environ.get('LIVEFS_EDITOR') - if not ctx.livefs_editor: + if not ctx.livefs_editor and ctx.args.build: raise Exception('Obtain a copy of livefs-editor and point ' + 'LIVEFS_EDITOR to it\n' 'https://github.com/mwhudson/livefs-editor') From eb7dee2c9eb2291dcca458a221b50ef140e6e234 Mon Sep 17 00:00:00 2001 From: Olivier Gayot Date: Wed, 27 Apr 2022 20:04:20 +0200 Subject: [PATCH 3/4] kvm-test: get rid of unused import random Signed-off-by: Olivier Gayot --- scripts/kvm-test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/kvm-test.py b/scripts/kvm-test.py index 4942c8e9..b12f593f 100755 --- a/scripts/kvm-test.py +++ b/scripts/kvm-test.py @@ -14,7 +14,6 @@ import contextlib import copy import crypt import os -import random import shlex import socket import subprocess From e58d1d00c2a8ed0cb0d006143f23f391a6a384fc Mon Sep 17 00:00:00 2001 From: Olivier Gayot Date: Thu, 28 Apr 2022 12:28:03 +0200 Subject: [PATCH 4/4] kvm-test: introduce ability to pass -nic options to QEMU The only way we had to pass -nic options to QEMU was to specify the number of networks using the --nets option. Depending on its value, the option would instruct QEMU to: * instantiate *n* user host networks if --nets >= 1 * instantiate no network if --nets is 0 * instantiate a "dead" network if --nets < 0 To simulate more advanced networks (such as networks where HTTP proxy is needed), we want to add the possibility to use TAP network interfaces. This patch adds the ability to pass custom -nic options to QEMU. Three new options are available: * The --nic option passes the argument as-is to QEMU -nic option * The --nic-user option passes a user host network -nic option to QEMU - with SSH forwarding enabled. This is just like we do when using the --nets >= 1 option) * The --net-tap takes the name of an existing tap interface and generates the following -nic argument: tap,id={ifname},ifname={ifname},script=no,downscript=no,model=e1000 In the example below: * the first network uses the tap0 interface. * the second network uses the tap1 interface (it is syntactically equivalent to the first one) * the third network uses a user host network (with SSH forwarding) $ kvm-test.py \ --nic tap,id=tap0,ifname=tap0,script=no,downscript=no,model=e1000 \ --nic-tap tap1 \ --nic-user Signed-off-by: Olivier Gayot --- scripts/kvm-test.py | 85 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 16 deletions(-) diff --git a/scripts/kvm-test.py b/scripts/kvm-test.py index b12f593f..cf554b3f 100755 --- a/scripts/kvm-test.py +++ b/scripts/kvm-test.py @@ -19,7 +19,7 @@ import socket import subprocess import sys import tempfile -from typing import Tuple +from typing import List, Tuple import yaml @@ -47,6 +47,11 @@ def salted_crypt(plaintext_password): return crypt.crypt(plaintext_password, salt) +class Tap: + def __init__(self, ifname: str) -> None: + self.ifname = ifname + + class Context: def __init__(self, args): self.config = self.load_config() @@ -149,6 +154,18 @@ parser.add_argument('-i', '--img', action='store', help='use this img') parser.add_argument('-n', '--nets', action='store', default=1, type=int, help='''number of network interfaces. 0=no network, -1=deadnet''') +parser.add_argument('--nic-user', action="append_const", dest="nics", + const=None, + help='pass user host -nic to QEMU' + ' - overrides --nets') +parser.add_argument('--nic-tap', action="append", dest="nics", type=Tap, + metavar="ifname", + help='TAP interface to be passed as -nic to QEMU' + ' - overrides --nets') +parser.add_argument('--nic', action="append", dest="nics", + metavar="argument", + help='pass custom -nic argument to QEMU' + ' - overrides --nets') parser.add_argument('-o', '--overwrite', default=False, action='store_true', help='allow overwrite of the target image') parser.add_argument('-q', '--quick', default=False, action='store_true', @@ -338,23 +355,59 @@ class PortFinder: return next(self.finder) -def nets(ctx): - if ctx.args.nets > 0: - ports = PortFinder() - ret = [] - for _ in range(ctx.args.nets): - port = ports.get() - ret.extend(('-nic', - 'user,model=virtio-net-pci,' + - f'hostfwd=tcp::{port}-:22')) - return ret - elif ctx.args.nets == 0: - # no network - return ('-nic', 'none') - else: - # nic present but restricted - simulate deadnet environment +class NetFactory: + """ Generate -nic options for QEMU. """ + ports_finder = PortFinder() + + def user(self) -> Tuple[str, ...]: + """ User host network with SSH forwarding """ + port = self.ports_finder.get() + return ('-nic', f'user,model=virtio-net-pci,hostfwd=tcp::{port}-:22') + + def tap(self, ifname: str) -> Tuple[str, ...]: + """ Network using an existing TAP interface. """ + tap_props = { + "id": ifname, + "ifname": ifname, + "script": "no", + "downscript": "no", + "model": "e1000", + } + + nic = ",".join(["tap"] + [f"{k}={v}" for k, v in tap_props.items()]) + + return ('-nic', nic) + + def deadnet(self) -> Tuple[str, ...]: + """ NIC present but restricted - simulate deadnet environment """ return ('-nic', 'user,model=virtio-net-pci,restrict=on') + def nonet(self) -> Tuple[str, ...]: + """ No network """ + return ('-nic', 'none') + + +def nets(ctx) -> List[str]: + nics: List[str] = [] + factory = NetFactory() + + if ctx.args.nics: + for nic in ctx.args.nics: + if nic is None: + nics.extend(factory.user()) + elif isinstance(nic, Tap): + nics.extend(factory.tap(nic.ifname)) + else: + nics.extend(('-nic', nic)) + elif ctx.args.nets > 0: + for _ in range(ctx.args.nets): + nics.extend(factory.user()) + elif ctx.args.nets == 0: + nics.extend(factory.nonet()) + else: + nics.extend(factory.deadnet()) + return nics + def bios(ctx): ret = []