summaryrefslogtreecommitdiff
path: root/tools/mic
blob: 70000a723ebdb9bb379184daa9bf3f134efd1646 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
#!/usr/bin/env python

#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('-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')

    parser.set_defaults(alias="cr")

    subparsers  = parser.add_subparsers(title='Subcommands', dest='subcommand')
    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 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)

    # 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'] is '':
            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')

    # collect aliases
    aliases = {}
    for name, obj in globals().iteritems():
        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:])

    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_DEBUG)
        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))