Statistics
| Branch: | Tag: | Revision:

root / image_creator / dialog_main.py @ f953c647

History | View | Annotate | Download (10.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 d0daafa2 Nikos Skalkotos
        if image.is_unsupported():
105 e482b7f9 Nikos Skalkotos
106 e482b7f9 Nikos Skalkotos
            session['excluded_tasks'] = [-1]
107 e482b7f9 Nikos Skalkotos
            session['task_metadata'] = ["EXCLUDE_ALL_TASKS"]
108 e482b7f9 Nikos Skalkotos
109 e482b7f9 Nikos Skalkotos
            msg = "The system on the input media is not supported." \
110 e482b7f9 Nikos Skalkotos
                "\n\nReason: %s\n\n" \
111 e482b7f9 Nikos Skalkotos
                "We highly recommend not to create an image out of this, " \
112 e482b7f9 Nikos Skalkotos
                "since the image won't be cleaned up and you will not be " \
113 e482b7f9 Nikos Skalkotos
                "able to configure it during the deployment. Press <YES> if " \
114 e482b7f9 Nikos Skalkotos
                "you still want to continue with the image creation process." \
115 d0daafa2 Nikos Skalkotos
                % image._unsupported
116 e482b7f9 Nikos Skalkotos
117 e482b7f9 Nikos Skalkotos
            if not d.yesno(msg, width=WIDTH, defaultno=1, height=12):
118 e482b7f9 Nikos Skalkotos
                main_menu(session)
119 e482b7f9 Nikos Skalkotos
120 e482b7f9 Nikos Skalkotos
            d.infobox("Thank you for using snf-image-creator. Bye", width=53)
121 e482b7f9 Nikos Skalkotos
            return 0
122 e482b7f9 Nikos Skalkotos
123 cf4f52b6 Nikos Skalkotos
        msg = "snf-image-creator detected a %s system on the input media. " \
124 b9cd03ee Nikos Skalkotos
              "Would you like to run a wizard to assist you through the " \
125 b9cd03ee Nikos Skalkotos
              "image creation process?\n\nChoose <Wizard> to run the wizard," \
126 023e1217 Nikos Skalkotos
              " <Expert> to run the snf-image-creator in expert mode or " \
127 023e1217 Nikos Skalkotos
              "press ESC to quit the program." \
128 80411610 Nikos Skalkotos
              % (image.ostype if image.ostype == image.distro or
129 80411610 Nikos Skalkotos
                 image.distro == "unknown" else "%s (%s)" %
130 f5174d2c Nikos Skalkotos
                 (image.ostype, image.distro))
131 cf4f52b6 Nikos Skalkotos
132 3c33e68a Nikos Skalkotos
        update_background_title(session)
133 3c33e68a Nikos Skalkotos
134 cf4f52b6 Nikos Skalkotos
        while True:
135 023e1217 Nikos Skalkotos
            code = d.yesno(msg, width=WIDTH, height=12, yes_label="Wizard",
136 023e1217 Nikos Skalkotos
                           no_label="Expert")
137 cf4f52b6 Nikos Skalkotos
            if code == d.DIALOG_OK:
138 88f83027 Nikos Skalkotos
                if start_wizard(session):
139 cf4f52b6 Nikos Skalkotos
                    break
140 cf4f52b6 Nikos Skalkotos
            elif code == d.DIALOG_CANCEL:
141 cf4f52b6 Nikos Skalkotos
                main_menu(session)
142 cf4f52b6 Nikos Skalkotos
                break
143 5a0399d8 Nikos Skalkotos
144 24684bbb Nikos Skalkotos
            if confirm_exit(d):
145 cf4f52b6 Nikos Skalkotos
                break
146 5a0399d8 Nikos Skalkotos
147 672cabb9 Nikos Skalkotos
        d.infobox("Thank you for using snf-image-creator. Bye", width=53)
148 672cabb9 Nikos Skalkotos
    finally:
149 672cabb9 Nikos Skalkotos
        disk.cleanup()
150 672cabb9 Nikos Skalkotos
151 672cabb9 Nikos Skalkotos
    return 0
152 672cabb9 Nikos Skalkotos
153 672cabb9 Nikos Skalkotos
154 789a3763 Nikos Skalkotos
def select_file(d, media):
155 88f83027 Nikos Skalkotos
    """Select a media file"""
156 f2ddf4db Nikos Skalkotos
    if media == '/':
157 f2ddf4db Nikos Skalkotos
        return '/'
158 f2ddf4db Nikos Skalkotos
159 3664c5f5 Nikos Skalkotos
    default = os.getcwd() + os.sep
160 789a3763 Nikos Skalkotos
    while 1:
161 789a3763 Nikos Skalkotos
        if media is not None:
162 789a3763 Nikos Skalkotos
            if not os.path.exists(media):
163 789a3763 Nikos Skalkotos
                d.msgbox("The file `%s' you choose does not exist." % media,
164 023e1217 Nikos Skalkotos
                         width=SMALL_WIDTH)
165 789a3763 Nikos Skalkotos
            else:
166 02f71654 Nikos Skalkotos
                mode = os.stat(media).st_mode
167 02f71654 Nikos Skalkotos
                if not stat.S_ISDIR(mode):
168 02f71654 Nikos Skalkotos
                    break
169 3664c5f5 Nikos Skalkotos
                default = media
170 789a3763 Nikos Skalkotos
171 3664c5f5 Nikos Skalkotos
        (code, media) = d.fselect(default, 10, 60, extra_button=1,
172 6228d45e Nikos Skalkotos
                                  title="Please select an input media.",
173 02f71654 Nikos Skalkotos
                                  extra_label="Bundle Host")
174 789a3763 Nikos Skalkotos
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
175 789a3763 Nikos Skalkotos
            if confirm_exit(d, "You canceled the media selection dialog box."):
176 789a3763 Nikos Skalkotos
                sys.exit(0)
177 789a3763 Nikos Skalkotos
            else:
178 789a3763 Nikos Skalkotos
                media = None
179 789a3763 Nikos Skalkotos
                continue
180 b686bde9 Nikos Skalkotos
        elif code == d.DIALOG_EXTRA:
181 02f71654 Nikos Skalkotos
            return '/'
182 789a3763 Nikos Skalkotos
183 789a3763 Nikos Skalkotos
    return media
184 789a3763 Nikos Skalkotos
185 789a3763 Nikos Skalkotos
186 a26ae008 Nikos Skalkotos
def _dialog_form(self, text, height=20, width=60, form_height=15, fields=[],
187 a26ae008 Nikos Skalkotos
                 **kwargs):
188 a26ae008 Nikos Skalkotos
    """Display a form box.
189 a26ae008 Nikos Skalkotos

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