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