Another method of install tpk.
[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
128     parent_parser.add_argument('-d', '--debug', action='store_true',
129                                help='debug output')
130     parent_parser.add_argument('-v', '--verbose', action='store_true',
131                                 help='verbose output')
132     parent_parser.add_argument('-i', '--interactive', action='store_true',
133                                 dest='interactive', default=True,
134                                help='interactive output')
135     parent_parser.add_argument('--run_script', action='store', dest='run_script',
136                                                    default=None, help='Run script on local PC after image created')
137     parent_parser.add_argument('--tpk_install', action='store', dest='tpk_install',
138                                                                        default=None, help='Copy tpk file to /usr/apps/.preload-tpk')
139
140     parser.set_defaults(alias="cr")
141
142     subparsers  = parser.add_subparsers(title='Subcommands', dest='subcommand')
143     auto_parser = subparsers.add_parser('auto', parents=[parent_parser], help='auto detect image type from magic header')
144
145     fs_parser   = subparsers.add_parser('fs', parents=[parent_parser],
146                                         help='create fs image')
147     fs_parser.add_argument("--include-src", dest = "include_src",action = "store_true",
148                            default = False, help = "Generate a image with source rpms included")
149
150     loop_parser = subparsers.add_parser('loop', parents=[parent_parser], help='create loop image')
151
152     loop_parser.add_argument("--compress-disk-image", dest="compress_image",
153                              choices=("gz", "bz2"), default=None,
154                              help="Same with --compress-image")
155     # alias to compress-image for compatibility
156     loop_parser.add_argument("--compress-image", dest="compress_image",
157                              choices=("gz", "bz2"), default=None,
158                              help="Compress all loop images with 'gz' or 'bz2'")
159     loop_parser.add_argument("--shrink", action='store_true', default=False,
160                   help="Whether to shrink loop images to minimal size")
161                   
162     qcow_parser = subparsers.add_parser('qcow', parents=[parent_parser], help='create qcow image')
163
164     raw_parser = subparsers.add_parser('raw', parents=[parent_parser], help='create raw image')
165
166     raw_parser.add_argument("--compress-disk-image", dest="compress_image",
167                             choices=("gz", "bz2"), default=None,
168                             help="Same with --compress-image")
169     raw_parser.add_argument("--compress-image", dest="compress_image",
170                             choices=("gz", "bz2"), default = None,
171                             help="Compress all raw images before package")
172     raw_parser.add_argument("--generate-bmap", action="store_true", default = None,
173                             help="also generate the block map file")
174     raw_parser.add_argument("--fstab-entry", dest="fstab_entry", choices=("name", "uuid"), default="uuid",
175                             help="Set fstab entry, 'name' means using device names, "
176                                  "'uuid' means using filesystem uuid")
177     return parser
178
179 def main(argv):
180     """Script entry point."""
181     
182     def print_version():
183         """log name, verion, hostname"""
184         
185         name = 'mic'
186         msger.raw("%s %s (%s)" % (name,
187                                   VERSION,
188                                   misc.get_hostname_distro_str()))
189                                   
190     def has_parameter(arg, arglist):
191         """
192         Helper function.
193         Check if argument requires parameter by analyzing
194         its action. Parameter is required only for 'store' and 'append' actions
195         """
196         if arg.startswith('-'):
197             for args in arglist:
198                 if arg in (args['short'], args['long']):
199                     if args.get('action') in (None, 'store', 'append'):
200                         return True
201                     return False
202
203     def sigterm_handler(signal, frame):
204         raise errors.Abort('\nSIGTERM catched, program aborted.')
205
206     # Add SIGTERM handler for exit gracefully
207     signal.signal(signal.SIGTERM, sigterm_handler)
208
209     # Create top level parser
210     epilog = "Try 'mic SUBCOMMAND --help' for help on a specific subcommand."
211     description = "mic - the Image Creation tool"
212     parser = ArgumentParser(description=description, epilog=epilog,
213                             formatter_class=MICHelpFormatter)
214
215     # List of global arguments
216     # The main purpose of this structure is to contain arguments
217     # of add_argument. This is used to do aliasing properly
218     # (see code under the comment 'replace aliases with real commands')
219     global_args = [{'short': '-V', 'long': '--version', 'action': 'version',
220                     'version': '%(prog)s ' + VERSION},
221                    {'short': '-d', 'long': '--debug', 'action': 'store_true',
222                     'help': 'debug output'},
223                    {'short': '-v', 'long': '--verbose', 'action': 'store_true',
224                     'help': 'verbose output'},
225                    {'short': '-i', 'long': '--interactive', 'action': 'store_true',
226                     'dest': 'interactive', 'default': 'True', 'help': 'interactive output'}, 
227                    {'short': '', 'long': '--non-interactive', 'action': 'store_false',
228                     'dest': 'interactive', 'default': 'True', 'help': 'non-interactive output'}, 
229                   ]
230
231     for args in global_args:
232         parser_kwargs = {}
233         for key in ('action', 'help', 'version', 'default', 'dest'):
234             if key in args:
235                 parser_kwargs[key] = args[key]
236         
237         if args['short'] is '':
238             parser.add_argument(args['long'], **parser_kwargs)
239         else:
240             parser.add_argument(args['short'], args['long'], **parser_kwargs)
241
242     # hacked by the request of cmdln lovers
243     parser.format_usage = parser.format_help
244
245     # Create parsers for subcommands
246     subparsers = parser.add_subparsers(title='subcommands')
247
248     # collect aliases
249     aliases = {}
250     for name, obj in globals().iteritems():
251         if name.endswith('_parser') and callable(obj):
252             aliases[obj(subparsers).get_default('alias')] = name.split('_')[0]
253
254     # replace aliases with real commands
255     for i, arg in enumerate(argv[1:]):
256         if not arg.startswith('-'):
257             # argv[i] is previous argument to arg
258             if not has_parameter(argv[i], global_args) and arg in aliases:
259                 argv[i+1] = aliases[arg]
260                 break
261
262     # Parse arguments
263     args = parser.parse_args(argv[1:])
264
265     if args.interactive:
266         msger.enable_interactive()
267     else:
268         msger.disable_interactive()
269
270     if args.verbose:
271         msger.set_loglevel('VERBOSE')
272
273     if args.debug:
274         try:
275             import rpm
276             rpm.setVerbosity(rpm.RPMLOG_DEBUG)
277         except ImportError:
278             pass
279
280         msger.set_loglevel('DEBUG')
281
282     print_version()
283
284     # Import target module and call 'main' from it
285     module = __import__("mic.%s" % args.module, fromlist=[args.module])
286     return module.main(parser, args, argv[1:])
287
288     
289 if __name__ == "__main__":
290     try:
291         sys.exit(main(sys.argv))
292     except KeyboardInterrupt:
293         msger.error('\n^C catched, program aborted.')
294     except IOError as ioerr:
295         # catch 'no space left' exception, etc
296         if ioerr.errno == errno.ENOSPC:
297             msger.error('\nNo space left on device')
298         raise
299     except errors.Usage as usage:
300         msger.error(str(usage))
301     except errors.Abort as  msg:
302         msger.info(str(msg))
303     except errors.CreatorError as err:
304         if msger.get_loglevel() == 'DEBUG':
305             import traceback
306             msger.error(traceback.format_exc())
307         else:
308             msger.error(str(err))