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