Skip to content

Commit

Permalink
[sonic_installer] Change sonic_installer check ASIC mismatch by platf…
Browse files Browse the repository at this point in the history
…orms 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
  • Loading branch information
wen587 committed Oct 25, 2021
1 parent 9017d99 commit 8ea834b
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 36 deletions.
26 changes: 25 additions & 1 deletion sonic_installer/bootloader/aboot.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,24 @@

from M2Crypto import X509

from sonic_py_common import device_info
from ..common import (
HOST_PATH,
IMAGE_DIR_PREFIX,
IMAGE_PREFIX,
ROOTFS_NAME,
run_command,
run_command_or_raise,
default_sigpipe,
)
from .bootloader import Bootloader

_secureboot = None
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'
Expand Down Expand Up @@ -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:
Expand Down
51 changes: 25 additions & 26 deletions sonic_installer/bootloader/grub.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):

Expand Down Expand Up @@ -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):
Expand Down
7 changes: 1 addition & 6 deletions sonic_installer/bootloader/onie.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
7 changes: 7 additions & 0 deletions sonic_installer/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import subprocess
import sys
import signal

import click

Expand Down Expand Up @@ -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)

6 changes: 3 additions & 3 deletions sonic_installer/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 8ea834b

Please sign in to comment.