Statistics
| Branch: | Tag: | Revision:

root / image_creator / main.py @ 56884b64

History | View | Annotate | Download (12.1 kB)

1 ae48a082 Nikos Skalkotos
#!/usr/bin/env python
2 ae48a082 Nikos Skalkotos
3 574f2712 Nikos Skalkotos
# Copyright 2012 GRNET S.A. All rights reserved.
4 d57775d4 Nikos Skalkotos
#
5 d57775d4 Nikos Skalkotos
# Redistribution and use in source and binary forms, with or
6 d57775d4 Nikos Skalkotos
# without modification, are permitted provided that the following
7 d57775d4 Nikos Skalkotos
# conditions are met:
8 d57775d4 Nikos Skalkotos
#
9 d57775d4 Nikos Skalkotos
#   1. Redistributions of source code must retain the above
10 d57775d4 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
11 d57775d4 Nikos Skalkotos
#      disclaimer.
12 d57775d4 Nikos Skalkotos
#
13 d57775d4 Nikos Skalkotos
#   2. Redistributions in binary form must reproduce the above
14 d57775d4 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
15 d57775d4 Nikos Skalkotos
#      disclaimer in the documentation and/or other materials
16 d57775d4 Nikos Skalkotos
#      provided with the distribution.
17 d57775d4 Nikos Skalkotos
#
18 d57775d4 Nikos Skalkotos
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 d57775d4 Nikos Skalkotos
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 d57775d4 Nikos Skalkotos
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 d57775d4 Nikos Skalkotos
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 d57775d4 Nikos Skalkotos
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 d57775d4 Nikos Skalkotos
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 d57775d4 Nikos Skalkotos
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 d57775d4 Nikos Skalkotos
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 d57775d4 Nikos Skalkotos
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 d57775d4 Nikos Skalkotos
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 d57775d4 Nikos Skalkotos
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 d57775d4 Nikos Skalkotos
# POSSIBILITY OF SUCH DAMAGE.
30 d57775d4 Nikos Skalkotos
#
31 d57775d4 Nikos Skalkotos
# The views and conclusions contained in the software and
32 d57775d4 Nikos Skalkotos
# documentation are those of the authors and should not be
33 d57775d4 Nikos Skalkotos
# interpreted as representing official policies, either expressed
34 d57775d4 Nikos Skalkotos
# or implied, of GRNET S.A.
35 d57775d4 Nikos Skalkotos
36 c408053f Nikos Skalkotos
from image_creator import __version__ as version
37 997ac76a Nikos Skalkotos
from image_creator import util
38 d57775d4 Nikos Skalkotos
from image_creator.disk import Disk
39 3582e34d Nikos Skalkotos
from image_creator.util import FatalError, MD5
40 4e58b51b Nikos Skalkotos
from image_creator.output.cli import SilentOutput, SimpleOutput, \
41 f99fe99d Nikos Skalkotos
    OutputWthProgress
42 bb4db5a8 Nikos Skalkotos
from image_creator.os_type import os_cls
43 3b7d3fc7 Nikos Skalkotos
from image_creator.kamaki_wrapper import Kamaki, ClientError
44 d57775d4 Nikos Skalkotos
import sys
45 d57775d4 Nikos Skalkotos
import os
46 c408053f Nikos Skalkotos
import optparse
47 b1395967 Nikos Skalkotos
import StringIO
48 9c354f13 Nikos Skalkotos
import signal
49 3ccb2618 Nikos Skalkotos
50 c408053f Nikos Skalkotos
51 0ae01e26 Nikos Skalkotos
def check_writable_dir(option, opt_str, value, parser):
52 0ae01e26 Nikos Skalkotos
    dirname = os.path.dirname(value)
53 0ae01e26 Nikos Skalkotos
    name = os.path.basename(value)
54 0ae01e26 Nikos Skalkotos
    if dirname and not os.path.isdir(dirname):
55 b5430a9f Nikos Skalkotos
        raise FatalError("`%s' is not an existing directory" % dirname)
56 c408053f Nikos Skalkotos
57 0ae01e26 Nikos Skalkotos
    if not name:
58 b5430a9f Nikos Skalkotos
        raise FatalError("`%s' is not a valid file name" % dirname)
59 c408053f Nikos Skalkotos
60 c408053f Nikos Skalkotos
    setattr(parser.values, option.dest, value)
61 c408053f Nikos Skalkotos
62 c408053f Nikos Skalkotos
63 c408053f Nikos Skalkotos
def parse_options(input_args):
64 0ae01e26 Nikos Skalkotos
    usage = "Usage: %prog [options] <input_media>"
65 c408053f Nikos Skalkotos
    parser = optparse.OptionParser(version=version, usage=usage)
66 c408053f Nikos Skalkotos
67 997ac76a Nikos Skalkotos
    account = os.environ["OKEANOS_USER"] if "OKEANOS_USER" in os.environ \
68 997ac76a Nikos Skalkotos
        else None
69 997ac76a Nikos Skalkotos
    token = os.environ["OKEANOS_TOKEN"] if "OKEANOS_TOKEN" in os.environ \
70 997ac76a Nikos Skalkotos
        else None
71 9cbb5794 Nikos Skalkotos
72 0ae01e26 Nikos Skalkotos
    parser.add_option("-o", "--outfile", type="string", dest="outfile",
73 f99fe99d Nikos Skalkotos
                      default=None, action="callback",
74 f99fe99d Nikos Skalkotos
                      callback=check_writable_dir, help="dump image to FILE",
75 f99fe99d Nikos Skalkotos
                      metavar="FILE")
76 979096dd Nikos Skalkotos
77 997ac76a Nikos Skalkotos
    parser.add_option("-f", "--force", dest="force", default=False,
78 f99fe99d Nikos Skalkotos
                      action="store_true",
79 f99fe99d Nikos Skalkotos
                      help="overwrite output files if they exist")
80 76d4a1c9 Nikos Skalkotos
81 979096dd Nikos Skalkotos
    parser.add_option("-s", "--silent", dest="silent", default=False,
82 c16850ae Nikos Skalkotos
                      help="output only errors",
83 f99fe99d Nikos Skalkotos
                      action="store_true")
84 0ae01e26 Nikos Skalkotos
85 b5430a9f Nikos Skalkotos
    parser.add_option("-u", "--upload", dest="upload", type="string",
86 f99fe99d Nikos Skalkotos
                      default=False,
87 f99fe99d Nikos Skalkotos
                      help="upload the image to pithos with name FILENAME",
88 f99fe99d Nikos Skalkotos
                      metavar="FILENAME")
89 1a3f1298 Nikos Skalkotos
90 b5430a9f Nikos Skalkotos
    parser.add_option("-r", "--register", dest="register", type="string",
91 f99fe99d Nikos Skalkotos
                      default=False,
92 dcf9274b Vangelis Koukis
                      help="register the image with ~okeanos as IMAGENAME",
93 f99fe99d Nikos Skalkotos
                      metavar="IMAGENAME")
94 1a3f1298 Nikos Skalkotos
95 997ac76a Nikos Skalkotos
    parser.add_option("-a", "--account", dest="account", type="string",
96 c16850ae Nikos Skalkotos
                      default=account, help="use this ACCOUNT when "
97 dcf9274b Vangelis Koukis
                      "uploading/registering images [Default: %s]" % account)
98 997ac76a Nikos Skalkotos
99 7d3dc857 Nikos Skalkotos
    parser.add_option("-m", "--metadata", dest="metadata", default=[],
100 c16850ae Nikos Skalkotos
                      help="add custom KEY=VALUE metadata to the image",
101 f99fe99d Nikos Skalkotos
                      action="append", metavar="KEY=VALUE")
102 7d3dc857 Nikos Skalkotos
103 997ac76a Nikos Skalkotos
    parser.add_option("-t", "--token", dest="token", type="string",
104 c16850ae Nikos Skalkotos
                      default=token, help="use this token when "
105 dcf9274b Vangelis Koukis
                      "uploading/registering images [Default: %s]" % token)
106 997ac76a Nikos Skalkotos
107 997ac76a Nikos Skalkotos
    parser.add_option("--print-sysprep", dest="print_sysprep", default=False,
108 f99fe99d Nikos Skalkotos
                      help="print the enabled and disabled system preparation "
109 f99fe99d Nikos Skalkotos
                      "operations for this input media", action="store_true")
110 997ac76a Nikos Skalkotos
111 997ac76a Nikos Skalkotos
    parser.add_option("--enable-sysprep", dest="enabled_syspreps", default=[],
112 f99fe99d Nikos Skalkotos
                      help="run SYSPREP operation on the input media",
113 f99fe99d Nikos Skalkotos
                      action="append", metavar="SYSPREP")
114 997ac76a Nikos Skalkotos
115 997ac76a Nikos Skalkotos
    parser.add_option("--disable-sysprep", dest="disabled_syspreps",
116 f99fe99d Nikos Skalkotos
                      help="prevent SYSPREP operation from running on the "
117 f99fe99d Nikos Skalkotos
                      "input media", default=[], action="append",
118 f99fe99d Nikos Skalkotos
                      metavar="SYSPREP")
119 997ac76a Nikos Skalkotos
120 997ac76a Nikos Skalkotos
    parser.add_option("--no-sysprep", dest="sysprep", default=True,
121 c16850ae Nikos Skalkotos
                      help="don't perform any system preparation operation",
122 f99fe99d Nikos Skalkotos
                      action="store_false")
123 997ac76a Nikos Skalkotos
124 997ac76a Nikos Skalkotos
    parser.add_option("--no-shrink", dest="shrink", default=True,
125 f99fe99d Nikos Skalkotos
                      help="don't shrink any partition", action="store_false")
126 997ac76a Nikos Skalkotos
127 c16850ae Nikos Skalkotos
    parser.add_option("--tmpdir", dest="tmp", type="string", default=None,
128 c16850ae Nikos Skalkotos
                      help="create large temporary image files under DIR",
129 c16850ae Nikos Skalkotos
                      metavar="DIR")
130 c16850ae Nikos Skalkotos
131 c408053f Nikos Skalkotos
    options, args = parser.parse_args(input_args)
132 c408053f Nikos Skalkotos
133 0ae01e26 Nikos Skalkotos
    if len(args) != 1:
134 0ae01e26 Nikos Skalkotos
        parser.error('Wrong number of arguments')
135 7d3dc857 Nikos Skalkotos
136 c408053f Nikos Skalkotos
    options.source = args[0]
137 c408053f Nikos Skalkotos
    if not os.path.exists(options.source):
138 b5430a9f Nikos Skalkotos
        raise FatalError("Input media `%s' is not accessible" % options.source)
139 c408053f Nikos Skalkotos
140 f99fe99d Nikos Skalkotos
    if options.register and not options.upload:
141 b5430a9f Nikos Skalkotos
        raise FatalError("You also need to set -u when -r option is set")
142 1a3f1298 Nikos Skalkotos
143 997ac76a Nikos Skalkotos
    if options.upload and options.account is None:
144 997ac76a Nikos Skalkotos
        raise FatalError("Image uploading cannot be performed. No ~okeanos "
145 f99fe99d Nikos Skalkotos
                         "account name is specified. Use -a to set an account "
146 f99fe99d Nikos Skalkotos
                         "name.")
147 997ac76a Nikos Skalkotos
148 997ac76a Nikos Skalkotos
    if options.upload and options.token is None:
149 997ac76a Nikos Skalkotos
        raise FatalError("Image uploading cannot be performed. No ~okeanos "
150 f99fe99d Nikos Skalkotos
                         "token is specified. User -t to set a token.")
151 997ac76a Nikos Skalkotos
152 c16850ae Nikos Skalkotos
    if options.tmp is not None and not os.path.isdir(options.tmp):
153 c16850ae Nikos Skalkotos
        raise FatalError("The directory `%s' specified with --tmpdir is not "
154 c16850ae Nikos Skalkotos
                         "valid." % options.tmp)
155 c16850ae Nikos Skalkotos
156 7d3dc857 Nikos Skalkotos
    meta = {}
157 7d3dc857 Nikos Skalkotos
    for m in options.metadata:
158 7d3dc857 Nikos Skalkotos
        try:
159 7d3dc857 Nikos Skalkotos
            key, value = m.split('=', 1)
160 7d3dc857 Nikos Skalkotos
        except ValueError:
161 f99fe99d Nikos Skalkotos
            raise FatalError("Metadata option: `%s' is not in "
162 f99fe99d Nikos Skalkotos
                             "KEY=VALUE format." % m)
163 7d3dc857 Nikos Skalkotos
        meta[key] = value
164 7d3dc857 Nikos Skalkotos
    options.metadata = meta
165 7d3dc857 Nikos Skalkotos
166 c408053f Nikos Skalkotos
    return options
167 d57775d4 Nikos Skalkotos
168 8c574358 Nikos Skalkotos
169 22a6d232 Nikos Skalkotos
def image_creator():
170 c408053f Nikos Skalkotos
    options = parse_options(sys.argv[1:])
171 c408053f Nikos Skalkotos
172 f99fe99d Nikos Skalkotos
    if options.outfile is None and not options.upload and not \
173 f99fe99d Nikos Skalkotos
            options.print_sysprep:
174 f99fe99d Nikos Skalkotos
        raise FatalError("At least one of `-o', `-u' or `--print-sysprep' "
175 f99fe99d Nikos Skalkotos
                         "must be set")
176 76d4a1c9 Nikos Skalkotos
177 e77e66a9 Nikos Skalkotos
    if options.silent:
178 4e58b51b Nikos Skalkotos
        out = SilentOutput()
179 e77e66a9 Nikos Skalkotos
    else:
180 4e58b51b Nikos Skalkotos
        out = OutputWthProgress(True) if sys.stderr.isatty() else \
181 f99fe99d Nikos Skalkotos
            SimpleOutput(False)
182 e77e66a9 Nikos Skalkotos
183 e108efd2 Nikos Skalkotos
    title = 'snf-image-creator %s' % version
184 e77e66a9 Nikos Skalkotos
    out.output(title)
185 e77e66a9 Nikos Skalkotos
    out.output('=' * len(title))
186 979096dd Nikos Skalkotos
187 c408053f Nikos Skalkotos
    if os.geteuid() != 0:
188 f99fe99d Nikos Skalkotos
        raise FatalError("You must run %s as root"
189 f99fe99d Nikos Skalkotos
                         % os.path.basename(sys.argv[0]))
190 c408053f Nikos Skalkotos
191 69aa33fa Nikos Skalkotos
    if not options.force and options.outfile is not None:
192 8e3065a0 Nikos Skalkotos
        for extension in ('', '.meta', '.md5sum'):
193 0ae01e26 Nikos Skalkotos
            filename = "%s%s" % (options.outfile, extension)
194 c408053f Nikos Skalkotos
            if os.path.exists(filename):
195 c408053f Nikos Skalkotos
                raise FatalError("Output file %s exists "
196 f99fe99d Nikos Skalkotos
                                 "(use --force to overwrite it)." % filename)
197 c408053f Nikos Skalkotos
198 c16850ae Nikos Skalkotos
    disk = Disk(options.source, out, options.tmp)
199 9c354f13 Nikos Skalkotos
200 9c354f13 Nikos Skalkotos
    def signal_handler(signum, frame):
201 9c354f13 Nikos Skalkotos
        disk.cleanup()
202 9c354f13 Nikos Skalkotos
203 9c354f13 Nikos Skalkotos
    signal.signal(signal.SIGINT, signal_handler)
204 9c354f13 Nikos Skalkotos
    signal.signal(signal.SIGTERM, signal_handler)
205 d57775d4 Nikos Skalkotos
    try:
206 e22aa3a9 Nikos Skalkotos
        snapshot = disk.snapshot()
207 e22aa3a9 Nikos Skalkotos
208 e22aa3a9 Nikos Skalkotos
        dev = disk.get_device(snapshot)
209 df499fea Nikos Skalkotos
210 df499fea Nikos Skalkotos
        # If no customization is to be applied, the image should be mounted ro
211 a803b449 Nikos Skalkotos
        readonly = (not (options.sysprep or options.shrink) or
212 a803b449 Nikos Skalkotos
                    options.print_sysprep)
213 df499fea Nikos Skalkotos
        dev.mount(readonly)
214 22a6d232 Nikos Skalkotos
215 bb4db5a8 Nikos Skalkotos
        cls = os_cls(dev.distro, dev.ostype)
216 bb4db5a8 Nikos Skalkotos
        image_os = cls(dev.root, dev.g, out)
217 e77e66a9 Nikos Skalkotos
        out.output()
218 3f70f242 Nikos Skalkotos
219 f165adc0 Nikos Skalkotos
        for sysprep in options.disabled_syspreps:
220 e7cbfb0a Nikos Skalkotos
            image_os.disable_sysprep(image_os.get_sysprep_by_name(sysprep))
221 f165adc0 Nikos Skalkotos
222 f165adc0 Nikos Skalkotos
        for sysprep in options.enabled_syspreps:
223 e7cbfb0a Nikos Skalkotos
            image_os.enable_sysprep(image_os.get_sysprep_by_name(sysprep))
224 76d4a1c9 Nikos Skalkotos
225 f165adc0 Nikos Skalkotos
        if options.print_sysprep:
226 f165adc0 Nikos Skalkotos
            image_os.print_syspreps()
227 e77e66a9 Nikos Skalkotos
            out.output()
228 76d4a1c9 Nikos Skalkotos
229 76d4a1c9 Nikos Skalkotos
        if options.outfile is None and not options.upload:
230 76d4a1c9 Nikos Skalkotos
            return 0
231 76d4a1c9 Nikos Skalkotos
232 9cbb5794 Nikos Skalkotos
        if options.sysprep:
233 f165adc0 Nikos Skalkotos
            image_os.do_sysprep()
234 1a3f1298 Nikos Skalkotos
235 5886f568 Nikos Skalkotos
        metadata = image_os.meta
236 8c574358 Nikos Skalkotos
        dev.umount()
237 1a3f1298 Nikos Skalkotos
238 dbf466eb Nikos Skalkotos
        size = options.shrink and dev.shrink() or dev.size
239 e8b1b48b Nikos Skalkotos
        metadata.update(dev.meta)
240 ae48a082 Nikos Skalkotos
241 7d3dc857 Nikos Skalkotos
        # Add command line metadata to the collected ones...
242 7d3dc857 Nikos Skalkotos
        metadata.update(options.metadata)
243 7d3dc857 Nikos Skalkotos
244 e77e66a9 Nikos Skalkotos
        md5 = MD5(out)
245 e77e66a9 Nikos Skalkotos
        checksum = md5.compute(snapshot, size)
246 8e3065a0 Nikos Skalkotos
247 143e9484 Nikos Skalkotos
        metastring = '\n'.join(
248 f99fe99d Nikos Skalkotos
            ['%s=%s' % (key, value) for (key, value) in metadata.items()])
249 143e9484 Nikos Skalkotos
        metastring += '\n'
250 0ae01e26 Nikos Skalkotos
251 b1395967 Nikos Skalkotos
        if options.outfile is not None:
252 d603d80d Nikos Skalkotos
            dev.dump(options.outfile)
253 997ac76a Nikos Skalkotos
254 663f5f80 Nikos Skalkotos
            out.output('Dumping metadata file ...', False)
255 b1395967 Nikos Skalkotos
            with open('%s.%s' % (options.outfile, 'meta'), 'w') as f:
256 5b801534 Nikos Skalkotos
                f.write(metastring)
257 e77e66a9 Nikos Skalkotos
            out.success('done')
258 b1395967 Nikos Skalkotos
259 663f5f80 Nikos Skalkotos
            out.output('Dumping md5sum file ...', False)
260 b1395967 Nikos Skalkotos
            with open('%s.%s' % (options.outfile, 'md5sum'), 'w') as f:
261 f99fe99d Nikos Skalkotos
                f.write('%s %s\n' % (checksum,
262 f99fe99d Nikos Skalkotos
                                     os.path.basename(options.outfile)))
263 e77e66a9 Nikos Skalkotos
            out.success('done')
264 b1395967 Nikos Skalkotos
265 e22aa3a9 Nikos Skalkotos
        # Destroy the device. We only need the snapshot from now on
266 e22aa3a9 Nikos Skalkotos
        disk.destroy_device(dev)
267 e22aa3a9 Nikos Skalkotos
268 e77e66a9 Nikos Skalkotos
        out.output()
269 3b7d3fc7 Nikos Skalkotos
        try:
270 3b7d3fc7 Nikos Skalkotos
            uploaded_obj = ""
271 3b7d3fc7 Nikos Skalkotos
            if options.upload:
272 3b7d3fc7 Nikos Skalkotos
                out.output("Uploading image to pithos:")
273 3b7d3fc7 Nikos Skalkotos
                kamaki = Kamaki(options.account, options.token, out)
274 825fe2a6 Nikos Skalkotos
                with open(snapshot, 'rb') as f:
275 3b7d3fc7 Nikos Skalkotos
                    uploaded_obj = kamaki.upload(f, size, options.upload,
276 f99fe99d Nikos Skalkotos
                                                 "(1/4)  Calculating block "
277 f99fe99d Nikos Skalkotos
                                                 "hashes",
278 f99fe99d Nikos Skalkotos
                                                 "(2/4)  Uploading missing "
279 f99fe99d Nikos Skalkotos
                                                 "blocks")
280 3b7d3fc7 Nikos Skalkotos
281 3b7d3fc7 Nikos Skalkotos
                out.output("(3/4)  Uploading metadata file...", False)
282 3b7d3fc7 Nikos Skalkotos
                kamaki.upload(StringIO.StringIO(metastring),
283 3b7d3fc7 Nikos Skalkotos
                              size=len(metastring),
284 3b7d3fc7 Nikos Skalkotos
                              remote_path="%s.%s" % (options.upload, 'meta'))
285 3b7d3fc7 Nikos Skalkotos
                out.success('done')
286 663f5f80 Nikos Skalkotos
                out.output("(4/4)  Uploading md5sum file ...", False)
287 f99fe99d Nikos Skalkotos
                md5sumstr = '%s %s\n' % (checksum,
288 f99fe99d Nikos Skalkotos
                                         os.path.basename(options.upload))
289 3b7d3fc7 Nikos Skalkotos
                kamaki.upload(StringIO.StringIO(md5sumstr),
290 3b7d3fc7 Nikos Skalkotos
                              size=len(md5sumstr),
291 3b7d3fc7 Nikos Skalkotos
                              remote_path="%s.%s" % (options.upload, 'md5sum'))
292 3b7d3fc7 Nikos Skalkotos
                out.success('done')
293 3b7d3fc7 Nikos Skalkotos
                out.output()
294 3b7d3fc7 Nikos Skalkotos
295 3b7d3fc7 Nikos Skalkotos
            if options.register:
296 663f5f80 Nikos Skalkotos
                out.output('Registering image with ~okeanos ...', False)
297 3b7d3fc7 Nikos Skalkotos
                kamaki.register(options.register, uploaded_obj, metadata)
298 3b7d3fc7 Nikos Skalkotos
                out.success('done')
299 3b7d3fc7 Nikos Skalkotos
                out.output()
300 3b7d3fc7 Nikos Skalkotos
        except ClientError as e:
301 3b7d3fc7 Nikos Skalkotos
            raise FatalError("Pithos client: %d %s" % (e.status, e.message))
302 997ac76a Nikos Skalkotos
303 d57775d4 Nikos Skalkotos
    finally:
304 e77e66a9 Nikos Skalkotos
        out.output('cleaning up...')
305 d57775d4 Nikos Skalkotos
        disk.cleanup()
306 d57775d4 Nikos Skalkotos
307 e77e66a9 Nikos Skalkotos
    out.success("snf-image-creator exited without errors")
308 b1395967 Nikos Skalkotos
309 c408053f Nikos Skalkotos
    return 0
310 c408053f Nikos Skalkotos
311 ae48a082 Nikos Skalkotos
312 0ae01e26 Nikos Skalkotos
def main():
313 c408053f Nikos Skalkotos
    try:
314 0ae01e26 Nikos Skalkotos
        ret = image_creator()
315 c408053f Nikos Skalkotos
        sys.exit(ret)
316 c408053f Nikos Skalkotos
    except FatalError as e:
317 bb4db5a8 Nikos Skalkotos
        colored = sys.stderr.isatty()
318 bb4db5a8 Nikos Skalkotos
        SimpleOutput(colored).error(e)
319 c408053f Nikos Skalkotos
        sys.exit(1)
320 d57775d4 Nikos Skalkotos
321 0ae01e26 Nikos Skalkotos
if __name__ == '__main__':
322 0ae01e26 Nikos Skalkotos
    main()
323 ae48a082 Nikos Skalkotos
324 d57775d4 Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :