|
28 | 28 | CLOUD_INIT_SERVICE_UNIT_NAME = "cloud-init" |
29 | 29 | CLOUD_INIT_SERVICE_UNIT_NAME_FALLBACK = "cloud-init-main" |
30 | 30 |
|
| 31 | +FIRST_BOOT_SCRIPT_RUNNER = """#!/bin/bash |
| 32 | +function run_scripts { |
| 33 | + script_dir=$1 |
| 34 | +
|
| 35 | + for f in $script_dir/*.sh; do |
| 36 | + if [ -x "$f" ]; then |
| 37 | + echo "Invoking script: $f" |
| 38 | + "$f" |
| 39 | + echo "Exit code: $?" |
| 40 | + fi |
| 41 | + done |
| 42 | +} |
| 43 | +
|
| 44 | +# Run Coriolis provided scripts. |
| 45 | +run_scripts /usr/lib/coriolis/firstboot/service |
| 46 | +
|
| 47 | +# Run user provided scripts. |
| 48 | +run_scripts /usr/lib/coriolis/firstboot/user |
| 49 | +
|
| 50 | +mkdir -p /var/lib/coriolis |
| 51 | +touch /var/lib/coriolis/firstboot-complete |
| 52 | +""" |
| 53 | +FIRST_BOOT_SCRIPT_RUNNER_PATH = "/usr/lib/coriolis/firstboot/run-firstboot.sh" |
| 54 | +FIRST_BOOT_SYSTEMD_UNIT = """ |
| 55 | +[Unit] |
| 56 | +Description=Coriolis replica first-boot scripts. |
| 57 | +After=network-online.target |
| 58 | +Wants=network-online.target |
| 59 | +ConditionPathExists=!/var/lib/coriolis/firstboot-complete |
| 60 | +
|
| 61 | +[Service] |
| 62 | +Type=oneshot |
| 63 | +ExecStart=/usr/lib/coriolis/firstboot/run-firstboot.sh |
| 64 | +RemainAfterExit=yes |
| 65 | +
|
| 66 | +[Install] |
| 67 | +WantedBy=multi-user.target |
| 68 | +""" |
| 69 | +FIRST_BOOT_SYSTEMD_UNIT_NAME = "coriolis-firstboot.service" |
| 70 | +FIRST_BOOT_SYSTEMD_UNIT_PATH = ( |
| 71 | + f"/etc/systemd/system/{FIRST_BOOT_SYSTEMD_UNIT_NAME}") |
| 72 | + |
31 | 73 |
|
32 | 74 | class BaseOSMorphingTools(object, with_metaclass(abc.ABCMeta)): |
33 | 75 |
|
@@ -100,6 +142,15 @@ def get_packages(self): |
100 | 142 | def run_user_script(self, user_script): |
101 | 143 | pass |
102 | 144 |
|
| 145 | + @abc.abstractmethod |
| 146 | + def register_firstboot_script( |
| 147 | + self, |
| 148 | + script: str, |
| 149 | + index: int = 0, |
| 150 | + user_provided=True, |
| 151 | + ): |
| 152 | + pass |
| 153 | + |
103 | 154 | @abc.abstractmethod |
104 | 155 | def pre_packages_install(self, package_names): |
105 | 156 | pass |
@@ -719,3 +770,53 @@ def _setup_network_preservation(self, nics_info) -> None: |
719 | 770 | self._add_net_udev_rules(net_ifaces_info) |
720 | 771 |
|
721 | 772 | return |
| 773 | + |
| 774 | + def register_firstboot_script( |
| 775 | + self, |
| 776 | + script: str, |
| 777 | + index: int = 0, |
| 778 | + user_provided=True, |
| 779 | + ): |
| 780 | + if len(script) == 0: |
| 781 | + LOG.debug("Empty first-boot script, skipping...") |
| 782 | + return |
| 783 | + |
| 784 | + if user_provided: |
| 785 | + script_dir = "/usr/lib/coriolis/firstboot/user" |
| 786 | + else: |
| 787 | + script_dir = "/usr/lib/coriolis/firstboot/service" |
| 788 | + unique_id = str(uuid.uuid4()).split("-")[0] |
| 789 | + script_path = os.path.join(script_dir, f"{index:02d}-{unique_id}.sh") |
| 790 | + |
| 791 | + self._exec_cmd_chroot(f"mkdir -p {script_dir}") |
| 792 | + self._write_file_sudo(script_path, script) |
| 793 | + self._exec_cmd_chroot(f"chown root:root {script_path}") |
| 794 | + self._exec_cmd_chroot(f"chmod 755 {script_path}") |
| 795 | + |
| 796 | + # systemd unit used to launch first-boot scripts. |
| 797 | + if not self._test_path(FIRST_BOOT_SYSTEMD_UNIT_PATH): |
| 798 | + self._write_file_sudo( |
| 799 | + FIRST_BOOT_SYSTEMD_UNIT_PATH, FIRST_BOOT_SYSTEMD_UNIT) |
| 800 | + self._exec_cmd_chroot( |
| 801 | + "chown root:root %s" % FIRST_BOOT_SYSTEMD_UNIT_PATH) |
| 802 | + self._exec_cmd_chroot( |
| 803 | + "chmod 644 %s" % FIRST_BOOT_SYSTEMD_UNIT_PATH) |
| 804 | + wants_dir = "/etc/systemd/system/multi-user.target.wants" |
| 805 | + self._exec_cmd_chroot("mkdir -p %s" % wants_dir) |
| 806 | + self._exec_cmd_chroot( |
| 807 | + "ln -sf %s %s/%s" % ( |
| 808 | + FIRST_BOOT_SYSTEMD_UNIT_PATH, |
| 809 | + wants_dir, |
| 810 | + FIRST_BOOT_SYSTEMD_UNIT_NAME)) |
| 811 | + |
| 812 | + # A script that iterates over "/usr/lib/coriolis/firstboot/*.sh" |
| 813 | + # scripts and runs them. |
| 814 | + if not self._test_path(FIRST_BOOT_SCRIPT_RUNNER_PATH): |
| 815 | + self._write_file_sudo( |
| 816 | + FIRST_BOOT_SCRIPT_RUNNER_PATH, FIRST_BOOT_SCRIPT_RUNNER) |
| 817 | + self._exec_cmd_chroot( |
| 818 | + "chown root:root %s" % FIRST_BOOT_SCRIPT_RUNNER_PATH) |
| 819 | + self._exec_cmd_chroot( |
| 820 | + "chmod 755 %s" % FIRST_BOOT_SCRIPT_RUNNER_PATH) |
| 821 | + |
| 822 | + LOG.info(f"Registered first-boot script: {script_path}") |
0 commit comments