From 8ea834b979dc35d79b15c433bd4d9559205f412e Mon Sep 17 00:00:00 2001 From: jingwenxie Date: Mon, 25 Oct 2021 10:14:21 +0800 Subject: [PATCH] [sonic_installer] Change sonic_installer check ASIC mismatch by platforms list (#1836) What I did Handle wrong image type installation by checking platform. Before the verification was done by comparing image's ASIC and running platform's ASIC. But the running's ASIC will change if wrong image was installed. Add the same support for Aboot image verification. How I did it Add a devices list file for the same ASIC while build the image and Check by devices list instead of machine.conf How to verify it Install a bin file which differs the running platform's ASIC. For example; install sonic-broadcom.bin to mellanox ASIC platform install sonic-vs.bin to broadcom ASIC platform install sonic-aboot-barefoot.swi to broadcom ASIC platform --- sonic_installer/bootloader/aboot.py | 26 ++++++++++++++- sonic_installer/bootloader/grub.py | 51 ++++++++++++++--------------- sonic_installer/bootloader/onie.py | 7 +--- sonic_installer/common.py | 7 ++++ sonic_installer/main.py | 6 ++-- 5 files changed, 61 insertions(+), 36 deletions(-) diff --git a/sonic_installer/bootloader/aboot.py b/sonic_installer/bootloader/aboot.py index a44c44fdd785..ab4c0ff38c23 100644 --- a/sonic_installer/bootloader/aboot.py +++ b/sonic_installer/bootloader/aboot.py @@ -15,6 +15,7 @@ from M2Crypto import X509 +from sonic_py_common import device_info from ..common import ( HOST_PATH, IMAGE_DIR_PREFIX, @@ -22,6 +23,7 @@ ROOTFS_NAME, run_command, run_command_or_raise, + default_sigpipe, ) from .bootloader import Bootloader @@ -29,6 +31,8 @@ DEFAULT_SWI_IMAGE = 'sonic.swi' KERNEL_CMDLINE_NAME = 'kernel-cmdline' +UNZIP_MISSING_FILE = 11 + # For the signature format, see: https://github.com/aristanetworks/swi-tools/tree/master/switools SWI_SIG_FILE_NAME = 'swi-signature' SWIX_SIG_FILE_NAME = 'swix-signature' @@ -164,7 +168,27 @@ def get_binary_image_version(self, image_path): return IMAGE_PREFIX + version.strip() def verify_image_platform(self, image_path): - return os.path.isfile(image_path) + if not os.path.isfile(image_path): + return False + + # Get running platform + platform = device_info.get_platform() + + # If .platforms_asic is not existed, unzip will return code 11. + # Return True for backward compatibility. + # Otherwise, we grep to see if current platform is inside the + # supported target platforms list. + with open(os.devnull, 'w') as fnull: + p1 = subprocess.Popen(['/usr/bin/unzip', '-qop', image_path, '.platforms_asic'], stdout=subprocess.PIPE, stderr=fnull, preexec_fn=default_sigpipe) + p2 = subprocess.Popen(['grep', '-Fxq', '-m 1', platform], stdin=p1.stdout, preexec_fn=default_sigpipe) + + p1.wait() + if p1.returncode == UNZIP_MISSING_FILE: + return True + + # Code 0 is returned by grep as a result of found + p2.wait() + return p2.returncode == 0 def verify_secureboot_image(self, image_path): try: diff --git a/sonic_installer/bootloader/grub.py b/sonic_installer/bootloader/grub.py index 0202da76bd81..11ee3de1f4c8 100644 --- a/sonic_installer/bootloader/grub.py +++ b/sonic_installer/bootloader/grub.py @@ -14,11 +14,11 @@ IMAGE_DIR_PREFIX, IMAGE_PREFIX, run_command, + default_sigpipe, ) from .onie import OnieInstallerBootloader -from .onie import default_sigpipe -MACHINE_CONF = "installer/machine.conf" +PLATFORMS_ASIC = "installer/platforms_asic" class GrubBootloader(OnieInstallerBootloader): @@ -85,35 +85,34 @@ def remove_image(self, image): run_command('grub-set-default --boot-directory=' + HOST_PATH + ' 0') click.echo('Image removed') + def platform_in_platforms_asic(self, platform, image_path): + """ + For those images that don't have devices list builtin, 'tar' will have non-zero returncode. + In this case, we simply return True to make it worked compatible as before. + Otherwise, we can grep to check if platform is inside the supported target platforms list. + """ + with open(os.devnull, 'w') as fnull: + p1 = subprocess.Popen(["sed", "-e", "1,/^exit_marker$/d", image_path], stdout=subprocess.PIPE, preexec_fn=default_sigpipe) + p2 = subprocess.Popen(["tar", "xf", "-", PLATFORMS_ASIC, "-O"], stdin=p1.stdout, stdout=subprocess.PIPE, stderr=fnull, preexec_fn=default_sigpipe) + p3 = subprocess.Popen(["grep", "-Fxq", "-m 1", platform], stdin=p2.stdout, preexec_fn=default_sigpipe) + + p2.wait() + if p2.returncode != 0: + return True + + # Code 0 is returned by grep as a result of found + p3.wait() + return p3.returncode ==0 + def verify_image_platform(self, image_path): if not os.path.isfile(image_path): return False - # Get running platform's ASIC - try: - version_info = device_info.get_sonic_version_info() - if version_info: - asic_type = version_info['asic_type'] - else: - asic_type = None - except (KeyError, TypeError) as e: - click.echo("Caught an exception: " + str(e)) - - # Get installing image's ASIC - p1 = subprocess.Popen(["sed", "-e", "1,/^exit_marker$/d", image_path], stdout=subprocess.PIPE, preexec_fn=default_sigpipe) - p2 = subprocess.Popen(["tar", "xf", "-", MACHINE_CONF, "-O"], stdin=p1.stdout, stdout=subprocess.PIPE, preexec_fn=default_sigpipe) - p3 = subprocess.Popen(["sed", "-n", r"s/^machine=\(.*\)/\1/p"], stdin=p2.stdout, stdout=subprocess.PIPE, preexec_fn=default_sigpipe, text=True) - - stdout = p3.communicate()[0] - image_asic = stdout.rstrip('\n') - - # Return false if machine is not found or unexpected issue occur - if not image_asic: - return False + # Get running platform + platform = device_info.get_platform() - if asic_type == image_asic: - return True - return False + # Check if platform is inside image's target platforms + return self.platform_in_platforms_asic(platform, image_path) @classmethod def detect(cls): diff --git a/sonic_installer/bootloader/onie.py b/sonic_installer/bootloader/onie.py index aa23c347a21e..be17ba56192a 100644 --- a/sonic_installer/bootloader/onie.py +++ b/sonic_installer/bootloader/onie.py @@ -4,20 +4,15 @@ import os import re -import signal import subprocess from ..common import ( IMAGE_DIR_PREFIX, IMAGE_PREFIX, + default_sigpipe, ) from .bootloader import Bootloader -# Needed to prevent "broken pipe" error messages when piping -# output of multiple commands using subprocess.Popen() -def default_sigpipe(): - signal.signal(signal.SIGPIPE, signal.SIG_DFL) - class OnieInstallerBootloader(Bootloader): # pylint: disable=abstract-method DEFAULT_IMAGE_PATH = '/tmp/sonic_image' diff --git a/sonic_installer/common.py b/sonic_installer/common.py index 5e36cedb8c65..685063587ccf 100644 --- a/sonic_installer/common.py +++ b/sonic_installer/common.py @@ -5,6 +5,7 @@ import subprocess import sys +import signal import click @@ -41,3 +42,9 @@ def run_command_or_raise(argv, raise_exception=True): raise SonicRuntimeException("Failed to run command '{0}'".format(argv)) return out.rstrip("\n") + +# Needed to prevent "broken pipe" error messages when piping +# output of multiple commands using subprocess.Popen() +def default_sigpipe(): + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + diff --git a/sonic_installer/main.py b/sonic_installer/main.py index 72646531a9e1..1aaec8054ee2 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -533,15 +533,15 @@ def install(url, force, skip_platform_check=False, skip_migration=False, skip_pa raise click.Abort() else: # Verify not installing non-secure image in a secure running image - if not bootloader.verify_secureboot_image(image_path) and not force: + if not force and not bootloader.verify_secureboot_image(image_path): echo_and_log("Image file '{}' is of a different type than running image.\n".format(url) + "If you are sure you want to install this image, use -f|--force|--skip-secure-check.\n" + "Aborting...", LOG_ERR) raise click.Abort() # Verify that the binary image is of the same platform type as running platform - if not bootloader.verify_image_platform(image_path) and not skip_platform_check: - echo_and_log("Image file '{}' is of a different platform type than running platform.\n".format(url) + + if not skip_platform_check and not bootloader.verify_image_platform(image_path): + echo_and_log("Image file '{}' is of a different platform ASIC type than running platform's.\n".format(url) + "If you are sure you want to install this image, use --skip-platform-check.\n" + "Aborting...", LOG_ERR) raise click.Abort()