drop VERSION file, use __version__ directly in mic module
[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 import os, sys, errno
19 from mic import msger, creator, __version__ as VERSION
20 from mic.utils import cmdln, misc, errors
21 from mic.conf import configmgr
22 from mic.plugin import pluginmgr
23
24
25 def optparser_setup(func):
26     """Setup optparser for a function"""
27     if not hasattr(func, "optparser"):
28         func.optparser = cmdln.SubCmdOptionParser()
29         func.optparser.disable_interspersed_args()
30     return func
31
32
33 class MicCmd(cmdln.Cmdln):
34     """
35     Usage: mic SUBCOMMAND [OPTS] [ARGS...]
36
37     mic Means the Image Creation tool
38     Try 'mic help SUBCOMMAND' for help on a specific subcommand.
39
40     ${command_list}
41     global ${option_list}
42     ${help_list}
43     """
44
45     name = 'mic'
46     version = VERSION
47
48     def print_version(self):
49         msger.raw("%s %s (%s)" % (self.name,
50                                   self.version,
51                                   misc.get_distro_str()))
52
53     def get_optparser(self):
54         optparser = cmdln.CmdlnOptionParser(self, version=self.version)
55         # hook optparse print_version here
56         optparser.print_version = self.print_version
57         optparser.add_option('-d', '--debug', action='store_true',
58                              dest='debug',
59                              help='print debug message')
60         optparser.add_option('-v', '--verbose', action='store_true',
61                              dest='verbose',
62                              help='verbose information')
63         return optparser
64
65     def postoptparse(self):
66         if self.options.verbose:
67             msger.set_loglevel('verbose')
68
69         if self.options.debug:
70             try:
71                 import rpm
72                 rpm.setVerbosity(rpm.RPMLOG_NOTICE)
73             except ImportError:
74                 pass
75
76             msger.set_loglevel('debug')
77
78         self.print_version()
79
80     def help_create(self):
81         cr = creator.Creator()
82         cr.optparser = cr.get_optparser()
83         doc = cr.__doc__
84         doc = cr._help_reindent(doc)
85         doc = cr._help_preprocess(doc, None)
86         doc = doc.replace(cr.name, "${cmd_name}", 1)
87         doc = doc.rstrip() + '\n'
88         return doc
89
90     @cmdln.alias("cr")
91     def do_create(self, argv):
92         cr = creator.Creator()
93         cr.main(argv[1:])
94
95     def _root_confirm(self):
96         if os.geteuid() != 0:
97             msger.error('Root permission is required to continue, abort')
98
99     @cmdln.alias("cv")
100     @cmdln.option("-S", "--shell",
101                   action="store_true", dest="shell", default=False,
102                   help="Launch shell before packaging the converted image")
103     def do_convert(self, subcmd, opts, *args):
104         """${cmd_name}: convert image format
105
106         Usage:
107             mic convert <imagefile> <destformat>
108
109         ${cmd_option_list}
110         """
111
112         if not args:
113             # print help
114             handler = self._get_cmd_handler('convert')
115             if hasattr(handler, "optparser"):
116                 handler.optparser.print_help()
117             return 1
118
119         if len(args) == 1:
120             raise errors.Usage("It need 2 arguments (1 given)")
121         elif len(args) == 2:
122             (srcimg, destformat) = args
123         else:
124             raise errors.Usage("Extra argument given")
125
126         if not os.path.exists(srcimg):
127             raise errors.CreatorError("Cannot find the image: %s" % srcimg)
128
129         self._root_confirm()
130
131         configmgr.convert['shell'] = opts.shell
132
133         srcformat = misc.get_image_type(srcimg)
134         if srcformat == "ext3fsimg":
135             srcformat = "loop"
136
137         srcimager = None
138         destimager = None
139         for iname, icls in pluginmgr.get_plugins('imager').iteritems():
140             if iname == srcformat and hasattr(icls, "do_unpack"):
141                 srcimager = icls
142             if iname == destformat and hasattr(icls, "do_pack"):
143                 destimager = icls
144
145         if (srcimager and destimager) is None:
146             raise errors.CreatorError("Can't convert from %s to %s" \
147                                       % (srcformat, destformat))
148
149         else:
150             maptab = {
151                         "livecd": "iso",
152                         "liveusb": "usbimg",
153                         "loop": "img",
154                      }
155
156             if destformat in maptab:
157                 imgname = os.path.splitext(os.path.basename(srcimg))[0]
158                 dstname = "{0}.{1}".format(imgname, maptab[destformat])
159
160                 if os.path.exists(dstname):
161                     if msger.ask("Converted image %s seems existed, "
162                                  "remove and continue?" % dstname):
163                         os.unlink(dstname)
164                     else:
165                         raise errors.Abort("Canceled")
166
167             base_on = srcimager.do_unpack(srcimg)
168             destimager.do_pack(base_on)
169
170     @cmdln.alias("ch")
171     @cmdln.option('-s', '--saveto',
172                   action='store', dest='saveto', default=None,
173                   help="Save the unpacked image to specified dir")
174     @optparser_setup
175     def do_chroot(self, subcmd, opts, *args):
176         """${cmd_name}: chroot into an image
177
178         Usage:
179             mic chroot [options] <imagefile> [command [arg]...]
180
181         ${cmd_option_list}
182         """
183
184         if not args:
185             # print help
186             handler = self._get_cmd_handler('chroot')
187             if hasattr(handler, "optparser"):
188                 handler.optparser.print_help()
189             return 1
190
191         targetimage = args[0]
192
193         if not os.path.exists(targetimage):
194             raise errors.CreatorError("Cannot find the image: %s"
195                                       % targetimage)
196
197         self._root_confirm()
198
199         configmgr.chroot['saveto'] = opts.saveto
200
201         imagetype = misc.get_image_type(targetimage)
202         if imagetype in ("ext3fsimg", "ext4fsimg", "btrfsimg"):
203             imagetype = "loop"
204
205         chrootclass = None
206         for pname, pcls in pluginmgr.get_plugins('imager').iteritems():
207             if pname == imagetype and hasattr(pcls, "do_chroot"):
208                 chrootclass = pcls
209                 break
210
211         if not chrootclass:
212             raise errors.CreatorError("Cannot support image type: %s" \
213                                       % imagetype)
214
215         chrootclass.do_chroot(targetimage, args[1:])
216
217 if __name__ == "__main__":
218     try:
219         mic = MicCmd()
220         sys.exit(mic.main())
221
222     except KeyboardInterrupt:
223         msger.error('\n^C catched, program aborted.')
224
225     # catch 'no space left' exception, etc
226     except IOError, e:
227         if e.errno == errno.ENOSPC:
228             msger.error('\nNo space left on device')
229         raise
230
231     except errors.Usage, usage:
232         msger.error(str(usage))
233
234     except errors.Abort, msg:
235         msger.info(str(msg))
236
237     except errors.CreatorError, err:
238         if msger.get_loglevel() == 'debug':
239             import traceback
240             msger.error(traceback.format_exc())
241         else:
242             msger.error('\n'+str(err))