diff mbox series

[12/13] tests/vm: fedora autoinstall, using serial console

Message ID 20190508085645.11595-13-kraxel@redhat.com (mailing list archive)
State New, archived
Headers show
Series tests/vm: serial console autoinstall, misc fixes. | expand

Commit Message

Gerd Hoffmann May 8, 2019, 8:56 a.m. UTC
Download the install iso and prepare the image locally.  Install to
disk, using the serial console.  Create qemu user, configure ssh login.
Install packages needed for qemu builds.

Yes, we have docker images for fedora.  But for trouble-shooting it
might be helpful to have a vm too.  When vm builds fail you can use
it to figure whenever the vm setup or the guest os is the problem.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 tests/vm/basevm.py        |   9 +-
 tests/vm/Makefile.include |   3 +-
 tests/vm/fedora           | 187 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 197 insertions(+), 2 deletions(-)
 create mode 100755 tests/vm/fedora

Comments

Thomas Huth May 9, 2019, noon UTC | #1
On 08/05/2019 10.56, Gerd Hoffmann wrote:
> Download the install iso and prepare the image locally.  Install to
> disk, using the serial console.  Create qemu user, configure ssh login.
> Install packages needed for qemu builds.
> 
> Yes, we have docker images for fedora.  But for trouble-shooting it
> might be helpful to have a vm too.  When vm builds fail you can use
> it to figure whenever the vm setup or the guest os is the problem.
> 
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  tests/vm/basevm.py        |   9 +-
>  tests/vm/Makefile.include |   3 +-
>  tests/vm/fedora           | 187 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 197 insertions(+), 2 deletions(-)
>  create mode 100755 tests/vm/fedora
[...]
> diff --git a/tests/vm/fedora b/tests/vm/fedora
> new file mode 100755
> index 000000000000..d8cb62c803a4
> --- /dev/null
> +++ b/tests/vm/fedora
> @@ -0,0 +1,187 @@
> +#!/usr/bin/env python
> +#
> +# FreeBSD VM image
> +#
> +# Copyright 2017 Red Hat Inc.
> +#
> +# Authors:
> +#  Fam Zheng <famz@redhat.com>

All the above information needs some update, obviously.

> +class FedoraVM(basevm.BaseVM):
> +    name = "fedora"
> +    arch = "x86_64"
> +
> +    base = "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/"
> +    link = base + "Server/x86_64/iso/Fedora-Server-netinst-x86_64-30-1.2.iso"
> +    repo = base + "Server/x86_64/os/"
> +    full = base + "Everything/x86_64/os/"
> +    csum = "5e4eac4566d8c572bfb3bcf54b7d6c82006ec3c6c882a2c9235c6d3494d7b100"
> +    size = "20G"
> +    pkgs = [
> +        # tools
> +        'git',
> +        'flex', 'bison',
> +        'gcc', 'binutils', 'make',
> +
> +        # perl
> +        'perl-Test-Harness',
> +
> +        # libs: usb
> +        '"pkgconfig(libusb-1.0)"',
> +        '"pkgconfig(libusbredirparser-0.5)"',
> +
> +        # libs: crypto
> +        '"pkgconfig(gnutls)"',
> +
> +        # libs: ui
> +        '"pkgconfig(sdl2)"',
> +        '"pkgconfig(gtk+-3.0)"',
> +        '"pkgconfig(ncursesw)"',
> +
> +        # libs: audio
> +        '"pkgconfig(libpulse)"',
> +        '"pkgconfig(alsa)"',
> +    ]
> +
> +    BUILD_SCRIPT = """
> +        set -e;
> +        rm -rf /home/qemu/qemu-test.*
> +        cd $(mktemp -d /home/qemu/qemu-test.XXXXXX);
> +        mkdir src build; cd src;
> +        tar -xf /dev/vdb;
> +        cd ../build
> +        ../src/configure --python=python3 {configure_opts};
> +        gmake --output-sync -j{jobs} {target} {verbose};
> +    """
> +
> +    def build_image(self, img):
> +        self.print_step("Downloading install iso")
> +        cimg = self._download_with_cache(self.link, sha256sum=self.csum)
> +        img_tmp = img + ".tmp"
> +        iso = img + ".install.iso"
> +
> +        self.print_step("Preparing iso and disk image")
> +        subprocess.check_call(["cp", "-f", cimg, iso])
> +        subprocess.check_call(["qemu-img", "create", "-f", "qcow2",
> +                               img_tmp, self.size])
> +
> +        self.print_step("Booting installer")
> +        self.boot(img_tmp, extra_args = [
> +            "-machine", "graphics=off",
> +            "-cdrom", iso
> +        ])
> +        self.console_init(300)
> +        self.console_wait("installation process.")
> +        time.sleep(0.3)
> +        self.console_send("\t")
> +        time.sleep(0.3)
> +        self.console_send(" console=ttyS0")
> +        proxy = os.environ.get("http_proxy")
> +        if not proxy is None:
> +            self.console_send(" proxy=%s" % proxy)
> +        self.console_send(" inst.repo=%s" % self.repo)
> +        self.console_send("\n")
> +
> +        self.console_wait_send("2) Use text mode",         "2\n")
> +
> +        self.console_wait_send("5) [!] Installation Dest", "5\n")
> +        self.console_wait_send("1) [x]",                   "c\n")
> +        self.console_wait_send("2) [ ] Use All Space",     "2\n")
> +        self.console_wait_send("2) [x] Use All Space",     "c\n")
> +        self.console_wait_send("1) [ ] Standard Part",     "1\n")
> +        self.console_wait_send("1) [x] Standard Part",     "c\n")
> +
> +        self.console_wait_send("7) [!] Root password",     "7\n")
> +        self.console_wait("Password:")
> +        self.console_send("%s\n" % self.ROOT_PASS)
> +        self.console_wait("Password (confirm):")
> +        self.console_send("%s\n" % self.ROOT_PASS)
> +
> +        self.console_wait_send("8) [ ] User creation",     "8\n")
> +        self.console_wait_send("1) [ ] Create user",       "1\n")
> +        self.console_wait_send("3) User name",             "3\n")
> +        self.console_wait_send("ENTER:", "%s\n" % self.GUEST_USER)
> +        self.console_wait_send("4) [ ] Use password",      "4\n")
> +        self.console_wait_send("5) Password",              "5\n")
> +        self.console_wait("Password:")
> +        self.console_send("%s\n" % self.GUEST_PASS)
> +        self.console_wait("Password (confirm):")
> +        self.console_send("%s\n" % self.GUEST_PASS)
> +        self.console_wait_send("7) Groups",                "c\n")
> +
> +        while True:
> +            good = self.console_wait("3) [x] Installation",
> +                                     "3) [!] Installation")
> +            self.console_send("r\n")
> +            if good:
> +                break
> +            time.sleep(10)
> +
> +        while True:
> +            good = self.console_wait("4) [x] Software",
> +                                     "4) [!] Software")
> +            self.console_send("r\n")
> +            if good:
> +                break
> +            time.sleep(10)
> +            self.console_send("r\n" % self.GUEST_PASS)
> +
> +        self.console_wait_send("'b' to begin install",     "b\n")
> +
> +        self.print_step("Installation started now, this will take a while")
> +
> +        self.console_wait_send("Installation complete",    "\n")
> +        self.print_step("Installation finished, rebooting")
> +
> +        # setup qemu user
> +        prompt = " ~]$"
> +        self.console_ssh_init(prompt, self.GUEST_USER, self.GUEST_PASS)
> +        self.console_wait_send(prompt, "exit\n")
> +
> +        # setup root user
> +        prompt = " ~]#"
> +        self.console_ssh_init(prompt, "root", self.ROOT_PASS)
> +        self.console_sshd_config(prompt)
> +
> +        # setup virtio-blk #1 (tarfile)
> +        self.console_wait(prompt)
> +        self.console_send("echo 'KERNEL==\"vdb\" MODE=\"666\"' >> %s\n" %
> +                          "/etc/udev/rules.d/99-qemu.rules")
> +
> +        self.print_step("Configuration finished, rebooting")
> +        self.console_wait_send(prompt, "reboot\n")
> +        self.console_wait("login:")
> +        self.wait_ssh()
> +
> +        self.print_step("Installing packages")
> +        self.ssh_root_check("rm -vf /etc/yum.repos.d/fedora*.repo\n")
> +        self.ssh_root_check("echo '[fedora]' >> /etc/yum.repos.d/qemu.repo\n")
> +        self.ssh_root_check("echo 'baseurl=%s' >> /etc/yum.repos.d/qemu.repo\n" % self.full)
> +        self.ssh_root_check("echo 'gpgcheck=0' >> /etc/yum.repos.d/qemu.repo\n")
> +        self.ssh_root_check("dnf install -y %s\n" % " ".join(self.pkgs))
> +
> +        # shutdown
> +        self.ssh_root("poweroff")
> +        self.console_wait("sleep state S5")
> +        self.wait()
> +
> +        if os.path.exists(img):
> +            os.remove(img)
> +        os.rename(img_tmp, img)
> +        os.remove(iso)
> +        self.print_step("All done")
> +
> +if __name__ == "__main__":
> +    sys.exit(basevm.main(FedoraVM))
> 

Maybe you could also do a kickstart installation instead?

 Thomas
Gerd Hoffmann May 9, 2019, 1:10 p.m. UTC | #2
Hi,

> > +        # shutdown
> > +        self.ssh_root("poweroff")
> > +        self.console_wait("sleep state S5")
> > +        self.wait()
> > +
> > +        if os.path.exists(img):
> > +            os.remove(img)
> > +        os.rename(img_tmp, img)
> > +        os.remove(iso)
> > +        self.print_step("All done")
> > +
> > +if __name__ == "__main__":
> > +    sys.exit(basevm.main(FedoraVM))

> Maybe you could also do a kickstart installation instead?

Well, the tricky part is how to get the kickstart/autoinstall/whatever
file passed to the guest then, in a way that works for every guest ...

cheers,
  Gerd
Daniel P. Berrangé May 9, 2019, 1:23 p.m. UTC | #3
On Thu, May 09, 2019 at 03:10:03PM +0200, Gerd Hoffmann wrote:
>   Hi,
> 
> > > +        # shutdown
> > > +        self.ssh_root("poweroff")
> > > +        self.console_wait("sleep state S5")
> > > +        self.wait()
> > > +
> > > +        if os.path.exists(img):
> > > +            os.remove(img)
> > > +        os.rename(img_tmp, img)
> > > +        os.remove(iso)
> > > +        self.print_step("All done")
> > > +
> > > +if __name__ == "__main__":
> > > +    sys.exit(basevm.main(FedoraVM))
> 
> > Maybe you could also do a kickstart installation instead?
> 
> Well, the tricky part is how to get the kickstart/autoinstall/whatever
> file passed to the guest then, in a way that works for every guest ...

Libosinfo reports use of cdrom, disk, floppy or initrd injection for
providing the installer automation file. Distros support one of more
of these methods, but there's no single one that works everywhere
afaik.

FWIW, virt-install recently got ability to auto-install images using
the libosinfo installer files. We don't have coverage for any BSD's
though in libosinfo at this time though.

Regards,
Daniel

[1] https://gitlab.com/libosinfo/osinfo-db/tree/master/data/install-script
    The <injection-method> element in the XML files
diff mbox series

Patch

diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py
index a27d2c72f5f5..ffd687578b5c 100755
--- a/tests/vm/basevm.py
+++ b/tests/vm/basevm.py
@@ -215,7 +215,7 @@  class BaseVM(object):
             # log console line
             sys.stderr.write("con recv: %s\n" % line)
 
-    def console_wait(self, expect):
+    def console_wait(self, expect, expectalt = None):
         vm = self._guest
         output = ""
         while True:
@@ -224,6 +224,8 @@  class BaseVM(object):
             except socket.timeout:
                 sys.stderr.write("console: *** read timeout ***\n")
                 sys.stderr.write("console: waiting for: '%s'\n" % expect)
+                if not expectalt is None:
+                    sys.stderr.write("console: waiting for: '%s' (alt)\n" % expectalt)
                 sys.stderr.write("console: line buffer:\n")
                 sys.stderr.write("\n")
                 self.console_log(output.rstrip())
@@ -232,6 +234,8 @@  class BaseVM(object):
             output += chars
             if expect in output:
                 break
+            if not expectalt is None and expectalt in output:
+                break
             if "\r" in output or "\n" in output:
                 lines = re.split("[\r\n]", output)
                 output = lines.pop()
@@ -239,6 +243,9 @@  class BaseVM(object):
                     self.console_log("\n".join(lines))
         if self.debug:
             self.console_log(output)
+        if not expectalt is None and expectalt in output:
+            return False
+        return True
 
     def console_send(self, command):
         vm = self._guest
diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include
index 8714b5947958..6e24ee786910 100644
--- a/tests/vm/Makefile.include
+++ b/tests/vm/Makefile.include
@@ -2,7 +2,7 @@ 
 
 .PHONY: vm-build-all vm-clean-all
 
-IMAGES := ubuntu.i386 freebsd netbsd openbsd centos
+IMAGES := ubuntu.i386 freebsd netbsd openbsd centos fedora
 IMAGES_DIR := $(HOME)/.cache/qemu-vm/images
 IMAGE_FILES := $(patsubst %, $(IMAGES_DIR)/%.img, $(IMAGES))
 
@@ -16,6 +16,7 @@  vm-test:
 	@echo "  vm-build-netbsd                 - Build QEMU in NetBSD VM"
 	@echo "  vm-build-openbsd                - Build QEMU in OpenBSD VM"
 	@echo "  vm-build-centos                 - Build QEMU in CentOS VM, with Docker"
+	@echo "  vm-build-fedora                 - Build QEMU in Fedora VM"
 	@echo ""
 	@echo "  vm-build-all                    - Build QEMU in all VMs"
 	@echo "  vm-clean-all                    - Clean up VM images"
diff --git a/tests/vm/fedora b/tests/vm/fedora
new file mode 100755
index 000000000000..d8cb62c803a4
--- /dev/null
+++ b/tests/vm/fedora
@@ -0,0 +1,187 @@ 
+#!/usr/bin/env python
+#
+# FreeBSD VM image
+#
+# Copyright 2017 Red Hat Inc.
+#
+# Authors:
+#  Fam Zheng <famz@redhat.com>
+#
+# This code is licensed under the GPL version 2 or later.  See
+# the COPYING file in the top-level directory.
+#
+
+import os
+import re
+import sys
+import time
+import socket
+import subprocess
+import basevm
+
+class FedoraVM(basevm.BaseVM):
+    name = "fedora"
+    arch = "x86_64"
+
+    base = "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/"
+    link = base + "Server/x86_64/iso/Fedora-Server-netinst-x86_64-30-1.2.iso"
+    repo = base + "Server/x86_64/os/"
+    full = base + "Everything/x86_64/os/"
+    csum = "5e4eac4566d8c572bfb3bcf54b7d6c82006ec3c6c882a2c9235c6d3494d7b100"
+    size = "20G"
+    pkgs = [
+        # tools
+        'git',
+        'flex', 'bison',
+        'gcc', 'binutils', 'make',
+
+        # perl
+        'perl-Test-Harness',
+
+        # libs: usb
+        '"pkgconfig(libusb-1.0)"',
+        '"pkgconfig(libusbredirparser-0.5)"',
+
+        # libs: crypto
+        '"pkgconfig(gnutls)"',
+
+        # libs: ui
+        '"pkgconfig(sdl2)"',
+        '"pkgconfig(gtk+-3.0)"',
+        '"pkgconfig(ncursesw)"',
+
+        # libs: audio
+        '"pkgconfig(libpulse)"',
+        '"pkgconfig(alsa)"',
+    ]
+
+    BUILD_SCRIPT = """
+        set -e;
+        rm -rf /home/qemu/qemu-test.*
+        cd $(mktemp -d /home/qemu/qemu-test.XXXXXX);
+        mkdir src build; cd src;
+        tar -xf /dev/vdb;
+        cd ../build
+        ../src/configure --python=python3 {configure_opts};
+        gmake --output-sync -j{jobs} {target} {verbose};
+    """
+
+    def build_image(self, img):
+        self.print_step("Downloading install iso")
+        cimg = self._download_with_cache(self.link, sha256sum=self.csum)
+        img_tmp = img + ".tmp"
+        iso = img + ".install.iso"
+
+        self.print_step("Preparing iso and disk image")
+        subprocess.check_call(["cp", "-f", cimg, iso])
+        subprocess.check_call(["qemu-img", "create", "-f", "qcow2",
+                               img_tmp, self.size])
+
+        self.print_step("Booting installer")
+        self.boot(img_tmp, extra_args = [
+            "-machine", "graphics=off",
+            "-cdrom", iso
+        ])
+        self.console_init(300)
+        self.console_wait("installation process.")
+        time.sleep(0.3)
+        self.console_send("\t")
+        time.sleep(0.3)
+        self.console_send(" console=ttyS0")
+        proxy = os.environ.get("http_proxy")
+        if not proxy is None:
+            self.console_send(" proxy=%s" % proxy)
+        self.console_send(" inst.repo=%s" % self.repo)
+        self.console_send("\n")
+
+        self.console_wait_send("2) Use text mode",         "2\n")
+
+        self.console_wait_send("5) [!] Installation Dest", "5\n")
+        self.console_wait_send("1) [x]",                   "c\n")
+        self.console_wait_send("2) [ ] Use All Space",     "2\n")
+        self.console_wait_send("2) [x] Use All Space",     "c\n")
+        self.console_wait_send("1) [ ] Standard Part",     "1\n")
+        self.console_wait_send("1) [x] Standard Part",     "c\n")
+
+        self.console_wait_send("7) [!] Root password",     "7\n")
+        self.console_wait("Password:")
+        self.console_send("%s\n" % self.ROOT_PASS)
+        self.console_wait("Password (confirm):")
+        self.console_send("%s\n" % self.ROOT_PASS)
+
+        self.console_wait_send("8) [ ] User creation",     "8\n")
+        self.console_wait_send("1) [ ] Create user",       "1\n")
+        self.console_wait_send("3) User name",             "3\n")
+        self.console_wait_send("ENTER:", "%s\n" % self.GUEST_USER)
+        self.console_wait_send("4) [ ] Use password",      "4\n")
+        self.console_wait_send("5) Password",              "5\n")
+        self.console_wait("Password:")
+        self.console_send("%s\n" % self.GUEST_PASS)
+        self.console_wait("Password (confirm):")
+        self.console_send("%s\n" % self.GUEST_PASS)
+        self.console_wait_send("7) Groups",                "c\n")
+
+        while True:
+            good = self.console_wait("3) [x] Installation",
+                                     "3) [!] Installation")
+            self.console_send("r\n")
+            if good:
+                break
+            time.sleep(10)
+
+        while True:
+            good = self.console_wait("4) [x] Software",
+                                     "4) [!] Software")
+            self.console_send("r\n")
+            if good:
+                break
+            time.sleep(10)
+            self.console_send("r\n" % self.GUEST_PASS)
+
+        self.console_wait_send("'b' to begin install",     "b\n")
+
+        self.print_step("Installation started now, this will take a while")
+
+        self.console_wait_send("Installation complete",    "\n")
+        self.print_step("Installation finished, rebooting")
+
+        # setup qemu user
+        prompt = " ~]$"
+        self.console_ssh_init(prompt, self.GUEST_USER, self.GUEST_PASS)
+        self.console_wait_send(prompt, "exit\n")
+
+        # setup root user
+        prompt = " ~]#"
+        self.console_ssh_init(prompt, "root", self.ROOT_PASS)
+        self.console_sshd_config(prompt)
+
+        # setup virtio-blk #1 (tarfile)
+        self.console_wait(prompt)
+        self.console_send("echo 'KERNEL==\"vdb\" MODE=\"666\"' >> %s\n" %
+                          "/etc/udev/rules.d/99-qemu.rules")
+
+        self.print_step("Configuration finished, rebooting")
+        self.console_wait_send(prompt, "reboot\n")
+        self.console_wait("login:")
+        self.wait_ssh()
+
+        self.print_step("Installing packages")
+        self.ssh_root_check("rm -vf /etc/yum.repos.d/fedora*.repo\n")
+        self.ssh_root_check("echo '[fedora]' >> /etc/yum.repos.d/qemu.repo\n")
+        self.ssh_root_check("echo 'baseurl=%s' >> /etc/yum.repos.d/qemu.repo\n" % self.full)
+        self.ssh_root_check("echo 'gpgcheck=0' >> /etc/yum.repos.d/qemu.repo\n")
+        self.ssh_root_check("dnf install -y %s\n" % " ".join(self.pkgs))
+
+        # shutdown
+        self.ssh_root("poweroff")
+        self.console_wait("sleep state S5")
+        self.wait()
+
+        if os.path.exists(img):
+            os.remove(img)
+        os.rename(img_tmp, img)
+        os.remove(iso)
+        self.print_step("All done")
+
+if __name__ == "__main__":
+    sys.exit(basevm.main(FedoraVM))