Statistics
| Branch: | Tag: | Revision:

root / image_creator / dialog_main.py @ 5e18a927

History | View | Annotate | Download (9.3 kB)

1 672cabb9 Nikos Skalkotos
#!/usr/bin/env python
2 121f3bc0 Nikos Skalkotos
# -*- coding: utf-8 -*-
3 121f3bc0 Nikos Skalkotos
#
4 672cabb9 Nikos Skalkotos
# Copyright 2012 GRNET S.A. All rights reserved.
5 672cabb9 Nikos Skalkotos
#
6 672cabb9 Nikos Skalkotos
# Redistribution and use in source and binary forms, with or
7 672cabb9 Nikos Skalkotos
# without modification, are permitted provided that the following
8 672cabb9 Nikos Skalkotos
# conditions are met:
9 672cabb9 Nikos Skalkotos
#
10 672cabb9 Nikos Skalkotos
#   1. Redistributions of source code must retain the above
11 672cabb9 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
12 672cabb9 Nikos Skalkotos
#      disclaimer.
13 672cabb9 Nikos Skalkotos
#
14 672cabb9 Nikos Skalkotos
#   2. Redistributions in binary form must reproduce the above
15 672cabb9 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
16 672cabb9 Nikos Skalkotos
#      disclaimer in the documentation and/or other materials
17 672cabb9 Nikos Skalkotos
#      provided with the distribution.
18 672cabb9 Nikos Skalkotos
#
19 672cabb9 Nikos Skalkotos
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
20 672cabb9 Nikos Skalkotos
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 672cabb9 Nikos Skalkotos
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 672cabb9 Nikos Skalkotos
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
23 672cabb9 Nikos Skalkotos
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 672cabb9 Nikos Skalkotos
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 672cabb9 Nikos Skalkotos
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 672cabb9 Nikos Skalkotos
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 672cabb9 Nikos Skalkotos
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 672cabb9 Nikos Skalkotos
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 672cabb9 Nikos Skalkotos
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 672cabb9 Nikos Skalkotos
# POSSIBILITY OF SUCH DAMAGE.
31 672cabb9 Nikos Skalkotos
#
32 672cabb9 Nikos Skalkotos
# The views and conclusions contained in the software and
33 672cabb9 Nikos Skalkotos
# documentation are those of the authors and should not be
34 672cabb9 Nikos Skalkotos
# interpreted as representing official policies, either expressed
35 672cabb9 Nikos Skalkotos
# or implied, of GRNET S.A.
36 672cabb9 Nikos Skalkotos
37 121f3bc0 Nikos Skalkotos
"""This module is the entrance point for the dialog-based version of the
38 121f3bc0 Nikos Skalkotos
snf-image-creator program. The main function will create a dialog where the
39 121f3bc0 Nikos Skalkotos
user is asked if he wants to use the program in expert or wizard mode.
40 121f3bc0 Nikos Skalkotos
"""
41 121f3bc0 Nikos Skalkotos
42 672cabb9 Nikos Skalkotos
import dialog
43 672cabb9 Nikos Skalkotos
import sys
44 672cabb9 Nikos Skalkotos
import os
45 02f71654 Nikos Skalkotos
import stat
46 672cabb9 Nikos Skalkotos
import textwrap
47 672cabb9 Nikos Skalkotos
import signal
48 5a0399d8 Nikos Skalkotos
import optparse
49 a26ae008 Nikos Skalkotos
import types
50 672cabb9 Nikos Skalkotos
51 672cabb9 Nikos Skalkotos
from image_creator import __version__ as version
52 023e1217 Nikos Skalkotos
from image_creator.util import FatalError
53 c3fe132c Nikos Skalkotos
from image_creator.output import Output
54 789a3763 Nikos Skalkotos
from image_creator.output.cli import SimpleOutput
55 023e1217 Nikos Skalkotos
from image_creator.output.dialog import GaugeOutput
56 c3fe132c Nikos Skalkotos
from image_creator.output.composite import CompositeOutput
57 672cabb9 Nikos Skalkotos
from image_creator.disk import Disk
58 88f83027 Nikos Skalkotos
from image_creator.dialog_wizard import start_wizard
59 023e1217 Nikos Skalkotos
from image_creator.dialog_menu import main_menu
60 023e1217 Nikos Skalkotos
from image_creator.dialog_util import SMALL_WIDTH, WIDTH, confirm_exit, \
61 023e1217 Nikos Skalkotos
    Reset, update_background_title
62 672cabb9 Nikos Skalkotos
63 672cabb9 Nikos Skalkotos
64 f5174d2c Nikos Skalkotos
def create_image(d, media, out, tmp):
65 88f83027 Nikos Skalkotos
    """Create an image out of `media'"""
66 a42a42b3 Nikos Skalkotos
    d.setBackgroundTitle('snf-image-creator')
67 a42a42b3 Nikos Skalkotos
68 789a3763 Nikos Skalkotos
    gauge = GaugeOutput(d, "Initialization", "Initializing...")
69 789a3763 Nikos Skalkotos
    out.add(gauge)
70 c16850ae Nikos Skalkotos
    disk = Disk(media, out, tmp)
71 672cabb9 Nikos Skalkotos
72 9afc4b89 Nikos Skalkotos
    def signal_handler(signum, frame):
73 789a3763 Nikos Skalkotos
        gauge.cleanup()
74 672cabb9 Nikos Skalkotos
        disk.cleanup()
75 672cabb9 Nikos Skalkotos
76 672cabb9 Nikos Skalkotos
    signal.signal(signal.SIGINT, signal_handler)
77 9c354f13 Nikos Skalkotos
    signal.signal(signal.SIGTERM, signal_handler)
78 672cabb9 Nikos Skalkotos
    try:
79 672cabb9 Nikos Skalkotos
        snapshot = disk.snapshot()
80 f5174d2c Nikos Skalkotos
        image = disk.get_image(snapshot)
81 672cabb9 Nikos Skalkotos
82 3afe6b44 Nikos Skalkotos
        out.output("Collecting image metadata ...")
83 a42a42b3 Nikos Skalkotos
        metadata = {}
84 f5174d2c Nikos Skalkotos
        for (key, value) in image.meta.items():
85 a42a42b3 Nikos Skalkotos
            metadata[str(key)] = str(value)
86 a42a42b3 Nikos Skalkotos
87 f5174d2c Nikos Skalkotos
        for (key, value) in image.os.meta.items():
88 a42a42b3 Nikos Skalkotos
            metadata[str(key)] = str(value)
89 a42a42b3 Nikos Skalkotos
90 7f623b20 Nikos Skalkotos
        out.success("done")
91 789a3763 Nikos Skalkotos
        gauge.cleanup()
92 789a3763 Nikos Skalkotos
        out.remove(gauge)
93 672cabb9 Nikos Skalkotos
94 789a3763 Nikos Skalkotos
        # Make sure the signal handler does not call gauge.cleanup again
95 672cabb9 Nikos Skalkotos
        def dummy(self):
96 672cabb9 Nikos Skalkotos
            pass
97 789a3763 Nikos Skalkotos
        gauge.cleanup = type(GaugeOutput.cleanup)(dummy, gauge, GaugeOutput)
98 672cabb9 Nikos Skalkotos
99 672cabb9 Nikos Skalkotos
        session = {"dialog": d,
100 672cabb9 Nikos Skalkotos
                   "disk": disk,
101 f5174d2c Nikos Skalkotos
                   "image": image,
102 672cabb9 Nikos Skalkotos
                   "metadata": metadata}
103 672cabb9 Nikos Skalkotos
104 cf4f52b6 Nikos Skalkotos
        msg = "snf-image-creator detected a %s system on the input media. " \
105 b9cd03ee Nikos Skalkotos
              "Would you like to run a wizard to assist you through the " \
106 b9cd03ee Nikos Skalkotos
              "image creation process?\n\nChoose <Wizard> to run the wizard," \
107 023e1217 Nikos Skalkotos
              " <Expert> to run the snf-image-creator in expert mode or " \
108 023e1217 Nikos Skalkotos
              "press ESC to quit the program." \
109 80411610 Nikos Skalkotos
              % (image.ostype if image.ostype == image.distro or
110 80411610 Nikos Skalkotos
                 image.distro == "unknown" else "%s (%s)" %
111 f5174d2c Nikos Skalkotos
                 (image.ostype, image.distro))
112 cf4f52b6 Nikos Skalkotos
113 3c33e68a Nikos Skalkotos
        update_background_title(session)
114 3c33e68a Nikos Skalkotos
115 cf4f52b6 Nikos Skalkotos
        while True:
116 023e1217 Nikos Skalkotos
            code = d.yesno(msg, width=WIDTH, height=12, yes_label="Wizard",
117 023e1217 Nikos Skalkotos
                           no_label="Expert")
118 cf4f52b6 Nikos Skalkotos
            if code == d.DIALOG_OK:
119 88f83027 Nikos Skalkotos
                if start_wizard(session):
120 cf4f52b6 Nikos Skalkotos
                    break
121 cf4f52b6 Nikos Skalkotos
            elif code == d.DIALOG_CANCEL:
122 cf4f52b6 Nikos Skalkotos
                main_menu(session)
123 cf4f52b6 Nikos Skalkotos
                break
124 5a0399d8 Nikos Skalkotos
125 24684bbb Nikos Skalkotos
            if confirm_exit(d):
126 cf4f52b6 Nikos Skalkotos
                break
127 5a0399d8 Nikos Skalkotos
128 672cabb9 Nikos Skalkotos
        d.infobox("Thank you for using snf-image-creator. Bye", width=53)
129 672cabb9 Nikos Skalkotos
    finally:
130 672cabb9 Nikos Skalkotos
        disk.cleanup()
131 672cabb9 Nikos Skalkotos
132 672cabb9 Nikos Skalkotos
    return 0
133 672cabb9 Nikos Skalkotos
134 672cabb9 Nikos Skalkotos
135 789a3763 Nikos Skalkotos
def select_file(d, media):
136 88f83027 Nikos Skalkotos
    """Select a media file"""
137 f2ddf4db Nikos Skalkotos
    if media == '/':
138 f2ddf4db Nikos Skalkotos
        return '/'
139 f2ddf4db Nikos Skalkotos
140 3664c5f5 Nikos Skalkotos
    default = os.getcwd() + os.sep
141 789a3763 Nikos Skalkotos
    while 1:
142 789a3763 Nikos Skalkotos
        if media is not None:
143 789a3763 Nikos Skalkotos
            if not os.path.exists(media):
144 789a3763 Nikos Skalkotos
                d.msgbox("The file `%s' you choose does not exist." % media,
145 023e1217 Nikos Skalkotos
                         width=SMALL_WIDTH)
146 789a3763 Nikos Skalkotos
            else:
147 02f71654 Nikos Skalkotos
                mode = os.stat(media).st_mode
148 02f71654 Nikos Skalkotos
                if not stat.S_ISDIR(mode):
149 02f71654 Nikos Skalkotos
                    break
150 3664c5f5 Nikos Skalkotos
                default = media
151 789a3763 Nikos Skalkotos
152 3664c5f5 Nikos Skalkotos
        (code, media) = d.fselect(default, 10, 60, extra_button=1,
153 6228d45e Nikos Skalkotos
                                  title="Please select an input media.",
154 02f71654 Nikos Skalkotos
                                  extra_label="Bundle Host")
155 789a3763 Nikos Skalkotos
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
156 789a3763 Nikos Skalkotos
            if confirm_exit(d, "You canceled the media selection dialog box."):
157 789a3763 Nikos Skalkotos
                sys.exit(0)
158 789a3763 Nikos Skalkotos
            else:
159 789a3763 Nikos Skalkotos
                media = None
160 789a3763 Nikos Skalkotos
                continue
161 b686bde9 Nikos Skalkotos
        elif code == d.DIALOG_EXTRA:
162 02f71654 Nikos Skalkotos
            return '/'
163 789a3763 Nikos Skalkotos
164 789a3763 Nikos Skalkotos
    return media
165 789a3763 Nikos Skalkotos
166 789a3763 Nikos Skalkotos
167 a26ae008 Nikos Skalkotos
def _dialog_form(self, text, height=20, width=60, form_height=15, fields=[],
168 a26ae008 Nikos Skalkotos
                 **kwargs):
169 a26ae008 Nikos Skalkotos
    """Display a form box.
170 a26ae008 Nikos Skalkotos

171 a26ae008 Nikos Skalkotos
    fields is in the form: [(label1, item1, item_length1), ...]
172 a26ae008 Nikos Skalkotos
    """
173 a26ae008 Nikos Skalkotos
174 a26ae008 Nikos Skalkotos
    cmd = ["--form", text, str(height), str(width), str(form_height)]
175 a26ae008 Nikos Skalkotos
176 a26ae008 Nikos Skalkotos
    label_len = 0
177 a26ae008 Nikos Skalkotos
    for field in fields:
178 a26ae008 Nikos Skalkotos
        if len(field[0]) > label_len:
179 a26ae008 Nikos Skalkotos
            label_len = len(field[0])
180 a26ae008 Nikos Skalkotos
181 49c07ce3 Nikos Skalkotos
    input_len = width - label_len - 1
182 a26ae008 Nikos Skalkotos
183 a26ae008 Nikos Skalkotos
    line = 1
184 a26ae008 Nikos Skalkotos
    for field in fields:
185 a26ae008 Nikos Skalkotos
        label = field[0]
186 a26ae008 Nikos Skalkotos
        item = field[1]
187 a26ae008 Nikos Skalkotos
        item_len = field[2]
188 a26ae008 Nikos Skalkotos
        cmd.extend((label, str(line), str(1), item, str(line),
189 49c07ce3 Nikos Skalkotos
                   str(label_len + 1), str(input_len), str(item_len)))
190 a26ae008 Nikos Skalkotos
        line += 1
191 a26ae008 Nikos Skalkotos
192 a26ae008 Nikos Skalkotos
    code, output = self._perform(*(cmd,), **kwargs)
193 a26ae008 Nikos Skalkotos
194 a26ae008 Nikos Skalkotos
    if not output:
195 a26ae008 Nikos Skalkotos
        return (code, [])
196 a26ae008 Nikos Skalkotos
197 a26ae008 Nikos Skalkotos
    return (code, output.splitlines())
198 a26ae008 Nikos Skalkotos
199 a26ae008 Nikos Skalkotos
200 672cabb9 Nikos Skalkotos
def main():
201 672cabb9 Nikos Skalkotos
202 672cabb9 Nikos Skalkotos
    d = dialog.Dialog(dialog="dialog")
203 672cabb9 Nikos Skalkotos
204 f9d8c3d9 Nikos Skalkotos
    # Add extra button in dialog library
205 f9d8c3d9 Nikos Skalkotos
    dialog._common_args_syntax["extra_button"] = \
206 f9d8c3d9 Nikos Skalkotos
        lambda enable: dialog._simple_option("--extra-button", enable)
207 f9d8c3d9 Nikos Skalkotos
208 f9d8c3d9 Nikos Skalkotos
    dialog._common_args_syntax["extra_label"] = \
209 f9d8c3d9 Nikos Skalkotos
        lambda string: ("--extra-label", string)
210 f9d8c3d9 Nikos Skalkotos
211 b9cd03ee Nikos Skalkotos
    # Allow yes-no label overwriting
212 b9cd03ee Nikos Skalkotos
    dialog._common_args_syntax["yes_label"] = \
213 b9cd03ee Nikos Skalkotos
        lambda string: ("--yes-label", string)
214 b9cd03ee Nikos Skalkotos
215 b9cd03ee Nikos Skalkotos
    dialog._common_args_syntax["no_label"] = \
216 b9cd03ee Nikos Skalkotos
        lambda string: ("--no-label", string)
217 b9cd03ee Nikos Skalkotos
218 a26ae008 Nikos Skalkotos
    # Monkey-patch pythondialog to include support for form dialog boxes
219 a26ae008 Nikos Skalkotos
    if not hasattr(dialog, 'form'):
220 a26ae008 Nikos Skalkotos
        d.form = types.MethodType(_dialog_form, d)
221 a26ae008 Nikos Skalkotos
222 789a3763 Nikos Skalkotos
    usage = "Usage: %prog [options] [<input_media>]"
223 789a3763 Nikos Skalkotos
    parser = optparse.OptionParser(version=version, usage=usage)
224 789a3763 Nikos Skalkotos
    parser.add_option("-l", "--logfile", type="string", dest="logfile",
225 789a3763 Nikos Skalkotos
                      default=None, help="log all messages to FILE",
226 789a3763 Nikos Skalkotos
                      metavar="FILE")
227 c16850ae Nikos Skalkotos
    parser.add_option("--tmpdir", type="string", dest="tmp", default=None,
228 c16850ae Nikos Skalkotos
                      help="create large temporary image files under DIR",
229 c16850ae Nikos Skalkotos
                      metavar="DIR")
230 789a3763 Nikos Skalkotos
231 789a3763 Nikos Skalkotos
    options, args = parser.parse_args(sys.argv[1:])
232 789a3763 Nikos Skalkotos
233 789a3763 Nikos Skalkotos
    if len(args) > 1:
234 789a3763 Nikos Skalkotos
        parser.error("Wrong number of arguments")
235 789a3763 Nikos Skalkotos
236 789a3763 Nikos Skalkotos
    d.setBackgroundTitle('snf-image-creator')
237 789a3763 Nikos Skalkotos
238 789a3763 Nikos Skalkotos
    try:
239 789a3763 Nikos Skalkotos
        if os.geteuid() != 0:
240 023e1217 Nikos Skalkotos
            raise FatalError("You must run %s as root" %
241 789a3763 Nikos Skalkotos
                             parser.get_prog_name())
242 789a3763 Nikos Skalkotos
243 c16850ae Nikos Skalkotos
        if options.tmp is not None and not os.path.isdir(options.tmp):
244 c16850ae Nikos Skalkotos
            raise FatalError("The directory `%s' specified with --tmpdir is "
245 c16850ae Nikos Skalkotos
                             "not valid" % options.tmp)
246 789a3763 Nikos Skalkotos
247 789a3763 Nikos Skalkotos
        logfile = None
248 789a3763 Nikos Skalkotos
        if options.logfile is not None:
249 672cabb9 Nikos Skalkotos
            try:
250 789a3763 Nikos Skalkotos
                logfile = open(options.logfile, 'w')
251 789a3763 Nikos Skalkotos
            except IOError as e:
252 789a3763 Nikos Skalkotos
                raise FatalError(
253 023e1217 Nikos Skalkotos
                    "Unable to open logfile `%s' for writing. Reason: %s" %
254 789a3763 Nikos Skalkotos
                    (options.logfile, e.strerror))
255 c16850ae Nikos Skalkotos
256 c16850ae Nikos Skalkotos
        media = select_file(d, args[0] if len(args) == 1 else None)
257 c16850ae Nikos Skalkotos
258 789a3763 Nikos Skalkotos
        try:
259 789a3763 Nikos Skalkotos
            log = SimpleOutput(False, logfile) if logfile is not None \
260 023e1217 Nikos Skalkotos
                else Output()
261 789a3763 Nikos Skalkotos
            while 1:
262 789a3763 Nikos Skalkotos
                try:
263 c3fe132c Nikos Skalkotos
                    out = CompositeOutput([log])
264 3afe6b44 Nikos Skalkotos
                    out.output("Starting %s v%s ..." %
265 789a3763 Nikos Skalkotos
                               (parser.get_prog_name(), version))
266 f5174d2c Nikos Skalkotos
                    ret = create_image(d, media, out, options.tmp)
267 789a3763 Nikos Skalkotos
                    sys.exit(ret)
268 789a3763 Nikos Skalkotos
                except Reset:
269 3afe6b44 Nikos Skalkotos
                    log.output("Resetting everything ...")
270 789a3763 Nikos Skalkotos
                    continue
271 789a3763 Nikos Skalkotos
        finally:
272 789a3763 Nikos Skalkotos
            if logfile is not None:
273 789a3763 Nikos Skalkotos
                logfile.close()
274 789a3763 Nikos Skalkotos
    except FatalError as e:
275 023e1217 Nikos Skalkotos
        msg = textwrap.fill(str(e), width=WIDTH)
276 023e1217 Nikos Skalkotos
        d.infobox(msg, width=WIDTH, title="Fatal Error")
277 789a3763 Nikos Skalkotos
        sys.exit(1)
278 672cabb9 Nikos Skalkotos
279 672cabb9 Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :