Merge "Another method of install tpk." into devel
[tools/mic.git] / tools / mic
1 #!/usr/bin/env python
2
3 #Copyright (c) 2011 Intel, Inc.
4 #
5 #This program is free software; you can redistribute it and/or modify it
6 #under the terms of the GNU General Public License as published by the Free
7 #Software Foundation; version 2 of the License
8 #
9 #This program is distributed in the hope that it will be useful, but
10 #WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 # for more details.
13 #
14 # You should have received a copy of the GNU General Public License along
15 # with this program; if not, write to the Free Software Foundation, Inc., 59
16 # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #
18 # pylint: disable-msg=E0611, E1101, R0201
19 # E0611: no name in module, some attributes are set during running, so ignore it
20 # E1101: %s %r has no %r member, some attributes are set during running,
21 #        so ignore it
22 # R0201: Method could be a function
23
24 """
25  This mudule is entry for mic.
26  It defines a class named MicCmd inheriting Cmdln, and supplies interfaces like
27  'create, chroot, convert' and also some parameters for command 'mic'.
28 """
29 import os
30 import signal
31 import sys
32 import errno
33
34 from argparse import ArgumentParser, SUPPRESS
35
36 from mic import msger, __version__ as VERSION
37 from mic.utils import misc, errors
38 from mic.conf import configmgr
39 from mic.plugin import pluginmgr
40 from mic.helpformat import MICHelpFormatter, subparser
41     
42
43 @subparser
44 def chroot_parser(parser):
45     """chroot into an image
46
47     Examples:
48         mic chroot platform.img
49         mic chroot platform.img ls
50     """
51     parser.add_argument('imagefile', help='Path of image file')
52     parser.add_argument('-s', '--saveto', action = 'store', dest = 'saveto', default = None,
53                         help = "Save the unpacked image to specified dir")
54     parser.add_argument('-c', '--cmd', dest = 'cmd', default = None,
55                         help = "command which will be executed in chroot environment")
56     parser.set_defaults(alias="ch")
57     return parser
58
59 @subparser
60 def create_parser(parser):
61     """create an image
62     Examples:
63       $ mic -d -v create auto handset_blackbay.ks
64       $ mic -d -v cr loop handset_blackbay.ks --logfile=mic.log
65     """
66
67     parent_parser = ArgumentParser(add_help=False)
68     parent_parser.add_argument('ksfile', help='Path of ksfile')
69     parent_parser.add_argument('--logfile', dest='logfile', default=None,
70                                help='Path of logfile')
71     parent_parser.add_argument('-c', '--config', dest='config', default=None,
72                                help='Specify config file for mic')
73     parent_parser.add_argument('-k', '--cachedir', action='store',
74                                dest='cachedir', default=None,
75                                help='Cache directory to store the downloaded')
76     parent_parser.add_argument('-o', '--outdir', action='store', dest='outdir',
77                                default=None, help='Output directory')
78     parent_parser.add_argument('-A', '--arch', dest='arch', default=None,
79                                help='Specify repo architecture')
80     parent_parser.add_argument('--release', dest='release', default=None, metavar='RID',
81                                help='Generate a release of RID with all necessary'
82                                ' files, when @BUILD_ID@ is contained in '
83                                'kickstart file, it will be replaced by RID')
84     parent_parser.add_argument("--record-pkgs", dest="record_pkgs", default=None,
85                                help='Record the info of installed packages, '
86                                'multiple values can be specified which '
87                                'joined by ",", valid values: "name", '
88                                '"content", "license", "vcs"')
89     parent_parser.add_argument('--pkgmgr', dest='pkgmgr', default=None,
90                                help='Specify backend package manager')
91     parent_parser.add_argument('--local-pkgs-path', dest='local_pkgs_path', default=None,
92                                help='Path for local pkgs(rpms) to be installed')
93     parent_parser.add_argument('--runtime', dest='runtime', default=None,
94                                help='Specify runtime mode, avaiable: bootstrap')
95     # --taring-to is alias to --pack-to
96     parent_parser.add_argument('--taring-to', dest='pack_to', default=None,
97                                help=SUPPRESS)
98     parent_parser.add_argument('--pack-to', dest='pack_to', default=None,
99                                help='Pack the images together into the specified'
100                                     ' achive, extension supported: .zip, .tar, '
101                                     '.tar.gz, .tar.bz2, etc. by default, .tar '
102                                     'will be used')
103     parent_parser.add_argument('--copy-kernel', action='store_true', dest='copy_kernel',
104                                help='Copy kernel files from image /boot directory'
105                                     ' to the image output directory.')
106     parent_parser.add_argument('--install-pkgs', action='store', dest='install_pkgs', default=None,
107                                help='Specify what type of packages to be installed,'
108                                     ' valid: source, debuginfo, debugsource')
109     parent_parser.add_argument('--check-pkgs', action='store', dest='check_pkgs', default=[],
110                                help='Check if given packages would be installed, '
111                                     'packages should be separated by comma')
112     parent_parser.add_argument('--tmpfs', action='store_true', dest='enabletmpfs',
113                                help='Setup tmpdir as tmpfs to accelerate, experimental'
114                                     ' feature, use it if you have more than 4G memory')
115     parent_parser.add_argument('--repourl', action='append', dest='repourl', default=[],
116                                help=SUPPRESS)
117     parent_parser.add_argument('-R', '--repo', action='append',
118                                dest='repo', default=[],
119                                help=SUPPRESS)
120     parent_parser.add_argument('--ignore-ksrepo', action='store_true',
121                                dest='ignore_ksrepo', default=False,
122                                help=SUPPRESS)
123     parent_parser.add_argument('--strict-mode', action='store_true',
124                                dest='strict_mode', default=False,
125                                help='Abort creation of image, if there are some errors'
126                                     ' during rpm installation. ')
127     parent_parser.add_argument('--use-mic-in-bootstrap', action='store_true',
128                                dest='use_mic_in_bootstrap', default=False,
129                                help='This option works in bootstrap runtime mode,'
130                                     ' Use mic in bootstrap to create image.'
131                                     ' By default, copy host mic to bootstrap and use it.')
132
133     parent_parser.add_argument('-d', '--debug', action='store_true',
134                                help='debug output')
135     parent_parser.add_argument('-v', '--verbose', action='store_true',
136                                 help='verbose output')
137     parent_parser.add_argument('-i', '--interactive', action='store_true',
138                                 dest='interactive', default=True,
139                                help='interactive output')
140     parent_parser.add_argument('--run_script', action='store', dest='run_script',
141                                                    default=None, help='Run script on local PC after image created')
142     parent_parser.add_argument('--tpk_install', action='store', dest='tpk_install',
143                                                                        default=None, help='Copy tpk file to /usr/apps/.preload-tpk')
144     parent_parser.add_argument('--rpm-debug', action='store_true', dest='rpm_debug', help='Set debug mode for rpm install')
145
146     parser.set_defaults(alias="cr")
147
148     subparsers  = parser.add_subparsers(title='Subcommands', dest='subcommand')
149     auto_parser = subparsers.add_parser('auto', parents=[parent_parser], help='auto detect image type from magic header')
150
151     fs_parser   = subparsers.add_parser('fs', parents=[parent_parser],
152                                         help='create fs image')
153     fs_parser.add_argument("--include-src", dest = "include_src",action = "store_true",
154                            default = False, help = "Generate a image with source rpms included")
155
156     loop_parser = subparsers.add_parser('loop', parents=[parent_parser], help='create loop image')
157
158     loop_parser.add_argument("--compress-disk-image", dest="compress_image",
159                              choices=("gz", "bz2"), default=None,
160                              help="Same with --compress-image")
161     # alias to compress-image for compatibility
162     loop_parser.add_argument("--compress-image", dest="compress_image",
163                              choices=("gz", "bz2"), default=None,
164                              help="Compress all loop images with 'gz' or 'bz2'")
165     loop_parser.add_argument("--shrink", action='store_true', default=False,
166                   help="Whether to shrink loop images to minimal size")
167                   
168     qcow_parser = subparsers.add_parser('qcow', parents=[parent_parser], help='create qcow image')
169
170     raw_parser = subparsers.add_parser('raw', parents=[parent_parser], help='create raw image')
171
172     raw_parser.add_argument("--compress-disk-image", dest="compress_image",
173                             choices=("gz", "bz2"), default=None,
174                             help="Same with --compress-image")
175     raw_parser.add_argument("--compress-image", dest="compress_image",
176                             choices=("gz", "bz2"), default = None,
177                             help="Compress all raw images before package")
178     raw_parser.add_argument("--generate-bmap", action="store_true", default = None,
179                             help="also generate the block map file")
180     raw_parser.add_argument("--fstab-entry", dest="fstab_entry", choices=("name", "uuid"), default="uuid",
181                             help="Set fstab entry, 'name' means using device names, "
182                                  "'uuid' means using filesystem uuid")
183     return parser
184
185 def main(argv):
186     """Script entry point."""
187     
188     def print_version():
189         """log name, verion, hostname"""
190         
191         name = 'mic'
192         msger.raw("%s %s (%s)" % (name,
193                                   VERSION,
194                                   misc.get_hostname_distro_str()))
195                                   
196     def has_parameter(arg, arglist):
197         """
198         Helper function.
199         Check if argument requires parameter by analyzing
200         its action. Parameter is required only for 'store' and 'append' actions
201         """
202         if arg.startswith('-'):
203             for args in arglist:
204                 if arg in (args['short'], args['long']):
205                     if args.get('action') in (None, 'store', 'append'):
206                         return True
207                     return False
208
209     def sigterm_handler(signal, frame):
210         raise errors.Abort('\nSIGTERM catched, program aborted.')
211
212     # Add SIGTERM handler for exit gracefully
213     signal.signal(signal.SIGTERM, sigterm_handler)
214
215     # Create top level parser
216     epilog = "Try 'mic SUBCOMMAND --help' for help on a specific subcommand."
217     description = "mic - the Image Creation tool"
218     parser = ArgumentParser(description=description, epilog=epilog,
219                             formatter_class=MICHelpFormatter)
220
221     # List of global arguments
222     # The main purpose of this structure is to contain arguments
223     # of add_argument. This is used to do aliasing properly
224     # (see code under the comment 'replace aliases with real commands')
225     global_args = [{'short': '-V', 'long': '--version', 'action': 'version',
226                     'version': '%(prog)s ' + VERSION},
227                    {'short': '-d', 'long': '--debug', 'action': 'store_true',
228                     'help': 'debug output'},
229                    {'short': '-v', 'long': '--verbose', 'action': 'store_true',
230                     'help': 'verbose output'},
231                    {'short': '-i', 'long': '--interactive', 'action': 'store_true',
232                     'dest': 'interactive', 'default': 'True', 'help': 'interactive output'}, 
233                    {'short': '', 'long': '--non-interactive', 'action': 'store_false',
234                     'dest': 'interactive', 'default': 'True', 'help': 'non-interactive output'}, 
235                   ]
236
237     for args in global_args:
238         parser_kwargs = {}
239         for key in ('action', 'help', 'version', 'default', 'dest'):
240             if key in args:
241                 parser_kwargs[key] = args[key]
242         
243         if args['short'] is '':
244             parser.add_argument(args['long'], **parser_kwargs)
245         else:
246             parser.add_argument(args['short'], args['long'], **parser_kwargs)
247
248     # hacked by the request of cmdln lovers
249     parser.format_usage = parser.format_help
250
251     # Create parsers for subcommands
252     subparsers = parser.add_subparsers(title='subcommands')
253
254     # collect aliases
255     aliases = {}
256     for name, obj in globals().iteritems():
257         if name.endswith('_parser') and callable(obj):
258             aliases[obj(subparsers).get_default('alias')] = name.split('_')[0]
259
260     # replace aliases with real commands
261     for i, arg in enumerate(argv[1:]):
262         if not arg.startswith('-'):
263             # argv[i] is previous argument to arg
264             if not has_parameter(argv[i], global_args) and arg in aliases:
265                 argv[i+1] = aliases[arg]
266                 break
267
268     # Parse arguments
269     args = parser.parse_args(argv[1:])
270
271     if args.interactive:
272         msger.enable_interactive()
273     else:
274         msger.disable_interactive()
275
276     if args.verbose:
277         msger.set_loglevel('VERBOSE')
278
279     if args.debug:
280         try:
281             import rpm
282             rpm.setVerbosity(rpm.RPMLOG_DEBUG)
283         except ImportError:
284             pass
285
286         msger.set_loglevel('DEBUG')
287
288     print_version()
289
290     # Import target module and call 'main' from it
291     module = __import__("mic.%s" % args.module, fromlist=[args.module])
292     return module.main(parser, args, argv[1:])
293
294     
295 if __name__ == "__main__":
296     try:
297         sys.exit(main(sys.argv))
298     except KeyboardInterrupt:
299         msger.error('\n^C catched, program aborted.')
300     except IOError as ioerr:
301         # catch 'no space left' exception, etc
302         if ioerr.errno == errno.ENOSPC:
303             msger.error('\nNo space left on device')
304         raise
305     except errors.Usage as usage:
306         msger.error(str(usage))
307     except errors.Abort as  msg:
308         msger.info(str(msg))
309     except errors.CreatorError as err:
310         if msger.get_loglevel() == 'DEBUG':
311             import traceback
312             msger.error(traceback.format_exc())
313         else:
314             msger.error(str(err))