diff --git a/sonic_installer/bootloader/aboot.py b/sonic_installer/bootloader/aboot.py index a44c44fdd7..ab4c0ff38c 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 0202da76bd..11ee3de1f4 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 aa23c347a2..be17ba5619 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 5e36cedb8c..685063587c 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 72646531a9..1aaec8054e 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()