diff --git a/software/software/constants.py b/software/software/constants.py index a80f43dc..83a74445 100644 --- a/software/software/constants.py +++ b/software/software/constants.py @@ -109,6 +109,7 @@ OSTREE_REPO = 'ostree_repo' SYSROOT_OSTREE_REF = "debian:starlingx" OSTREE_CONFIG = "config" OSTREE_GPG_VERIFY = "gpg-verify" +OSTREE_ACTIVE_DEPLOYMENT_TAG = "'* '" # Sysroot SYSROOT_OSTREE = "/sysroot/ostree/repo" @@ -262,3 +263,10 @@ RESERVED_WORDS_SET = {"OS_AUTH_TOKEN", "OS_AUTH_TYPE", "OS_AUTH_URL", "OS_ENDPOI "OS_PROJECT_NAME", "OS_REGION_NAME", "OS_SERVICE_TOKEN", "OS_USERNAME", "OS_USER_DOMAIN_NAME", "OS_SERVICE_TYPE", "OS_TENANT_ID", "OS_TENANT_NAME", "OS_USER_DOMAIN_ID", "SYSTEM_API_VERSION", "SYSTEM_URL"} + +# Mount bind +READ_ONLY_PERMISSION = "ro,noatime" +READ_WRITE_PERMISSION = "rw,noatime" +ETC = "/etc" +USR = "/usr" +USR_ETC = "/usr/etc" diff --git a/software/software/ostree_utils.py b/software/software/ostree_utils.py index 2ba6551e..affcb580 100644 --- a/software/software/ostree_utils.py +++ b/software/software/ostree_utils.py @@ -462,31 +462,77 @@ def fetch_pending_deployment(): return pending_deployment -def mount_new_deployment(deployment_dir): +def fetch_active_deployment(): """ - Unmount /usr and /etc from the file system and remount it to directory - /usr and /etc respectively - :param deployment_dir: a path on the filesystem which points to the pending - deployment - example: /ostree/deploy/debian/deploy/ + Fetch the deployment ID of the active deployment + :return: The deployment ID of the active deployment """ + + cmd = "ostree admin status | grep %s | awk '{printf $3}'" % constants.OSTREE_ACTIVE_DEPLOYMENT_TAG + try: - new_usr_mount_dir = "%s/usr" % (deployment_dir) - new_etc_mount_dir = "%s/etc" % (deployment_dir) - sh.mount("--bind", "-o", "ro,noatime", new_usr_mount_dir, "/usr") - sh.mount("--bind", "-o", "rw,noatime", new_etc_mount_dir, "/etc") + output = subprocess.run(cmd, shell=True, check=True, capture_output=True) + except subprocess.CalledProcessError as e: + msg = "Failed to fetch ostree admin status for active deployment." + info_msg = "OSTree Admin Status Error: return code: %s , Output: %s" \ + % (e.returncode, e.stderr.decode("utf-8")) + LOG.info(info_msg) + raise OSTreeCommandFail(msg) + + # Store the output of the above command in a string + active_deployment = output.stdout.decode('utf-8') + + return active_deployment + + +def create_bind_mount(source, target, permissions=constants.READ_ONLY_PERMISSION): + """ + Create a bind mount. + :param source: The source directory + :param target: The directory where the bind will be mounted + :param permissions: The access permissions + """ + LOG.info("Creating bind mount from: %s, to: %s, with permissions: %s" + % (source, target, permissions)) + + try: + sh.mount("--bind", "-o", permissions, source, target) except sh.ErrorReturnCode: - LOG.warning("Mount failed. Retrying to mount /usr and /etc again after 5 secs.") + LOG.warning("Mount failed. Retrying to mount %s again after 5 secs." % target) time.sleep(5) try: - sh.mount("--bind", "-o", "ro,noatime", new_usr_mount_dir, "/usr") - sh.mount("--bind", "-o", "rw,noatime", new_etc_mount_dir, "/etc") + sh.mount("--bind", "-o", permissions, source, target) except sh.ErrorReturnCode as e: - msg = "Failed to re-mount /usr and /etc." - info_msg = "OSTree Deployment Mount Error: Output: %s" \ - % (e.stderr.decode("utf-8")) + msg = "Failed to re-mount %s" % target + info_msg = "OSTree Deployment Mount Error: Output: %s" % (e.stderr.decode("utf-8")) LOG.warning(info_msg) raise OSTreeCommandFail(msg) + + +def mount_new_deployment(pending_dir, active_dir): + """ + Create the following mounts used by an inservice patch: + 1) /usr -> /usr + 2) /etc -> /etc + 3) /usr/etc -> /usr/etc + 4) /etc -> /etc + Also mounts K8s version files + :param pending_dir: a path on the filesystem which points to the pending + deployment (e.g.: /ostree/deploy/debian/deploy/) + :param active_dir: a path on the filesystem which points to the active + deployment + """ + try: + new_usr_mount_dir = "%s%s" % (pending_dir, constants.USR) + new_etc_mount_dir = "%s%s" % (pending_dir, constants.ETC) + new_usr_etc_mount_dir = "%s%s" % (pending_dir, constants.USR_ETC) + act_usr_etc_mount_dir = "%s%s" % (active_dir, constants.USR_ETC) + act_etc_mount_dir = "%s%s" % (active_dir, constants.ETC) + + create_bind_mount(new_usr_mount_dir, constants.USR) + create_bind_mount(new_etc_mount_dir, constants.ETC, constants.READ_WRITE_PERMISSION) + create_bind_mount(new_usr_etc_mount_dir, act_usr_etc_mount_dir) + create_bind_mount(new_etc_mount_dir, act_etc_mount_dir) finally: # Handle the switch from bind mounts to symlinks for K8s versions. # Can be removed once the switch is complete. diff --git a/software/software/software_agent.py b/software/software/software_agent.py index 118d5782..a9e4dabc 100644 --- a/software/software/software_agent.py +++ b/software/software/software_agent.py @@ -777,8 +777,10 @@ class PatchAgent(PatchService): try: pending_deployment = ostree_utils.fetch_pending_deployment() deployment_dir = constants.OSTREE_BASE_DEPLOYMENT_DIR + pending_deployment + active_deployment = ostree_utils.fetch_active_deployment() + active_dir = constants.OSTREE_BASE_DEPLOYMENT_DIR + active_deployment setflag(mount_pending_file) - ostree_utils.mount_new_deployment(deployment_dir) + ostree_utils.mount_new_deployment(deployment_dir, active_dir) clearflag(mount_pending_file) LOG.info("Running post-install patch-scripts") subprocess.check_output([run_install_software_scripts_cmd, "postinstall"],