Statistics
| Branch: | Tag: | Revision:

root / image_creator / main.py @ 7d3dc857

History | View | Annotate | Download (10.2 kB)

1 ae48a082 Nikos Skalkotos
#!/usr/bin/env python
2 ae48a082 Nikos Skalkotos
3 d57775d4 Nikos Skalkotos
# Copyright 2011 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 8e3065a0 Nikos Skalkotos
from image_creator.util import get_command, error, success, output, \
40 8e3065a0 Nikos Skalkotos
                                                    FatalError, progress, md5
41 5b801534 Nikos Skalkotos
from image_creator.os_type import get_os_class
42 997ac76a Nikos Skalkotos
from image_creator.kamaki_wrapper import Kamaki
43 d57775d4 Nikos Skalkotos
import sys
44 d57775d4 Nikos Skalkotos
import os
45 c408053f Nikos Skalkotos
import optparse
46 b1395967 Nikos Skalkotos
import StringIO
47 3ccb2618 Nikos Skalkotos
48 c408053f Nikos Skalkotos
49 0ae01e26 Nikos Skalkotos
def check_writable_dir(option, opt_str, value, parser):
50 0ae01e26 Nikos Skalkotos
    dirname = os.path.dirname(value)
51 0ae01e26 Nikos Skalkotos
    name = os.path.basename(value)
52 0ae01e26 Nikos Skalkotos
    if dirname and not os.path.isdir(dirname):
53 b5430a9f Nikos Skalkotos
        raise FatalError("`%s' is not an existing directory" % dirname)
54 c408053f Nikos Skalkotos
55 0ae01e26 Nikos Skalkotos
    if not name:
56 b5430a9f Nikos Skalkotos
        raise FatalError("`%s' is not a valid file name" % dirname)
57 c408053f Nikos Skalkotos
58 c408053f Nikos Skalkotos
    setattr(parser.values, option.dest, value)
59 c408053f Nikos Skalkotos
60 c408053f Nikos Skalkotos
61 c408053f Nikos Skalkotos
def parse_options(input_args):
62 0ae01e26 Nikos Skalkotos
    usage = "Usage: %prog [options] <input_media>"
63 c408053f Nikos Skalkotos
    parser = optparse.OptionParser(version=version, usage=usage)
64 c408053f Nikos Skalkotos
65 997ac76a Nikos Skalkotos
    account = os.environ["OKEANOS_USER"] if "OKEANOS_USER" in os.environ \
66 997ac76a Nikos Skalkotos
        else None
67 997ac76a Nikos Skalkotos
    token = os.environ["OKEANOS_TOKEN"] if "OKEANOS_TOKEN" in os.environ \
68 997ac76a Nikos Skalkotos
        else None
69 9cbb5794 Nikos Skalkotos
70 0ae01e26 Nikos Skalkotos
    parser.add_option("-o", "--outfile", type="string", dest="outfile",
71 0ae01e26 Nikos Skalkotos
        default=None, action="callback", callback=check_writable_dir,
72 979096dd Nikos Skalkotos
        help="dump image to FILE", metavar="FILE")
73 979096dd Nikos Skalkotos
74 997ac76a Nikos Skalkotos
    parser.add_option("-f", "--force", dest="force", default=False,
75 997ac76a Nikos Skalkotos
        action="store_true", help="overwrite output files if they exist")
76 76d4a1c9 Nikos Skalkotos
77 979096dd Nikos Skalkotos
    parser.add_option("-s", "--silent", dest="silent", default=False,
78 f165adc0 Nikos Skalkotos
        help="silent mode, only output errors", action="store_true")
79 0ae01e26 Nikos Skalkotos
80 b5430a9f Nikos Skalkotos
    parser.add_option("-u", "--upload", dest="upload", type="string",
81 b5430a9f Nikos Skalkotos
        default=False, help="upload the image to pithos with name FILENAME",
82 b5430a9f Nikos Skalkotos
        metavar="FILENAME")
83 1a3f1298 Nikos Skalkotos
84 b5430a9f Nikos Skalkotos
    parser.add_option("-r", "--register", dest="register", type="string",
85 b5430a9f Nikos Skalkotos
        default=False, help="register the image to ~okeanos as IMAGENAME",
86 b5430a9f Nikos Skalkotos
        metavar="IMAGENAME")
87 1a3f1298 Nikos Skalkotos
88 997ac76a Nikos Skalkotos
    parser.add_option("-a", "--account", dest="account", type="string",
89 997ac76a Nikos Skalkotos
        default=account,
90 997ac76a Nikos Skalkotos
        help="Use this ACCOUNT when uploading/registring images [Default: %s]"\
91 997ac76a Nikos Skalkotos
        % account)
92 997ac76a Nikos Skalkotos
93 7d3dc857 Nikos Skalkotos
    parser.add_option("-m", "--metadata", dest="metadata", default=[],
94 7d3dc857 Nikos Skalkotos
        help="Add custom KEY=VALUE metadata to the image", action="append",
95 7d3dc857 Nikos Skalkotos
        metavar="KEY=VALUE")
96 7d3dc857 Nikos Skalkotos
97 997ac76a Nikos Skalkotos
    parser.add_option("-t", "--token", dest="token", type="string",
98 997ac76a Nikos Skalkotos
        default=token,
99 997ac76a Nikos Skalkotos
        help="Use this token when uploading/registring images [Default: %s]"\
100 997ac76a Nikos Skalkotos
        % token)
101 997ac76a Nikos Skalkotos
102 997ac76a Nikos Skalkotos
    parser.add_option("--print-sysprep", dest="print_sysprep", default=False,
103 997ac76a Nikos Skalkotos
        help="print the enabled and disabled system preparation operations "
104 997ac76a Nikos Skalkotos
        "for this input media", action="store_true")
105 997ac76a Nikos Skalkotos
106 997ac76a Nikos Skalkotos
    parser.add_option("--enable-sysprep", dest="enabled_syspreps", default=[],
107 997ac76a Nikos Skalkotos
        help="run SYSPREP operation on the input media",
108 997ac76a Nikos Skalkotos
        action="append", metavar="SYSPREP")
109 997ac76a Nikos Skalkotos
110 997ac76a Nikos Skalkotos
    parser.add_option("--disable-sysprep", dest="disabled_syspreps",
111 997ac76a Nikos Skalkotos
        help="prevent SYSPREP operation from running on the input media",
112 997ac76a Nikos Skalkotos
        default=[], action="append", metavar="SYSPREP")
113 997ac76a Nikos Skalkotos
114 997ac76a Nikos Skalkotos
    parser.add_option("--no-sysprep", dest="sysprep", default=True,
115 997ac76a Nikos Skalkotos
        help="don't perform system preperation", action="store_false")
116 997ac76a Nikos Skalkotos
117 997ac76a Nikos Skalkotos
    parser.add_option("--no-shrink", dest="shrink", default=True,
118 997ac76a Nikos Skalkotos
        help="don't shrink any partition", action="store_false")
119 997ac76a Nikos Skalkotos
120 c408053f Nikos Skalkotos
    options, args = parser.parse_args(input_args)
121 c408053f Nikos Skalkotos
122 0ae01e26 Nikos Skalkotos
    if len(args) != 1:
123 0ae01e26 Nikos Skalkotos
        parser.error('Wrong number of arguments')
124 7d3dc857 Nikos Skalkotos
125 c408053f Nikos Skalkotos
    options.source = args[0]
126 c408053f Nikos Skalkotos
    if not os.path.exists(options.source):
127 b5430a9f Nikos Skalkotos
        raise FatalError("Input media `%s' is not accessible" % options.source)
128 c408053f Nikos Skalkotos
129 997ac76a Nikos Skalkotos
    if options.register and options.upload == False:
130 b5430a9f Nikos Skalkotos
        raise FatalError("You also need to set -u when -r option is set")
131 1a3f1298 Nikos Skalkotos
132 997ac76a Nikos Skalkotos
    if options.upload and options.account is None:
133 997ac76a Nikos Skalkotos
        raise FatalError("Image uploading cannot be performed. No ~okeanos "
134 997ac76a Nikos Skalkotos
        "account name is specified. Use -a to set an account name.")
135 997ac76a Nikos Skalkotos
136 997ac76a Nikos Skalkotos
    if options.upload and options.token is None:
137 997ac76a Nikos Skalkotos
        raise FatalError("Image uploading cannot be performed. No ~okeanos "
138 997ac76a Nikos Skalkotos
        "token is specified. User -t to set a token.")
139 997ac76a Nikos Skalkotos
140 7d3dc857 Nikos Skalkotos
    meta = {}
141 7d3dc857 Nikos Skalkotos
    for m in options.metadata:
142 7d3dc857 Nikos Skalkotos
        try:
143 7d3dc857 Nikos Skalkotos
            key, value = m.split('=', 1)
144 7d3dc857 Nikos Skalkotos
        except ValueError:
145 7d3dc857 Nikos Skalkotos
            raise FatalError("Metadata option: `%s' is not in "\
146 7d3dc857 Nikos Skalkotos
                                                    "KEY=VALUE format." % m)
147 7d3dc857 Nikos Skalkotos
        meta[key] = value
148 7d3dc857 Nikos Skalkotos
    options.metadata = meta
149 7d3dc857 Nikos Skalkotos
150 c408053f Nikos Skalkotos
    return options
151 d57775d4 Nikos Skalkotos
152 8c574358 Nikos Skalkotos
153 22a6d232 Nikos Skalkotos
def image_creator():
154 c408053f Nikos Skalkotos
    options = parse_options(sys.argv[1:])
155 c408053f Nikos Skalkotos
156 979096dd Nikos Skalkotos
    if options.silent:
157 979096dd Nikos Skalkotos
        util.silent = True
158 979096dd Nikos Skalkotos
159 76d4a1c9 Nikos Skalkotos
    if options.outfile is None and not options.upload \
160 f165adc0 Nikos Skalkotos
                                            and not options.print_sysprep:
161 5b801534 Nikos Skalkotos
        raise FatalError("At least one of `-o', `-u' or `--print-sysprep' " \
162 5b801534 Nikos Skalkotos
                                                                "must be set")
163 76d4a1c9 Nikos Skalkotos
164 e108efd2 Nikos Skalkotos
    title = 'snf-image-creator %s' % version
165 e108efd2 Nikos Skalkotos
    output(title)
166 e108efd2 Nikos Skalkotos
    output('=' * len(title))
167 979096dd Nikos Skalkotos
168 c408053f Nikos Skalkotos
    if os.geteuid() != 0:
169 c408053f Nikos Skalkotos
        raise FatalError("You must run %s as root" \
170 c408053f Nikos Skalkotos
                        % os.path.basename(sys.argv[0]))
171 c408053f Nikos Skalkotos
172 69aa33fa Nikos Skalkotos
    if not options.force and options.outfile is not None:
173 8e3065a0 Nikos Skalkotos
        for extension in ('', '.meta', '.md5sum'):
174 0ae01e26 Nikos Skalkotos
            filename = "%s%s" % (options.outfile, extension)
175 c408053f Nikos Skalkotos
            if os.path.exists(filename):
176 c408053f Nikos Skalkotos
                raise FatalError("Output file %s exists "
177 c408053f Nikos Skalkotos
                    "(use --force to overwrite it)." % filename)
178 c408053f Nikos Skalkotos
179 c408053f Nikos Skalkotos
    disk = Disk(options.source)
180 d57775d4 Nikos Skalkotos
    try:
181 e22aa3a9 Nikos Skalkotos
        snapshot = disk.snapshot()
182 e22aa3a9 Nikos Skalkotos
183 e22aa3a9 Nikos Skalkotos
        dev = disk.get_device(snapshot)
184 0d5a999d Nikos Skalkotos
        dev.mount()
185 22a6d232 Nikos Skalkotos
186 aa2062ba Nikos Skalkotos
        osclass = get_os_class(dev.distro, dev.ostype)
187 aa2062ba Nikos Skalkotos
        image_os = osclass(dev.root, dev.g)
188 aa2062ba Nikos Skalkotos
        metadata = image_os.get_metadata()
189 979096dd Nikos Skalkotos
        output()
190 3f70f242 Nikos Skalkotos
191 f165adc0 Nikos Skalkotos
        for sysprep in options.disabled_syspreps:
192 f165adc0 Nikos Skalkotos
            image_os.disable_sysprep(sysprep)
193 f165adc0 Nikos Skalkotos
194 f165adc0 Nikos Skalkotos
        for sysprep in options.enabled_syspreps:
195 f165adc0 Nikos Skalkotos
            image_os.enable_sysprep(sysprep)
196 76d4a1c9 Nikos Skalkotos
197 f165adc0 Nikos Skalkotos
        if options.print_sysprep:
198 f165adc0 Nikos Skalkotos
            image_os.print_syspreps()
199 76d4a1c9 Nikos Skalkotos
            output()
200 76d4a1c9 Nikos Skalkotos
201 76d4a1c9 Nikos Skalkotos
        if options.outfile is None and not options.upload:
202 76d4a1c9 Nikos Skalkotos
            return 0
203 76d4a1c9 Nikos Skalkotos
204 9cbb5794 Nikos Skalkotos
        if options.sysprep:
205 f165adc0 Nikos Skalkotos
            image_os.do_sysprep()
206 1a3f1298 Nikos Skalkotos
207 8c574358 Nikos Skalkotos
        dev.umount()
208 1a3f1298 Nikos Skalkotos
209 e108efd2 Nikos Skalkotos
        size = options.shrink and dev.shrink() or dev.size
210 e8b1b48b Nikos Skalkotos
        metadata.update(dev.meta)
211 ae48a082 Nikos Skalkotos
212 7d3dc857 Nikos Skalkotos
        # Add command line metadata to the collected ones...
213 7d3dc857 Nikos Skalkotos
        metadata.update(options.metadata)
214 7d3dc857 Nikos Skalkotos
215 8e3065a0 Nikos Skalkotos
        checksum = md5(snapshot, size)
216 8e3065a0 Nikos Skalkotos
217 143e9484 Nikos Skalkotos
        metastring = '\n'.join(
218 5b801534 Nikos Skalkotos
                ['%s=%s' % (key, value) for (key, value) in metadata.items()])
219 143e9484 Nikos Skalkotos
        metastring += '\n'
220 0ae01e26 Nikos Skalkotos
221 b1395967 Nikos Skalkotos
        if options.outfile is not None:
222 d603d80d Nikos Skalkotos
            dev.dump(options.outfile)
223 997ac76a Nikos Skalkotos
224 b1395967 Nikos Skalkotos
            output('Dumping metadata file...', False)
225 b1395967 Nikos Skalkotos
            with open('%s.%s' % (options.outfile, 'meta'), 'w') as f:
226 5b801534 Nikos Skalkotos
                f.write(metastring)
227 b1395967 Nikos Skalkotos
            success('done')
228 b1395967 Nikos Skalkotos
229 b1395967 Nikos Skalkotos
            output('Dumping md5sum file...', False)
230 b1395967 Nikos Skalkotos
            with open('%s.%s' % (options.outfile, 'md5sum'), 'w') as f:
231 5b801534 Nikos Skalkotos
                f.write('%s %s\n' % (checksum, \
232 5b801534 Nikos Skalkotos
                                            os.path.basename(options.outfile)))
233 b1395967 Nikos Skalkotos
            success('done')
234 b1395967 Nikos Skalkotos
235 e22aa3a9 Nikos Skalkotos
        # Destroy the device. We only need the snapshot from now on
236 e22aa3a9 Nikos Skalkotos
        disk.destroy_device(dev)
237 e22aa3a9 Nikos Skalkotos
238 b1395967 Nikos Skalkotos
        output()
239 b1395967 Nikos Skalkotos
240 b1395967 Nikos Skalkotos
        uploaded_obj = ""
241 997ac76a Nikos Skalkotos
        if options.upload:
242 8e3065a0 Nikos Skalkotos
            output("Uploading image to pithos:")
243 997ac76a Nikos Skalkotos
            kamaki = Kamaki(options.account, options.token)
244 b1395967 Nikos Skalkotos
            with open(snapshot) as f:
245 b1395967 Nikos Skalkotos
                uploaded_obj = kamaki.upload(f, size, options.upload,
246 b1395967 Nikos Skalkotos
                                "(1/4)  Calculating block hashes",
247 b1395967 Nikos Skalkotos
                                "(2/4)  Uploading missing blocks")
248 b1395967 Nikos Skalkotos
249 b1395967 Nikos Skalkotos
            output("(3/4)  Uploading metadata file...", False)
250 b1395967 Nikos Skalkotos
            kamaki.upload(StringIO.StringIO(metastring), size=len(metastring),
251 b1395967 Nikos Skalkotos
                                remote_path="%s.%s" % (options.upload, 'meta'))
252 b1395967 Nikos Skalkotos
            success('done')
253 b1395967 Nikos Skalkotos
            output("(4/4)  Uploading md5sum file...", False)
254 143e9484 Nikos Skalkotos
            md5sumstr = '%s %s\n' % (
255 143e9484 Nikos Skalkotos
                checksum, os.path.basename(options.upload))
256 b1395967 Nikos Skalkotos
            kamaki.upload(StringIO.StringIO(md5sumstr), size=len(md5sumstr),
257 b1395967 Nikos Skalkotos
                            remote_path="%s.%s" % (options.upload, 'md5sum'))
258 b1395967 Nikos Skalkotos
            success('done')
259 b1395967 Nikos Skalkotos
            output()
260 b1395967 Nikos Skalkotos
261 b1395967 Nikos Skalkotos
        if options.register:
262 b1aea98e Nikos Skalkotos
            output('Registring image to ~okeanos...', False)
263 b1395967 Nikos Skalkotos
            kamaki.register(options.register, uploaded_obj, metadata)
264 42ace959 Nikos Skalkotos
            success('done')
265 b1395967 Nikos Skalkotos
            output()
266 997ac76a Nikos Skalkotos
267 d57775d4 Nikos Skalkotos
    finally:
268 979096dd Nikos Skalkotos
        output('cleaning up...')
269 d57775d4 Nikos Skalkotos
        disk.cleanup()
270 d57775d4 Nikos Skalkotos
271 b1395967 Nikos Skalkotos
    success("snf-image-creator exited without errors")
272 b1395967 Nikos Skalkotos
273 c408053f Nikos Skalkotos
    return 0
274 c408053f Nikos Skalkotos
275 ae48a082 Nikos Skalkotos
276 0ae01e26 Nikos Skalkotos
def main():
277 c408053f Nikos Skalkotos
    try:
278 0ae01e26 Nikos Skalkotos
        ret = image_creator()
279 c408053f Nikos Skalkotos
        sys.exit(ret)
280 c408053f Nikos Skalkotos
    except FatalError as e:
281 22a6d232 Nikos Skalkotos
        error(e)
282 c408053f Nikos Skalkotos
        sys.exit(1)
283 d57775d4 Nikos Skalkotos
284 0ae01e26 Nikos Skalkotos
285 0ae01e26 Nikos Skalkotos
if __name__ == '__main__':
286 0ae01e26 Nikos Skalkotos
    main()
287 ae48a082 Nikos Skalkotos
288 d57775d4 Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :