xref: /openbmc/qemu/tests/vm/centos.aarch64 (revision d322fe2daf1bd3572b7917f6beafbc1cfbfe50fe)
1*d322fe2dSRobert Foley#!/usr/bin/env python3
2*d322fe2dSRobert Foley#
3*d322fe2dSRobert Foley# Centos aarch64 image
4*d322fe2dSRobert Foley#
5*d322fe2dSRobert Foley# Copyright 2020 Linaro
6*d322fe2dSRobert Foley#
7*d322fe2dSRobert Foley# Authors:
8*d322fe2dSRobert Foley#  Robert Foley <robert.foley@linaro.org>
9*d322fe2dSRobert Foley#  Originally based on ubuntu.aarch64
10*d322fe2dSRobert Foley#
11*d322fe2dSRobert Foley# This code is licensed under the GPL version 2 or later.  See
12*d322fe2dSRobert Foley# the COPYING file in the top-level directory.
13*d322fe2dSRobert Foley#
14*d322fe2dSRobert Foley
15*d322fe2dSRobert Foleyimport os
16*d322fe2dSRobert Foleyimport sys
17*d322fe2dSRobert Foleyimport subprocess
18*d322fe2dSRobert Foleyimport basevm
19*d322fe2dSRobert Foleyimport time
20*d322fe2dSRobert Foleyimport traceback
21*d322fe2dSRobert Foleyimport aarch64vm
22*d322fe2dSRobert Foley
23*d322fe2dSRobert FoleyDEFAULT_CONFIG = {
24*d322fe2dSRobert Foley    'cpu'          : "max",
25*d322fe2dSRobert Foley    'machine'      : "virt,gic-version=max",
26*d322fe2dSRobert Foley    'install_cmds' : "yum install -y make git python3 gcc gcc-c++ flex bison, "\
27*d322fe2dSRobert Foley        "yum install -y glib2-devel pixman-devel zlib-devel, "\
28*d322fe2dSRobert Foley        "yum install -y perl-Test-Harness, "\
29*d322fe2dSRobert Foley        "alternatives --set python /usr/bin/python3, "\
30*d322fe2dSRobert Foley        "sudo dnf config-manager "\
31*d322fe2dSRobert Foley        "--add-repo=https://download.docker.com/linux/centos/docker-ce.repo,"\
32*d322fe2dSRobert Foley        "sudo dnf install --nobest -y docker-ce.aarch64,"\
33*d322fe2dSRobert Foley        "systemctl enable docker",
34*d322fe2dSRobert Foley    # We increase beyond the default time since during boot
35*d322fe2dSRobert Foley    # it can take some time (many seconds) to log into the VM.
36*d322fe2dSRobert Foley    'ssh_timeout'  : 60,
37*d322fe2dSRobert Foley}
38*d322fe2dSRobert Foley
39*d322fe2dSRobert Foleyclass CentosAarch64VM(basevm.BaseVM):
40*d322fe2dSRobert Foley    name = "centos.aarch64"
41*d322fe2dSRobert Foley    arch = "aarch64"
42*d322fe2dSRobert Foley    login_prompt = "localhost login:"
43*d322fe2dSRobert Foley    prompt = '[root@localhost ~]#'
44*d322fe2dSRobert Foley    image_name = "CentOS-8-aarch64-1905-dvd1.iso"
45*d322fe2dSRobert Foley    image_link = "http://mirrors.usc.edu/pub/linux/distributions/centos/8.0.1905/isos/aarch64/"
46*d322fe2dSRobert Foley    image_link += image_name
47*d322fe2dSRobert Foley    BUILD_SCRIPT = """
48*d322fe2dSRobert Foley        set -e;
49*d322fe2dSRobert Foley        cd $(mktemp -d);
50*d322fe2dSRobert Foley        sudo chmod a+r /dev/vdb;
51*d322fe2dSRobert Foley        tar --checkpoint=.10 -xf /dev/vdb;
52*d322fe2dSRobert Foley        ./configure {configure_opts};
53*d322fe2dSRobert Foley        make --output-sync {target} -j{jobs} {verbose};
54*d322fe2dSRobert Foley    """
55*d322fe2dSRobert Foley    def set_key_perm(self):
56*d322fe2dSRobert Foley        """Set permissions properly on certain files to allow
57*d322fe2dSRobert Foley           ssh access."""
58*d322fe2dSRobert Foley        self.console_wait_send(self.prompt,
59*d322fe2dSRobert Foley                               "/usr/sbin/restorecon -R -v /root/.ssh\n")
60*d322fe2dSRobert Foley        self.console_wait_send(self.prompt,
61*d322fe2dSRobert Foley                "/usr/sbin/restorecon -R -v "\
62*d322fe2dSRobert Foley                "/home/{}/.ssh\n".format(self._config["guest_user"]))
63*d322fe2dSRobert Foley
64*d322fe2dSRobert Foley    def create_kickstart(self):
65*d322fe2dSRobert Foley        """Generate the kickstart file used to generate the centos image."""
66*d322fe2dSRobert Foley        # Start with the template for the kickstart.
67*d322fe2dSRobert Foley        ks_file = "../tests/vm/centos-8-aarch64.ks"
68*d322fe2dSRobert Foley        subprocess.check_call("cp {} ./ks.cfg".format(ks_file), shell=True)
69*d322fe2dSRobert Foley        # Append the ssh keys to the kickstart file
70*d322fe2dSRobert Foley        # as the post processing phase of installation.
71*d322fe2dSRobert Foley        with open("ks.cfg", "a") as f:
72*d322fe2dSRobert Foley            # Add in the root pw and guest user.
73*d322fe2dSRobert Foley            rootpw = "rootpw --plaintext {}\n"
74*d322fe2dSRobert Foley            f.write(rootpw.format(self._config["root_pass"]))
75*d322fe2dSRobert Foley            add_user = "user --groups=wheel --name={} "\
76*d322fe2dSRobert Foley                       "--password={} --plaintext\n"
77*d322fe2dSRobert Foley            f.write(add_user.format(self._config["guest_user"],
78*d322fe2dSRobert Foley                                    self._config["guest_pass"]))
79*d322fe2dSRobert Foley            # Add the ssh keys.
80*d322fe2dSRobert Foley            f.write("%post --log=/root/ks-post.log\n")
81*d322fe2dSRobert Foley            f.write("mkdir -p /root/.ssh\n")
82*d322fe2dSRobert Foley            addkey = 'echo "{}" >> /root/.ssh/authorized_keys\n'
83*d322fe2dSRobert Foley            addkey_cmd = addkey.format(self._config["ssh_pub_key"])
84*d322fe2dSRobert Foley            f.write(addkey_cmd)
85*d322fe2dSRobert Foley            f.write('mkdir -p /home/{}/.ssh\n'.format(self._config["guest_user"]))
86*d322fe2dSRobert Foley            addkey = 'echo "{}" >> /home/{}/.ssh/authorized_keys\n'
87*d322fe2dSRobert Foley            addkey_cmd = addkey.format(self._config["ssh_pub_key"],
88*d322fe2dSRobert Foley                                       self._config["guest_user"])
89*d322fe2dSRobert Foley            f.write(addkey_cmd)
90*d322fe2dSRobert Foley            f.write("%end\n")
91*d322fe2dSRobert Foley        # Take our kickstart file and create an .iso from it.
92*d322fe2dSRobert Foley        # The .iso will be provided to qemu as we boot
93*d322fe2dSRobert Foley        # from the install dvd.
94*d322fe2dSRobert Foley        # Anaconda will recognize the label "OEMDRV" and will
95*d322fe2dSRobert Foley        # start the automated installation.
96*d322fe2dSRobert Foley        gen_iso_img = 'genisoimage -output ks.iso -volid "OEMDRV" ks.cfg'
97*d322fe2dSRobert Foley        subprocess.check_call(gen_iso_img, shell=True)
98*d322fe2dSRobert Foley
99*d322fe2dSRobert Foley    def wait_for_shutdown(self):
100*d322fe2dSRobert Foley        """We wait for qemu to shutdown the VM and exit.
101*d322fe2dSRobert Foley           While this happens we display the console view
102*d322fe2dSRobert Foley           for easier debugging."""
103*d322fe2dSRobert Foley        # The image creation is essentially done,
104*d322fe2dSRobert Foley        # so whether or not the wait is successful we want to
105*d322fe2dSRobert Foley        # wait for qemu to exit (the self.wait()) before we return.
106*d322fe2dSRobert Foley        try:
107*d322fe2dSRobert Foley            self.console_wait("reboot: Power down")
108*d322fe2dSRobert Foley        except Exception as e:
109*d322fe2dSRobert Foley            sys.stderr.write("Exception hit\n")
110*d322fe2dSRobert Foley            if isinstance(e, SystemExit) and e.code == 0:
111*d322fe2dSRobert Foley                return 0
112*d322fe2dSRobert Foley            traceback.print_exc()
113*d322fe2dSRobert Foley        finally:
114*d322fe2dSRobert Foley            self.wait()
115*d322fe2dSRobert Foley
116*d322fe2dSRobert Foley    def build_base_image(self, dest_img):
117*d322fe2dSRobert Foley        """Run through the centos installer to create
118*d322fe2dSRobert Foley           a base image with name dest_img."""
119*d322fe2dSRobert Foley        # We create the temp image, and only rename
120*d322fe2dSRobert Foley        # to destination when we are done.
121*d322fe2dSRobert Foley        img = dest_img + ".tmp"
122*d322fe2dSRobert Foley        # Create an empty image.
123*d322fe2dSRobert Foley        # We will provide this as the install destination.
124*d322fe2dSRobert Foley        qemu_img_create = "qemu-img create {} 50G".format(img)
125*d322fe2dSRobert Foley        subprocess.check_call(qemu_img_create, shell=True)
126*d322fe2dSRobert Foley
127*d322fe2dSRobert Foley        # Create our kickstart file to be fed to the installer.
128*d322fe2dSRobert Foley        self.create_kickstart()
129*d322fe2dSRobert Foley        # Boot the install dvd with the params as our ks.iso
130*d322fe2dSRobert Foley        os_img = self._download_with_cache(self.image_link)
131*d322fe2dSRobert Foley        dvd_iso = "centos-8-dvd.iso"
132*d322fe2dSRobert Foley        subprocess.check_call(["cp", "-f", os_img, dvd_iso])
133*d322fe2dSRobert Foley        extra_args = "-cdrom ks.iso"
134*d322fe2dSRobert Foley        extra_args += " -drive file={},if=none,id=drive1,cache=writeback"
135*d322fe2dSRobert Foley        extra_args += " -device virtio-blk,drive=drive1,bootindex=1"
136*d322fe2dSRobert Foley        extra_args = extra_args.format(dvd_iso).split(" ")
137*d322fe2dSRobert Foley        self.boot(img, extra_args=extra_args)
138*d322fe2dSRobert Foley        self.console_wait_send("change the selection", "\n")
139*d322fe2dSRobert Foley        # We seem to need to hit esc (chr(27)) twice to abort the
140*d322fe2dSRobert Foley        # media check, which takes a long time.
141*d322fe2dSRobert Foley        # Waiting a bit seems to be more reliable before hitting esc.
142*d322fe2dSRobert Foley        self.console_wait("Checking")
143*d322fe2dSRobert Foley        time.sleep(5)
144*d322fe2dSRobert Foley        self.console_wait_send("Checking", chr(27))
145*d322fe2dSRobert Foley        time.sleep(5)
146*d322fe2dSRobert Foley        self.console_wait_send("Checking", chr(27))
147*d322fe2dSRobert Foley        print("Found Checking")
148*d322fe2dSRobert Foley        # Give sufficient time for the installer to create the image.
149*d322fe2dSRobert Foley        self.console_init(timeout=7200)
150*d322fe2dSRobert Foley        self.wait_for_shutdown()
151*d322fe2dSRobert Foley        os.rename(img, dest_img)
152*d322fe2dSRobert Foley        print("Done with base image build: {}".format(dest_img))
153*d322fe2dSRobert Foley
154*d322fe2dSRobert Foley    def check_create_base_img(self, img_base, img_dest):
155*d322fe2dSRobert Foley        """Create a base image using the installer.
156*d322fe2dSRobert Foley           We will use the base image if it exists.
157*d322fe2dSRobert Foley           This helps cut down on install time in case we
158*d322fe2dSRobert Foley           need to restart image creation,
159*d322fe2dSRobert Foley           since the base image creation can take a long time."""
160*d322fe2dSRobert Foley        if not os.path.exists(img_base):
161*d322fe2dSRobert Foley            print("Generate new base image: {}".format(img_base))
162*d322fe2dSRobert Foley            self.build_base_image(img_base);
163*d322fe2dSRobert Foley        else:
164*d322fe2dSRobert Foley            print("Use existing base image: {}".format(img_base))
165*d322fe2dSRobert Foley        # Save a copy of the base image and copy it to dest.
166*d322fe2dSRobert Foley        # which we will use going forward.
167*d322fe2dSRobert Foley        subprocess.check_call(["cp", img_base, img_dest])
168*d322fe2dSRobert Foley
169*d322fe2dSRobert Foley    def boot(self, img, extra_args=None):
170*d322fe2dSRobert Foley        aarch64vm.create_flash_images(self._tmpdir, self._efi_aarch64)
171*d322fe2dSRobert Foley        default_args = aarch64vm.get_pflash_args(self._tmpdir)
172*d322fe2dSRobert Foley        if extra_args:
173*d322fe2dSRobert Foley            extra_args.extend(default_args)
174*d322fe2dSRobert Foley        else:
175*d322fe2dSRobert Foley            extra_args = default_args
176*d322fe2dSRobert Foley        # We always add these performance tweaks
177*d322fe2dSRobert Foley        # because without them, we boot so slowly that we
178*d322fe2dSRobert Foley        # can time out finding the boot efi device.
179*d322fe2dSRobert Foley        if '-smp' not in extra_args and \
180*d322fe2dSRobert Foley           '-smp' not in self._config['extra_args'] and \
181*d322fe2dSRobert Foley           '-smp' not in self._args:
182*d322fe2dSRobert Foley            # Only add if not already there to give caller option to change it.
183*d322fe2dSRobert Foley            extra_args.extend(["-smp", "8"])
184*d322fe2dSRobert Foley        # We have overridden boot() since aarch64 has additional parameters.
185*d322fe2dSRobert Foley        # Call down to the base class method.
186*d322fe2dSRobert Foley        super(CentosAarch64VM, self).boot(img, extra_args=extra_args)
187*d322fe2dSRobert Foley
188*d322fe2dSRobert Foley    def build_image(self, img):
189*d322fe2dSRobert Foley        img_tmp = img + ".tmp"
190*d322fe2dSRobert Foley        self.check_create_base_img(img + ".base", img_tmp)
191*d322fe2dSRobert Foley
192*d322fe2dSRobert Foley        # Boot the new image for the first time to finish installation.
193*d322fe2dSRobert Foley        self.boot(img_tmp)
194*d322fe2dSRobert Foley        self.console_init()
195*d322fe2dSRobert Foley        self.console_wait_send(self.login_prompt, "root\n")
196*d322fe2dSRobert Foley        self.console_wait_send("Password:",
197*d322fe2dSRobert Foley                               "{}\n".format(self._config["root_pass"]))
198*d322fe2dSRobert Foley
199*d322fe2dSRobert Foley        self.set_key_perm()
200*d322fe2dSRobert Foley        self.console_wait_send(self.prompt, "rpm -q centos-release\n")
201*d322fe2dSRobert Foley        enable_adapter = "sed -i 's/ONBOOT=no/ONBOOT=yes/g'" \
202*d322fe2dSRobert Foley                         " /etc/sysconfig/network-scripts/ifcfg-enp0s1\n"
203*d322fe2dSRobert Foley        self.console_wait_send(self.prompt, enable_adapter)
204*d322fe2dSRobert Foley        self.console_wait_send(self.prompt, "ifup enp0s1\n")
205*d322fe2dSRobert Foley        self.console_wait_send(self.prompt,
206*d322fe2dSRobert Foley                               'echo "qemu  ALL=(ALL) NOPASSWD:ALL" | '\
207*d322fe2dSRobert Foley                               'sudo tee /etc/sudoers.d/qemu\n')
208*d322fe2dSRobert Foley        self.console_wait(self.prompt)
209*d322fe2dSRobert Foley
210*d322fe2dSRobert Foley        # Rest of the commands we issue through ssh.
211*d322fe2dSRobert Foley        self.wait_ssh(wait_root=True)
212*d322fe2dSRobert Foley
213*d322fe2dSRobert Foley        # If the user chooses *not* to do the second phase,
214*d322fe2dSRobert Foley        # then we will jump right to the graceful shutdown
215*d322fe2dSRobert Foley        if self._config['install_cmds'] != "":
216*d322fe2dSRobert Foley            install_cmds = self._config['install_cmds'].split(',')
217*d322fe2dSRobert Foley            for cmd in install_cmds:
218*d322fe2dSRobert Foley                self.ssh_root(cmd)
219*d322fe2dSRobert Foley        self.ssh_root("poweroff")
220*d322fe2dSRobert Foley        self.wait_for_shutdown()
221*d322fe2dSRobert Foley        os.rename(img_tmp, img)
222*d322fe2dSRobert Foley        print("image creation complete: {}".format(img))
223*d322fe2dSRobert Foley        return 0
224*d322fe2dSRobert Foley
225*d322fe2dSRobert Foleyif __name__ == "__main__":
226*d322fe2dSRobert Foley    defaults = aarch64vm.get_config_defaults(CentosAarch64VM, DEFAULT_CONFIG)
227*d322fe2dSRobert Foley    sys.exit(basevm.main(CentosAarch64VM, defaults))
228