#!/usr/bin/python3 -tt #Copyright (c) 2011 Intel, Inc. # #This program is free software; you can redistribute it and/or modify it #under the terms of the GNU General Public License as published by the Free #Software Foundation; version 2 of the License # #This program is distributed in the hope that it will be useful, but #WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., 59 # Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # pylint: disable-msg=E0611, E1101, R0201 # E0611: no name in module, some attributes are set during running, so ignore it # E1101: %s %r has no %r member, some attributes are set during running, # so ignore it # R0201: Method could be a function """ This mudule is entry for mic. It defines a class named MicCmd inheriting Cmdln, and supplies interfaces like 'create, chroot, convert' and also some parameters for command 'mic'. """ import os import signal import sys import errno from argparse import ArgumentParser, SUPPRESS from mic import msger, __version__ as VERSION from mic.utils import misc, errors from mic.conf import configmgr from mic.plugin import pluginmgr from mic.helpformat import MICHelpFormatter, subparser @subparser def chroot_parser(parser): """chroot into an image Examples: mic chroot platform.img mic chroot platform.img ls """ parser.add_argument('imagefile', help='Path of image file') parser.add_argument('-s', '--saveto', action = 'store', dest = 'saveto', default = None, help = "Save the unpacked image to specified dir") parser.add_argument('-c', '--cmd', dest = 'cmd', default = None, help = "command which will be executed in chroot environment") parser.set_defaults(alias="ch") return parser @subparser def create_parser(parser): """create an image Examples: $ mic -d -v create auto handset_blackbay.ks $ mic -d -v cr loop handset_blackbay.ks --logfile=mic.log """ parent_parser = ArgumentParser(add_help=False) parent_parser.add_argument('ksfile', help='Path of ksfile') parent_parser.add_argument('--logfile', dest='logfile', default=None, help='Path of logfile') parent_parser.add_argument('-c', '--config', dest='config', default=None, help='Specify config file for mic') parent_parser.add_argument('-k', '--cachedir', action='store', dest='cachedir', default=None, help='Cache directory to store the downloaded') parent_parser.add_argument('-o', '--outdir', action='store', dest='outdir', default=None, help='Output directory') parent_parser.add_argument('-A', '--arch', dest='arch', default=None, help='Specify repo architecture') parent_parser.add_argument('--release', dest='release', default=None, metavar='RID', help='Generate a release of RID with all necessary' ' files, when @BUILD_ID@ is contained in ' 'kickstart file, it will be replaced by RID') parent_parser.add_argument("--record-pkgs", dest="record_pkgs", default=None, help='Record the info of installed packages, ' 'multiple values can be specified which ' 'joined by ",", valid values: "name", ' '"content", "license", "vcs"') parent_parser.add_argument('--pkgmgr', dest='pkgmgr', default=None, help='Specify backend package manager') parent_parser.add_argument('--local-pkgs-path', dest='local_pkgs_path', default=None, help='Path for local pkgs(rpms) to be installed') parent_parser.add_argument('--runtime', dest='runtime', default=None, help='Specify runtime mode, avaiable: bootstrap') # --taring-to is alias to --pack-to parent_parser.add_argument('--taring-to', dest='pack_to', default=None, help=SUPPRESS) parent_parser.add_argument('--pack-to', dest='pack_to', default=None, help='Pack the images together into the specified' ' achive, extension supported: .zip, .tar, ' '.tar.gz, .tar.bz2, etc. by default, .tar ' 'will be used') parent_parser.add_argument('--copy-kernel', action='store_true', dest='copy_kernel', help='Copy kernel files from image /boot directory' ' to the image output directory.') parent_parser.add_argument('--install-pkgs', action='store', dest='install_pkgs', default=None, help='Specify what type of packages to be installed,' ' valid: source, debuginfo, debugsource') parent_parser.add_argument('--check-pkgs', action='store', dest='check_pkgs', default=[], help='Check if given packages would be installed, ' 'packages should be separated by comma') parent_parser.add_argument('--tmpfs', action='store_true', dest='enabletmpfs', help='Setup tmpdir as tmpfs to accelerate, experimental' ' feature, use it if you have more than 4G memory') parent_parser.add_argument('--repourl', action='append', dest='repourl', default=[], help=SUPPRESS) parent_parser.add_argument('-R', '--repo', action='append', dest='repo', default=[], help=SUPPRESS) parent_parser.add_argument('--ignore-ksrepo', action='store_true', dest='ignore_ksrepo', default=False, help=SUPPRESS) parent_parser.add_argument('--strict-mode', action='store_true', dest='strict_mode', default=False, help='Abort creation of image, if there are some errors' ' during rpm installation. ') parent_parser.add_argument('--use-mic-in-bootstrap', action='store_true', dest='use_mic_in_bootstrap', default=False, help='This option works in bootstrap runtime mode,' ' Use mic in bootstrap to create image.' ' By default, copy host mic to bootstrap and use it.') parent_parser.add_argument('-d', '--debug', action='store_true', help='debug output') parent_parser.add_argument('-v', '--verbose', action='store_true', help='verbose output') parent_parser.add_argument('-i', '--interactive', action='store_true', dest='interactive', default=True, help='interactive output') parent_parser.add_argument('--run_script', action='store', dest='run_script', default=None, help='Run script on local PC after image created') parent_parser.add_argument('--tpk_install', action='store', dest='tpk_install', default=None, help='Copy tpk file to /usr/apps/.preload-tpk') parent_parser.add_argument('--rpm-debug', action='store_true', dest='rpm_debug', help='Set debug mode for rpm install') parent_parser.add_argument('--skip-set-hosts', action='store_true', dest='skip_set_hosts', default=False, help='choose to skip set hosts by mic') parent_parser.add_argument('--postscripts-maxruntime', dest='postscripts_maxruntime', default=120, help='max run time for post scripts') parent_parser.add_argument('--block-recommends', action='store_true', dest='block_recommends', default=False, help='Do not install recommended packages') parser.set_defaults(alias="cr") subparsers = parser.add_subparsers(title='Subcommands', dest='subcommand') subparsers.required = True auto_parser = subparsers.add_parser('auto', parents=[parent_parser], help='auto detect image type from magic header') fs_parser = subparsers.add_parser('fs', parents=[parent_parser], help='create fs image') fs_parser.add_argument("--include-src", dest = "include_src",action = "store_true", default = False, help = "Generate a image with source rpms included") loop_parser = subparsers.add_parser('loop', parents=[parent_parser], help='create loop image') loop_parser.add_argument("--compress-disk-image", dest="compress_image", choices=("gz", "bz2"), default=None, help="Same with --compress-image") # alias to compress-image for compatibility loop_parser.add_argument("--compress-image", dest="compress_image", choices=("gz", "bz2"), default=None, help="Compress all loop images with 'gz' or 'bz2'") loop_parser.add_argument("--shrink", action='store_true', default=False, help="Whether to shrink loop images to minimal size") qcow_parser = subparsers.add_parser('qcow', parents=[parent_parser], help='create qcow image') raw_parser = subparsers.add_parser('raw', parents=[parent_parser], help='create raw image') raw_parser.add_argument("--compress-disk-image", dest="compress_image", choices=("gz", "bz2"), default=None, help="Same with --compress-image") raw_parser.add_argument("--compress-image", dest="compress_image", choices=("gz", "bz2"), default = None, help="Compress all raw images before package") raw_parser.add_argument("--generate-bmap", action="store_true", default = None, help="also generate the block map file") raw_parser.add_argument("--fstab-entry", dest="fstab_entry", choices=("name", "uuid"), default="uuid", help="Set fstab entry, 'name' means using device names, " "'uuid' means using filesystem uuid") return parser def main(argv): """Script entry point.""" def print_version(): """log name, verion, hostname""" name = 'mic' msger.raw("%s %s (%s)" % (name, VERSION, misc.get_hostname_distro_str())) def print_notification(): """notification since v2.0.1""" print("=================================================================================================================================") notification_msg = "MIC Notification since v2.0.1:\n" notification_msg += "mic version has been upgraded from python2 to python3. Due to compatibility problem between python2 and python3.\n" notification_msg += "OS of Image creation server for projects using python2 : have to be equal or lower than ubuntu 20.04 and openuse 42.3.\n" notification_msg += "OS of Image creation server for projects using python3 : have to be equal or higher than ubuntu 22.04 and openuse 15.2." print("%s" % notification_msg) print("=================================================================================================================================\n") def has_parameter(arg, arglist): """ Helper function. Check if argument requires parameter by analyzing its action. Parameter is required only for 'store' and 'append' actions """ if arg.startswith('-'): for args in arglist: if arg in (args['short'], args['long']): if args.get('action') in (None, 'store', 'append'): return True return False def sigterm_handler(signal, frame): raise errors.Abort('\nSIGTERM catched, program aborted.') # Add SIGTERM handler for exit gracefully signal.signal(signal.SIGTERM, sigterm_handler) #print notification for v2.0.1 print_notification() # Create top level parser epilog = "Try 'mic SUBCOMMAND --help' for help on a specific subcommand." description = "mic - the Image Creation tool" parser = ArgumentParser(description=description, epilog=epilog, formatter_class=MICHelpFormatter) # List of global arguments # The main purpose of this structure is to contain arguments # of add_argument. This is used to do aliasing properly # (see code under the comment 'replace aliases with real commands') global_args = [{'short': '-V', 'long': '--version', 'action': 'version', 'version': '%(prog)s ' + VERSION}, {'short': '-d', 'long': '--debug', 'action': 'store_true', 'help': 'debug output'}, {'short': '-v', 'long': '--verbose', 'action': 'store_true', 'help': 'verbose output'}, {'short': '-i', 'long': '--interactive', 'action': 'store_true', 'dest': 'interactive', 'default': 'True', 'help': 'interactive output'}, {'short': '', 'long': '--non-interactive', 'action': 'store_false', 'dest': 'interactive', 'default': 'True', 'help': 'non-interactive output'}, ] for args in global_args: parser_kwargs = {} for key in ('action', 'help', 'version', 'default', 'dest'): if key in args: parser_kwargs[key] = args[key] if args['short'] == '': parser.add_argument(args['long'], **parser_kwargs) else: parser.add_argument(args['short'], args['long'], **parser_kwargs) # hacked by the request of cmdln lovers parser.format_usage = parser.format_help # Create parsers for subcommands subparsers = parser.add_subparsers(title='subcommands') subparsers.required = True # collect aliases aliases = {} for name, obj in globals().items(): if name.endswith('_parser') and callable(obj): aliases[obj(subparsers).get_default('alias')] = name.split('_')[0] # replace aliases with real commands for i, arg in enumerate(argv[1:]): if not arg.startswith('-'): # argv[i] is previous argument to arg if not has_parameter(argv[i], global_args) and arg in aliases: argv[i+1] = aliases[arg] break # Parse arguments args = parser.parse_args(argv[1:]) #In python3.x version, if global opiton '-d' , '-v', '-i' is used, it will be not passed to mic create command. #beside, there is same arguments in mic create command. for debug_option in ('-d', '--debug'): if debug_option in argv[1:]: args.debug = True for verbose_option in ('-v', '--verbose'): if verbose_option in argv[1:]: args.verbose = True #for python3.x, args.interactive will always be True even using --non-interactive, that's because it is rewirte by option '-i' #for '-i', use it, action is True, without it, default value is True. if '--non-interactive' in argv[1:]: args.interactive = False if args.interactive: msger.enable_interactive() else: msger.disable_interactive() if args.verbose: msger.set_loglevel('VERBOSE') if args.debug: try: import rpm rpm.setVerbosity(rpm.RPMLOG_NOTICE) except ImportError: pass msger.set_loglevel('DEBUG') print_version() # Import target module and call 'main' from it module = __import__("mic.%s" % args.module, fromlist=[args.module]) return module.main(parser, args, argv[1:]) if __name__ == "__main__": try: sys.exit(main(sys.argv)) except KeyboardInterrupt: msger.error('\n^C catched, program aborted.') except IOError as ioerr: # catch 'no space left' exception, etc if ioerr.errno == errno.ENOSPC: msger.error('\nNo space left on device') raise except errors.Usage as usage: msger.error(str(usage)) except errors.Abort as msg: msger.info(str(msg)) except errors.CreatorError as err: if msger.get_loglevel() == 'DEBUG': import traceback msger.error(traceback.format_exc()) else: msger.error(str(err))