blob: c0379759abcdf6a3027d3f95ebbecfaf18ff5b4b [file] [log] [blame]
#!/usr/bin/env python3
# # -*- coding: utf-8 -*-
# Copyright 2021 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Create tarballs with modem FW, and upload them to Chromium OS Archive
Mirrors.
"""
import argparse
import logging
from distutils.dir_util import copy_tree
from enum import Enum
import os
import shutil
import sys
import tempfile
class PackageType(Enum):
"""Packaging options for different firmwares or cust packs."""
L850_MAIN_FW = 'l850-main-fw'
L850_OEM_FW = 'l850-oem-fw'
L850_OEM_DIR_ONLY = 'l850-oem-dir'
NL668_MAIN_FW = 'nl668-main-fw'
def __str__(self):
return self.value
MIRROR_PATH = 'gs://chromeos-localmirror/distfiles/'
FIBOCOM_TARBALL_PREFIX = 'cellular-firmware-fibocom-'
L850_TARBALL_PREFIX = FIBOCOM_TARBALL_PREFIX + 'l850-'
NL668_TARBALL_PREFIX = FIBOCOM_TARBALL_PREFIX + 'nl668-'
OEM_FW_PREFIX = 'OEM_cust.'
OEM_FW_POSTFIX = '_signed.fls3.xz'
class FwUploader(object):
"""Class to verify the files and upload the tarball to a gs bucket."""
def __init__(self, path, upload, tarball_dir_name):
self.path = os.path.abspath(path)
self.upload = upload
self.basename = os.path.basename(self.path)
self.tarball_dir_name = tarball_dir_name
def process_fw_and_upload(self):
if not self.validate():
return os.EX_USAGE
tempdir = tempfile.mkdtemp()
path_to_package = os.path.join(tempdir, self.tarball_dir_name)
os.mkdir(path_to_package)
if not self.prepare_files(self.path, path_to_package):
logging.error('Failed to prepare files for packaging')
return os.EX_OSFILE
os.chdir(tempdir)
tarball_name = '{0}.tar.xz'.format(self.tarball_dir_name)
os.system('tar -Ipixz -cf {0} {1}/'.format(tarball_name,
self.tarball_dir_name))
tarball_path = os.path.join(tempdir, tarball_name)
logging.info('Tarball created: %s', tarball_path)
if self.upload:
gs_bucket_path = os.path.join(MIRROR_PATH, tarball_name)
logging.info('Uploading file %s to %s', tarball_path,
gs_bucket_path)
os.system('gsutil cp -n -a public-read {0} {1}'.format(
tarball_path, gs_bucket_path))
logging.info('Removing temporary files')
shutil.rmtree(tempdir)
return os.EX_OK
class L850MainFw(FwUploader):
"""Uploader class for L850GL main FW."""
def __init__(self, path, upload):
super().__init__(path, upload, None)
self.tarball_dir_name = L850_TARBALL_PREFIX + self.basename.replace(
'.fls3.xz', '')
def validate(self):
main_fw_postfix = 'Secureboot.fls3.xz'
if not self.path.endswith(main_fw_postfix):
logging.error('The main FW file `%s` name does not match `*%s`',
self.path, main_fw_postfix)
return False
return True
@staticmethod
def prepare_files(fw_path, target_path):
logging.info('Copying %s into %s', fw_path, target_path)
shutil.copy(fw_path, target_path)
return True
class L850OemFw(FwUploader):
"""Uploader class for L850GL OEM FW."""
def __init__(self, path, upload):
super().__init__(path, upload, None)
self.tarball_dir_name = '{0}{1}'.format(
L850_TARBALL_PREFIX, self.basename.replace(OEM_FW_POSTFIX, ''))
def validate(self):
if not (self.basename.startswith(OEM_FW_PREFIX)
and self.basename.endswith(OEM_FW_POSTFIX)):
logging.error('The OEM FW file `%s` name does not match `%s*%s`',
self.basename, OEM_FW_PREFIX, OEM_FW_POSTFIX)
return False
return True
@staticmethod
def prepare_files(fw_path, target_path):
logging.info('Copying %s into %s', fw_path, target_path)
shutil.copy(fw_path, target_path)
return True
class L850OemDir(FwUploader):
"""Uploader class for L850GL cust packs directory."""
def __init__(self, path, upload, revision, board):
super().__init__(path, upload, None)
self.tarball_dir_name = '{0}{1}-carriers_OEM_{2}-{3}'.format(
L850_TARBALL_PREFIX, board, self.basename, revision)
self.revision = revision
def validate(self):
if not self.revision.startswith(
'r') or not self.revision[1:].isdigit():
logging.error('The revision should be in the form of r##')
return False
if len(self.basename) != 4 or not self.basename.isdigit():
logging.error('The OEM carrier directory name is expected to '
'consist of 4 digits')
return False
return True
def prepare_files(self, dir_path, target_path):
logging.info('Copying %s into %s', dir_path, target_path)
os.mkdir(os.path.join(target_path, self.basename))
copy_tree(dir_path, os.path.join(target_path, self.basename))
return True
class NL668MainFw(FwUploader):
"""Uploader class for NL668 main FW."""
def __init__(self, path, upload):
super().__init__(path, upload, None)
self.tarball_dir_name = NL668_TARBALL_PREFIX + self.basename
def validate(self):
if not os.path.isdir(self.path):
logging.error('The NL668 FW should be a directory')
return False
return True
def prepare_files(self, dir_path, target_path):
logging.info('Copying %s into %s', dir_path, target_path)
os.mkdir(os.path.join(target_path, self.basename))
copy_tree(dir_path, os.path.join(target_path, self.basename))
return True
def parse_arguments(argv):
"""Parses command line arguments.
Args:
argv: List of commandline arguments.
Returns:
Namespace object containing parsed arguments.
"""
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('type',
type=PackageType,
choices=list(PackageType),
help='The type of package to create')
parser.add_argument(
'path', help='The path to the FW file or directory to be packaged.')
parser.add_argument(
'--board',
help='The ChromeOS board in which this cust pack will be used.')
parser.add_argument(
'--revision',
help='The next ebuild number for that board. If the current ebuild '
'revision is r12, enter r13.')
parser.add_argument('--upload',
default=False,
action='store_true',
help='upload file to GS bucket.')
return parser.parse_args(argv[1:])
def main(argv):
"""Main function."""
logging.basicConfig(level=logging.DEBUG)
opts = parse_arguments(argv)
if opts.type == PackageType.L850_MAIN_FW:
fw_uploader = L850MainFw(opts.path, opts.upload)
elif opts.type == PackageType.L850_OEM_FW:
fw_uploader = L850OemFw(opts.path, opts.upload)
elif opts.type == PackageType.L850_OEM_DIR_ONLY:
if not opts.revision:
logging.error('The ebuild revision is needed to pack it, since '
'the tarballs need to be unique.')
return os.EX_USAGE
if not opts.board:
logging.error('Please enter the board name.')
return os.EX_USAGE
fw_uploader = L850OemDir(opts.path, opts.upload, opts.revision,
opts.board)
elif opts.type == PackageType.NL668_MAIN_FW:
fw_uploader = NL668MainFw(opts.path, opts.upload)
return fw_uploader.process_fw_and_upload()
if __name__ == '__main__':
sys.exit(main(sys.argv))