7785becd78b80cf0546985606b6e752986cdeefb
[platform/upstream/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 sys
31 import errno
32
33 from argparse import ArgumentParser, SUPPRESS
34
35 from mic import msger, __version__ as VERSION
36 from mic.utils import misc, errors
37 from mic.conf import configmgr
38 from mic.plugin import pluginmgr
39 from mic.helpformat import MICHelpFormatter, subparser
40     
41
42 @subparser
43 def chroot_parser(parser):
44     """chroot into an image
45
46     Examples:
47         mic chroot platform.img
48         mic chroot platform.img ls
49     """
50     parser.add_argument('imagefile', help='Path of image file')
51     parser.add_argument('-s', '--saveto', action = 'store', dest = 'saveto', default = None,
52                         help = "Save the unpacked image to specified dir")
53     parser.add_argument('-c', '--cmd', dest = 'cmd', default = None,
54                         help = "command which will be executed in chroot environment")
55     parser.set_defaults(alias="ch")
56     return parser
57     
58 @subparser
59 def create_parser(parser):
60     """create an image
61     Examples:
62       $ mic -d -v create auto handset_blackbay.ks
63       $ mic -d -v cr loop handset_blackbay.ks --logfile=mic.log
64     """
65
66     parent_parser = ArgumentParser(add_help=False)
67     parent_parser.add_argument('ksfile', help='Path of ksfile');
68     parent_parser.add_argument('--logfile', dest='logfile', default=None,
69                                help='Path of logfile')
70     parent_parser.add_argument('-c', '--config', dest='config', default=None,
71                                help='Specify config file for mic')                
72     parent_parser.add_argument('-k', '--cachedir', action='store',
73                                dest='cachedir', default=None,
74                                help='Cache directory to store the downloaded')
75     parent_parser.add_argument('-o', '--outdir', action='store', dest='outdir', 
76                                default=None, help='Output directory')
77     parent_parser.add_argument('-A', '--arch', dest='arch', default=None,
78                                help='Specify repo architecture')
79     parent_parser.add_argument('--release', dest='release', default=None, metavar='RID',
80                                help='Generate a release of RID with all necessary'
81                                ' files, when @BUILD_ID@ is contained in '
82                                'kickstart file, it will be replaced by RID')
83     parent_parser.add_argument("--record-pkgs", dest="record_pkgs", default=None,
84                                help='Record the info of installed packages, '
85                                'multiple values can be specified which '
86                                'joined by ",", valid values: "name", '
87                                '"content", "license", "vcs"')
88     parent_parser.add_argument('--pkgmgr', dest='pkgmgr', default=None,
89                                help='Specify backend package manager')
90     parent_parser.add_argument('--local-pkgs-path', dest='local_pkgs_path', default=None,
91                                help='Path for local pkgs(rpms) to be installed')
92     parent_parser.add_argument('--runtime', dest='runtime', default=None,
93                                help='Specify runtime mode, avaiable: bootstrap')
94     # --taring-to is alias to --pack-to
95     parent_parser.add_argument('--taring-to', dest='pack_to', default=None,
96                                help=SUPPRESS)
97     parent_parser.add_argument('--pack-to', dest='pack_to', default=None,
98                                help='Pack the images together into the specified'
99                                     ' achive, extension supported: .zip, .tar, '
100                                     '.tar.gz, .tar.bz2, etc. by default, .tar '
101                                     'will be used')
102     parent_parser.add_argument('--copy-kernel', action='store_true', dest='copy_kernel',
103                                help='Copy kernel files from image /boot directory'
104                                     ' to the image output directory.')
105     parent_parser.add_argument('--install-pkgs', action='store', dest='install_pkgs', default=None,
106                                help='Specify what type of packages to be installed,'
107                                     ' valid: source, debuginfo, debugsource')
108     parent_parser.add_argument('--check-pkgs', action='store', dest='check_pkgs', default=[],
109                                help='Check if given packages would be installed, '
110                                     'packages should be separated by comma')
111     parent_parser.add_argument('--tmpfs', action='store_true', dest='enabletmpfs',
112                                help='Setup tmpdir as tmpfs to accelerate, experimental'
113                                     ' feature, use it if you have more than 4G memory')
114     parent_parser.add_argument('--repourl', action='append', dest='repourl', default=[],
115                                help=SUPPRESS)
116     parent_parser.add_argument('-R', '--repo', action='append',
117                                dest='repo', default=[],
118                                help=SUPPRESS)
119     parent_parser.add_argument('--ignore-ksrepo', action='store_true',
120                                dest='ignore_ksrepo', default=False,
121                                help=SUPPRESS)
122     parent_parser.add_argument('--strict-mode', action='store_true',
123                                dest='strict_mode', default=False,
124                                help='Abort creation of image, if there are some errors'
125                                     ' during rpm installation. ')
126     parser.set_defaults(alias="cr")
127
128     subparsers  = parser.add_subparsers(title='Subcommands', dest='subcommand')
129     fs_parser   = subparsers.add_parser('fs', parents=[parent_parser], 
130                                         help='auto detect image type from magic header')
131     fs_parser.add_argument("--include-src", dest = "include_src",action = "store_true",
132                            default = False, help = "Generate a image with source rpms included")
133                   
134     auto_parser = subparsers.add_parser('auto', parents=[parent_parser], help='create fs image')
135     loop_parser = subparsers.add_parser('loop', parents=[parent_parser], help='create loop image')
136
137     loop_parser.add_argument("--compress-disk-image", dest="compress_image",
138                              choices=("gz", "bz2", "lzo"), default=None,
139                              help="Same with --compress-image")
140     # alias to compress-image for compatibility
141     loop_parser.add_argument("--compress-image", dest="compress_image",
142                              choices=("gz", "bz2", "lzo"), default=None,
143                              help="Compress all loop images with 'gz' or 'bz2' or 'lzo',"
144                                   "Note: if you want to use 'lzo', package 'lzop' is needed to"
145                                   "be installed manually.")
146     loop_parser.add_argument("--shrink", action='store_true', default=False,
147                   help="Whether to shrink loop images to minimal size")
148                   
149     qcow_parser = subparsers.add_parser('qcow', parents=[parent_parser], help='create qcow image')
150
151     return parser
152
153 def main(argv):
154     """Script entry point."""
155     
156     def print_version():
157         """log name, verion, hostname"""
158         
159         name = 'mic'
160         msger.raw("%s %s (%s)" % (name,
161                                   VERSION,
162                                   misc.get_hostname_distro_str()))
163                                   
164     def has_parameter(arg, arglist):
165         """
166         Helper function.
167         Check if argument requires parameter by analyzing
168         its action. Parameter is required only for 'store' and 'append' actions
169         """
170         if arg.startswith('-'):
171             for args in arglist:
172                 if arg in (args['short'], args['long']):
173                     if args.get('action') in (None, 'store', 'append'):
174                         return True
175                     return False
176
177     # Create top level parser
178     epilog = "Try 'mic SUBCOMMAND --help' for help on a specific subcommand."
179     description = "mic - the Image Creation tool"
180     parser = ArgumentParser(description=description, epilog=epilog,
181                             formatter_class=MICHelpFormatter)
182
183     # List of global arguments
184     # The main purpose of this structure is to contain arguments
185     # of add_argument. This is used to do aliasing properly
186     # (see code under the comment 'replace aliases with real commands')
187     global_args = [{'short': '-V', 'long': '--version', 'action': 'version',
188                     'version': '%(prog)s ' + VERSION},
189                    {'short': '-d', 'long': '--debug', 'action': 'store_true',
190                     'help': 'debug output'},
191                    {'short': '-v', 'long': '--verbose', 'action': 'store_true',
192                     'help': 'verbose output'},
193                    {'short': '-i', 'long': '--interactive', 'action': 'store_true',
194                     'dest': 'interactive', 'default': 'True', 'help': 'interactive output'}, 
195                    {'short': '', 'long': '--non-interactive', 'action': 'store_false',
196                     'dest': 'interactive', 'default': 'True', 'help': 'non-interactive output'}, 
197                   ]
198
199     for args in global_args:
200         parser_kwargs = {}
201         for key in ('action', 'help', 'version', 'default', 'dest'):
202             if key in args:
203                 parser_kwargs[key] = args[key]
204         
205         if args['short'] is '':
206             parser.add_argument(args['long'], **parser_kwargs)
207         else:
208             parser.add_argument(args['short'], args['long'], **parser_kwargs)
209
210     # hacked by the request of cmdln lovers
211     parser.format_usage = parser.format_help
212
213     # Create parsers for subcommands
214     subparsers = parser.add_subparsers(title='subcommands')
215
216     # collect aliases
217     aliases = {}
218     for name, obj in globals().iteritems():
219         if name.endswith('_parser') and callable(obj):
220             aliases[obj(subparsers).get_default('alias')] = name.split('_')[0]
221
222     # replace aliases with real commands
223     for i, arg in enumerate(argv[1:]):
224         if not arg.startswith('-'):
225             # argv[i] is previous argument to arg
226             if not has_parameter(argv[i], global_args) and arg in aliases:
227                 argv[i+1] = aliases[arg]
228                 break
229     
230     # Parse arguments
231     args = parser.parse_args(argv[1:])
232
233     if args.interactive:
234         msger.enable_interactive()
235     else:
236         msger.disable_interactive()
237
238     if args.verbose:
239         msger.set_loglevel('VERBOSE')
240
241     if args.debug:
242         try:
243             import rpm
244             rpm.setVerbosity(rpm.RPMLOG_NOTICE)
245         except ImportError:
246             pass
247
248         msger.set_loglevel('DEBUG')
249
250     print_version()
251
252     # Import target module and call 'main' from it
253     module = __import__("mic.%s" % args.module, fromlist=[args.module])
254     return module.main(parser, args, argv[1:])
255
256     
257 if __name__ == "__main__":
258     try:
259         sys.exit(main(sys.argv))
260     except KeyboardInterrupt:
261         msger.error('\n^C catched, program aborted.')
262     except IOError as ioerr:
263         # catch 'no space left' exception, etc
264         if ioerr.errno == errno.ENOSPC:
265             msger.error('\nNo space left on device')
266         raise
267     except errors.Usage as usage:
268         msger.error(str(usage))
269     except errors.Abort as  msg:
270         msger.info(str(msg))
271     except errors.CreatorError as err:
272         if msger.get_loglevel() == 'DEBUG':
273             import traceback
274             msger.error(traceback.format_exc())
275         else:
276             msger.error(str(err))