Statistics
| Branch: | Tag: | Revision:

root / image_creator / main.py @ c2cf27e8

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 0ae01e26 Nikos Skalkotos
    parser.add_option("-o", "--outfile", type="string", dest="outfile",
68 f99fe99d Nikos Skalkotos
                      default=None, action="callback",
69 f99fe99d Nikos Skalkotos
                      callback=check_writable_dir, help="dump image to FILE",
70 f99fe99d Nikos Skalkotos
                      metavar="FILE")
71 979096dd Nikos Skalkotos
72 997ac76a Nikos Skalkotos
    parser.add_option("-f", "--force", dest="force", default=False,
73 f99fe99d Nikos Skalkotos
                      action="store_true",
74 f99fe99d Nikos Skalkotos
                      help="overwrite output files if they exist")
75 76d4a1c9 Nikos Skalkotos
76 979096dd Nikos Skalkotos
    parser.add_option("-s", "--silent", dest="silent", default=False,
77 c16850ae Nikos Skalkotos
                      help="output only errors",
78 f99fe99d Nikos Skalkotos
                      action="store_true")
79 0ae01e26 Nikos Skalkotos
80 b5430a9f Nikos Skalkotos
    parser.add_option("-u", "--upload", dest="upload", type="string",
81 f99fe99d Nikos Skalkotos
                      default=False,
82 f99fe99d Nikos Skalkotos
                      help="upload the image to pithos with name FILENAME",
83 f99fe99d Nikos Skalkotos
                      metavar="FILENAME")
84 1a3f1298 Nikos Skalkotos
85 b5430a9f Nikos Skalkotos
    parser.add_option("-r", "--register", dest="register", type="string",
86 f99fe99d Nikos Skalkotos
                      default=False,
87 dcf9274b Vangelis Koukis
                      help="register the image with ~okeanos as IMAGENAME",
88 f99fe99d Nikos Skalkotos
                      metavar="IMAGENAME")
89 1a3f1298 Nikos Skalkotos
90 7d3dc857 Nikos Skalkotos
    parser.add_option("-m", "--metadata", dest="metadata", default=[],
91 c16850ae Nikos Skalkotos
                      help="add custom KEY=VALUE metadata to the image",
92 f99fe99d Nikos Skalkotos
                      action="append", metavar="KEY=VALUE")
93 7d3dc857 Nikos Skalkotos
94 997ac76a Nikos Skalkotos
    parser.add_option("-t", "--token", dest="token", type="string",
95 c2cf27e8 Nikos Skalkotos
                      default=None, help="use this authentication token when "
96 c2cf27e8 Nikos Skalkotos
                      "uploading/registering images")
97 997ac76a Nikos Skalkotos
98 997ac76a Nikos Skalkotos
    parser.add_option("--print-sysprep", dest="print_sysprep", default=False,
99 f99fe99d Nikos Skalkotos
                      help="print the enabled and disabled system preparation "
100 f99fe99d Nikos Skalkotos
                      "operations for this input media", action="store_true")
101 997ac76a Nikos Skalkotos
102 997ac76a Nikos Skalkotos
    parser.add_option("--enable-sysprep", dest="enabled_syspreps", default=[],
103 f99fe99d Nikos Skalkotos
                      help="run SYSPREP operation on the input media",
104 f99fe99d Nikos Skalkotos
                      action="append", metavar="SYSPREP")
105 997ac76a Nikos Skalkotos
106 997ac76a Nikos Skalkotos
    parser.add_option("--disable-sysprep", dest="disabled_syspreps",
107 f99fe99d Nikos Skalkotos
                      help="prevent SYSPREP operation from running on the "
108 f99fe99d Nikos Skalkotos
                      "input media", default=[], action="append",
109 f99fe99d Nikos Skalkotos
                      metavar="SYSPREP")
110 997ac76a Nikos Skalkotos
111 997ac76a Nikos Skalkotos
    parser.add_option("--no-sysprep", dest="sysprep", default=True,
112 c16850ae Nikos Skalkotos
                      help="don't perform any system preparation operation",
113 f99fe99d Nikos Skalkotos
                      action="store_false")
114 997ac76a Nikos Skalkotos
115 997ac76a Nikos Skalkotos
    parser.add_option("--no-shrink", dest="shrink", default=True,
116 f99fe99d Nikos Skalkotos
                      help="don't shrink any partition", action="store_false")
117 997ac76a Nikos Skalkotos
118 37d581b8 Nikos Skalkotos
    parser.add_option("--public", dest="public", default=False,
119 aa486e93 Nikos Skalkotos
                      help="register image with cyclades as public",
120 37d581b8 Nikos Skalkotos
                      action="store_true")
121 37d581b8 Nikos Skalkotos
122 c16850ae Nikos Skalkotos
    parser.add_option("--tmpdir", dest="tmp", type="string", default=None,
123 c16850ae Nikos Skalkotos
                      help="create large temporary image files under DIR",
124 c16850ae Nikos Skalkotos
                      metavar="DIR")
125 c16850ae Nikos Skalkotos
126 c408053f Nikos Skalkotos
    options, args = parser.parse_args(input_args)
127 c408053f Nikos Skalkotos
128 0ae01e26 Nikos Skalkotos
    if len(args) != 1:
129 0ae01e26 Nikos Skalkotos
        parser.error('Wrong number of arguments')
130 7d3dc857 Nikos Skalkotos
131 c408053f Nikos Skalkotos
    options.source = args[0]
132 c408053f Nikos Skalkotos
    if not os.path.exists(options.source):
133 b5430a9f Nikos Skalkotos
        raise FatalError("Input media `%s' is not accessible" % options.source)
134 c408053f Nikos Skalkotos
135 f99fe99d Nikos Skalkotos
    if options.register and not options.upload:
136 b5430a9f Nikos Skalkotos
        raise FatalError("You also need to set -u when -r option is set")
137 1a3f1298 Nikos Skalkotos
138 997ac76a Nikos Skalkotos
    if options.upload and options.token is None:
139 37d581b8 Nikos Skalkotos
        raise FatalError(
140 37d581b8 Nikos Skalkotos
            "Image uploading cannot be performed. "
141 31160dc8 Nikos Skalkotos
            "No authentication token is specified. Use -t to set a token")
142 997ac76a Nikos Skalkotos
143 c16850ae Nikos Skalkotos
    if options.tmp is not None and not os.path.isdir(options.tmp):
144 c16850ae Nikos Skalkotos
        raise FatalError("The directory `%s' specified with --tmpdir is not "
145 31160dc8 Nikos Skalkotos
                         "valid" % options.tmp)
146 c16850ae Nikos Skalkotos
147 7d3dc857 Nikos Skalkotos
    meta = {}
148 7d3dc857 Nikos Skalkotos
    for m in options.metadata:
149 7d3dc857 Nikos Skalkotos
        try:
150 7d3dc857 Nikos Skalkotos
            key, value = m.split('=', 1)
151 7d3dc857 Nikos Skalkotos
        except ValueError:
152 31160dc8 Nikos Skalkotos
            raise FatalError("Metadata option: `%s' is not in KEY=VALUE "
153 31160dc8 Nikos Skalkotos
                             "format." % m)
154 7d3dc857 Nikos Skalkotos
        meta[key] = value
155 7d3dc857 Nikos Skalkotos
    options.metadata = meta
156 7d3dc857 Nikos Skalkotos
157 c408053f Nikos Skalkotos
    return options
158 d57775d4 Nikos Skalkotos
159 8c574358 Nikos Skalkotos
160 22a6d232 Nikos Skalkotos
def image_creator():
161 c408053f Nikos Skalkotos
    options = parse_options(sys.argv[1:])
162 c408053f Nikos Skalkotos
163 f99fe99d Nikos Skalkotos
    if options.outfile is None and not options.upload and not \
164 f99fe99d Nikos Skalkotos
            options.print_sysprep:
165 f99fe99d Nikos Skalkotos
        raise FatalError("At least one of `-o', `-u' or `--print-sysprep' "
166 f99fe99d Nikos Skalkotos
                         "must be set")
167 76d4a1c9 Nikos Skalkotos
168 e77e66a9 Nikos Skalkotos
    if options.silent:
169 4e58b51b Nikos Skalkotos
        out = SilentOutput()
170 e77e66a9 Nikos Skalkotos
    else:
171 4e58b51b Nikos Skalkotos
        out = OutputWthProgress(True) if sys.stderr.isatty() else \
172 f99fe99d Nikos Skalkotos
            SimpleOutput(False)
173 e77e66a9 Nikos Skalkotos
174 e108efd2 Nikos Skalkotos
    title = 'snf-image-creator %s' % version
175 e77e66a9 Nikos Skalkotos
    out.output(title)
176 e77e66a9 Nikos Skalkotos
    out.output('=' * len(title))
177 979096dd Nikos Skalkotos
178 c408053f Nikos Skalkotos
    if os.geteuid() != 0:
179 f99fe99d Nikos Skalkotos
        raise FatalError("You must run %s as root"
180 f99fe99d Nikos Skalkotos
                         % os.path.basename(sys.argv[0]))
181 c408053f Nikos Skalkotos
182 69aa33fa Nikos Skalkotos
    if not options.force and options.outfile is not None:
183 8e3065a0 Nikos Skalkotos
        for extension in ('', '.meta', '.md5sum'):
184 0ae01e26 Nikos Skalkotos
            filename = "%s%s" % (options.outfile, extension)
185 c408053f Nikos Skalkotos
            if os.path.exists(filename):
186 c408053f Nikos Skalkotos
                raise FatalError("Output file %s exists "
187 31160dc8 Nikos Skalkotos
                                 "(use --force to overwrite it)" % filename)
188 31160dc8 Nikos Skalkotos
189 31160dc8 Nikos Skalkotos
    # Check if the authentication token is valid. The earlier the better
190 c2cf27e8 Nikos Skalkotos
    if options.token is not None:
191 c2cf27e8 Nikos Skalkotos
        try:
192 c2cf27e8 Nikos Skalkotos
            account = Kamaki.get_account(options.token)
193 c2cf27e8 Nikos Skalkotos
            if account is None:
194 c2cf27e8 Nikos Skalkotos
                raise FatalError("The authentication token you provided is not"
195 c2cf27e8 Nikos Skalkotos
                                 " valid!")
196 c2cf27e8 Nikos Skalkotos
        except ClientError as e:
197 c2cf27e8 Nikos Skalkotos
            raise FatalError("Astakos client: %d %s" % (e.status, e.message))
198 c2cf27e8 Nikos Skalkotos
199 c408053f Nikos Skalkotos
200 c16850ae Nikos Skalkotos
    disk = Disk(options.source, out, options.tmp)
201 9c354f13 Nikos Skalkotos
202 9c354f13 Nikos Skalkotos
    def signal_handler(signum, frame):
203 9c354f13 Nikos Skalkotos
        disk.cleanup()
204 9c354f13 Nikos Skalkotos
205 9c354f13 Nikos Skalkotos
    signal.signal(signal.SIGINT, signal_handler)
206 9c354f13 Nikos Skalkotos
    signal.signal(signal.SIGTERM, signal_handler)
207 d57775d4 Nikos Skalkotos
    try:
208 e22aa3a9 Nikos Skalkotos
        snapshot = disk.snapshot()
209 e22aa3a9 Nikos Skalkotos
210 e22aa3a9 Nikos Skalkotos
        dev = disk.get_device(snapshot)
211 df499fea Nikos Skalkotos
212 df499fea Nikos Skalkotos
        # If no customization is to be applied, the image should be mounted ro
213 a803b449 Nikos Skalkotos
        readonly = (not (options.sysprep or options.shrink) or
214 a803b449 Nikos Skalkotos
                    options.print_sysprep)
215 df499fea Nikos Skalkotos
        dev.mount(readonly)
216 22a6d232 Nikos Skalkotos
217 bb4db5a8 Nikos Skalkotos
        cls = os_cls(dev.distro, dev.ostype)
218 bb4db5a8 Nikos Skalkotos
        image_os = cls(dev.root, dev.g, out)
219 e77e66a9 Nikos Skalkotos
        out.output()
220 3f70f242 Nikos Skalkotos
221 f165adc0 Nikos Skalkotos
        for sysprep in options.disabled_syspreps:
222 e7cbfb0a Nikos Skalkotos
            image_os.disable_sysprep(image_os.get_sysprep_by_name(sysprep))
223 f165adc0 Nikos Skalkotos
224 f165adc0 Nikos Skalkotos
        for sysprep in options.enabled_syspreps:
225 e7cbfb0a Nikos Skalkotos
            image_os.enable_sysprep(image_os.get_sysprep_by_name(sysprep))
226 76d4a1c9 Nikos Skalkotos
227 f165adc0 Nikos Skalkotos
        if options.print_sysprep:
228 f165adc0 Nikos Skalkotos
            image_os.print_syspreps()
229 e77e66a9 Nikos Skalkotos
            out.output()
230 76d4a1c9 Nikos Skalkotos
231 76d4a1c9 Nikos Skalkotos
        if options.outfile is None and not options.upload:
232 76d4a1c9 Nikos Skalkotos
            return 0
233 76d4a1c9 Nikos Skalkotos
234 9cbb5794 Nikos Skalkotos
        if options.sysprep:
235 f165adc0 Nikos Skalkotos
            image_os.do_sysprep()
236 1a3f1298 Nikos Skalkotos
237 5886f568 Nikos Skalkotos
        metadata = image_os.meta
238 8c574358 Nikos Skalkotos
        dev.umount()
239 1a3f1298 Nikos Skalkotos
240 dbf466eb Nikos Skalkotos
        size = options.shrink and dev.shrink() or dev.size
241 e8b1b48b Nikos Skalkotos
        metadata.update(dev.meta)
242 ae48a082 Nikos Skalkotos
243 7d3dc857 Nikos Skalkotos
        # Add command line metadata to the collected ones...
244 7d3dc857 Nikos Skalkotos
        metadata.update(options.metadata)
245 7d3dc857 Nikos Skalkotos
246 e77e66a9 Nikos Skalkotos
        md5 = MD5(out)
247 e77e66a9 Nikos Skalkotos
        checksum = md5.compute(snapshot, size)
248 8e3065a0 Nikos Skalkotos
249 143e9484 Nikos Skalkotos
        metastring = '\n'.join(
250 f99fe99d Nikos Skalkotos
            ['%s=%s' % (key, value) for (key, value) in metadata.items()])
251 143e9484 Nikos Skalkotos
        metastring += '\n'
252 0ae01e26 Nikos Skalkotos
253 b1395967 Nikos Skalkotos
        if options.outfile is not None:
254 d603d80d Nikos Skalkotos
            dev.dump(options.outfile)
255 997ac76a Nikos Skalkotos
256 663f5f80 Nikos Skalkotos
            out.output('Dumping metadata file ...', False)
257 b1395967 Nikos Skalkotos
            with open('%s.%s' % (options.outfile, 'meta'), 'w') as f:
258 5b801534 Nikos Skalkotos
                f.write(metastring)
259 e77e66a9 Nikos Skalkotos
            out.success('done')
260 b1395967 Nikos Skalkotos
261 663f5f80 Nikos Skalkotos
            out.output('Dumping md5sum file ...', False)
262 b1395967 Nikos Skalkotos
            with open('%s.%s' % (options.outfile, 'md5sum'), 'w') as f:
263 f99fe99d Nikos Skalkotos
                f.write('%s %s\n' % (checksum,
264 f99fe99d Nikos Skalkotos
                                     os.path.basename(options.outfile)))
265 e77e66a9 Nikos Skalkotos
            out.success('done')
266 b1395967 Nikos Skalkotos
267 e22aa3a9 Nikos Skalkotos
        # Destroy the device. We only need the snapshot from now on
268 e22aa3a9 Nikos Skalkotos
        disk.destroy_device(dev)
269 e22aa3a9 Nikos Skalkotos
270 e77e66a9 Nikos Skalkotos
        out.output()
271 3b7d3fc7 Nikos Skalkotos
        try:
272 3b7d3fc7 Nikos Skalkotos
            uploaded_obj = ""
273 3b7d3fc7 Nikos Skalkotos
            if options.upload:
274 3b7d3fc7 Nikos Skalkotos
                out.output("Uploading image to pithos:")
275 31160dc8 Nikos Skalkotos
                kamaki = Kamaki(account, out)
276 825fe2a6 Nikos Skalkotos
                with open(snapshot, 'rb') as f:
277 37d581b8 Nikos Skalkotos
                    uploaded_obj = kamaki.upload(
278 37d581b8 Nikos Skalkotos
                        f, size, options.upload,
279 31160dc8 Nikos Skalkotos
                        "(1/4)  Calculating block hashes",
280 31160dc8 Nikos Skalkotos
                        "(2/4)  Uploading missing blocks")
281 3b7d3fc7 Nikos Skalkotos
282 fd9af948 Nikos Skalkotos
                out.output("(3/4)  Uploading metadata file ...", False)
283 3b7d3fc7 Nikos Skalkotos
                kamaki.upload(StringIO.StringIO(metastring),
284 3b7d3fc7 Nikos Skalkotos
                              size=len(metastring),
285 3b7d3fc7 Nikos Skalkotos
                              remote_path="%s.%s" % (options.upload, 'meta'))
286 3b7d3fc7 Nikos Skalkotos
                out.success('done')
287 663f5f80 Nikos Skalkotos
                out.output("(4/4)  Uploading md5sum file ...", False)
288 f99fe99d Nikos Skalkotos
                md5sumstr = '%s %s\n' % (checksum,
289 f99fe99d Nikos Skalkotos
                                         os.path.basename(options.upload))
290 3b7d3fc7 Nikos Skalkotos
                kamaki.upload(StringIO.StringIO(md5sumstr),
291 3b7d3fc7 Nikos Skalkotos
                              size=len(md5sumstr),
292 3b7d3fc7 Nikos Skalkotos
                              remote_path="%s.%s" % (options.upload, 'md5sum'))
293 3b7d3fc7 Nikos Skalkotos
                out.success('done')
294 3b7d3fc7 Nikos Skalkotos
                out.output()
295 3b7d3fc7 Nikos Skalkotos
296 3b7d3fc7 Nikos Skalkotos
            if options.register:
297 37d581b8 Nikos Skalkotos
                img_type = 'public' if options.public else 'private'
298 37d581b8 Nikos Skalkotos
                out.output('Registering %s image with ~okeanos ...' % img_type,
299 37d581b8 Nikos Skalkotos
                           False)
300 37d581b8 Nikos Skalkotos
                kamaki.register(options.register, uploaded_obj, metadata,
301 37d581b8 Nikos Skalkotos
                                options.public)
302 3b7d3fc7 Nikos Skalkotos
                out.success('done')
303 3b7d3fc7 Nikos Skalkotos
                out.output()
304 3b7d3fc7 Nikos Skalkotos
        except ClientError as e:
305 3b7d3fc7 Nikos Skalkotos
            raise FatalError("Pithos client: %d %s" % (e.status, e.message))
306 997ac76a Nikos Skalkotos
307 d57775d4 Nikos Skalkotos
    finally:
308 fd9af948 Nikos Skalkotos
        out.output('cleaning up ...')
309 d57775d4 Nikos Skalkotos
        disk.cleanup()
310 d57775d4 Nikos Skalkotos
311 e77e66a9 Nikos Skalkotos
    out.success("snf-image-creator exited without errors")
312 b1395967 Nikos Skalkotos
313 c408053f Nikos Skalkotos
    return 0
314 c408053f Nikos Skalkotos
315 ae48a082 Nikos Skalkotos
316 0ae01e26 Nikos Skalkotos
def main():
317 c408053f Nikos Skalkotos
    try:
318 0ae01e26 Nikos Skalkotos
        ret = image_creator()
319 c408053f Nikos Skalkotos
        sys.exit(ret)
320 c408053f Nikos Skalkotos
    except FatalError as e:
321 bb4db5a8 Nikos Skalkotos
        colored = sys.stderr.isatty()
322 bb4db5a8 Nikos Skalkotos
        SimpleOutput(colored).error(e)
323 c408053f Nikos Skalkotos
        sys.exit(1)
324 d57775d4 Nikos Skalkotos
325 0ae01e26 Nikos Skalkotos
if __name__ == '__main__':
326 0ae01e26 Nikos Skalkotos
    main()
327 ae48a082 Nikos Skalkotos
328 d57775d4 Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :