Statistics
| Branch: | Tag: | Revision:

root / image_creator / dialog_main.py @ 12c97404

History | View | Annotate | Download (9.3 kB)

1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
#
4
# Copyright 2012 GRNET S.A. All rights reserved.
5
#
6
# Redistribution and use in source and binary forms, with or
7
# without modification, are permitted provided that the following
8
# conditions are met:
9
#
10
#   1. Redistributions of source code must retain the above
11
#      copyright notice, this list of conditions and the following
12
#      disclaimer.
13
#
14
#   2. Redistributions in binary form must reproduce the above
15
#      copyright notice, this list of conditions and the following
16
#      disclaimer in the documentation and/or other materials
17
#      provided with the distribution.
18
#
19
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
20
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
23
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
# POSSIBILITY OF SUCH DAMAGE.
31
#
32
# The views and conclusions contained in the software and
33
# documentation are those of the authors and should not be
34
# interpreted as representing official policies, either expressed
35
# or implied, of GRNET S.A.
36

    
37
"""This module is the entrance point for the dialog-based version of the
38
snf-image-creator program. The main function will create a dialog where the
39
user is asked if he wants to use the program in expert or wizard mode.
40
"""
41

    
42
import dialog
43
import sys
44
import os
45
import stat
46
import textwrap
47
import signal
48
import optparse
49
import types
50

    
51
from image_creator import __version__ as version
52
from image_creator.util import FatalError
53
from image_creator.output import Output
54
from image_creator.output.cli import SimpleOutput
55
from image_creator.output.dialog import GaugeOutput
56
from image_creator.output.composite import CompositeOutput
57
from image_creator.disk import Disk
58
from image_creator.dialog_wizard import start_wizard
59
from image_creator.dialog_menu import main_menu
60
from image_creator.dialog_util import SMALL_WIDTH, WIDTH, confirm_exit, \
61
    Reset, update_background_title
62

    
63

    
64
def create_image(d, media, out, tmp):
65
    """Create an image out of `media'"""
66
    d.setBackgroundTitle('snf-image-creator')
67

    
68
    gauge = GaugeOutput(d, "Initialization", "Initializing...")
69
    out.add(gauge)
70
    disk = Disk(media, out, tmp)
71

    
72
    def signal_handler(signum, frame):
73
        gauge.cleanup()
74
        disk.cleanup()
75

    
76
    signal.signal(signal.SIGINT, signal_handler)
77
    signal.signal(signal.SIGTERM, signal_handler)
78
    try:
79
        snapshot = disk.snapshot()
80
        image = disk.get_image(snapshot)
81

    
82
        out.output("Collecting image metadata ...")
83
        metadata = {}
84
        for (key, value) in image.meta.items():
85
            metadata[str(key)] = str(value)
86

    
87
        for (key, value) in image.os.meta.items():
88
            metadata[str(key)] = str(value)
89

    
90
        out.success("done")
91
        gauge.cleanup()
92
        out.remove(gauge)
93

    
94
        # Make sure the signal handler does not call gauge.cleanup again
95
        def dummy(self):
96
            pass
97
        gauge.cleanup = type(GaugeOutput.cleanup)(dummy, gauge, GaugeOutput)
98

    
99
        session = {"dialog": d,
100
                   "disk": disk,
101
                   "image": image,
102
                   "metadata": metadata}
103

    
104
        msg = "snf-image-creator detected a %s system on the input media. " \
105
              "Would you like to run a wizard to assist you through the " \
106
              "image creation process?\n\nChoose <Wizard> to run the wizard," \
107
              " <Expert> to run the snf-image-creator in expert mode or " \
108
              "press ESC to quit the program." \
109
              % (image.ostype if image.ostype == image.distro or
110
                 image.distro == "unknown" else "%s (%s)" %
111
                 (image.ostype, image.distro))
112

    
113
        update_background_title(session)
114

    
115
        while True:
116
            code = d.yesno(msg, width=WIDTH, height=12, yes_label="Wizard",
117
                           no_label="Expert")
118
            if code == d.DIALOG_OK:
119
                if start_wizard(session):
120
                    break
121
            elif code == d.DIALOG_CANCEL:
122
                main_menu(session)
123
                break
124

    
125
            if confirm_exit(d):
126
                break
127

    
128
        d.infobox("Thank you for using snf-image-creator. Bye", width=53)
129
    finally:
130
        disk.cleanup()
131

    
132
    return 0
133

    
134

    
135
def select_file(d, media):
136
    """Select a media file"""
137
    if media == '/':
138
        return '/'
139

    
140
    default = os.getcwd() + os.sep
141
    while 1:
142
        if media is not None:
143
            if not os.path.exists(media):
144
                d.msgbox("The file `%s' you choose does not exist." % media,
145
                         width=SMALL_WIDTH)
146
            else:
147
                mode = os.stat(media).st_mode
148
                if not stat.S_ISDIR(mode):
149
                    break
150
                default = media
151

    
152
        (code, media) = d.fselect(default, 10, 60, extra_button=1,
153
                                  title="Please select an input media.",
154
                                  extra_label="Bundle Host")
155
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
156
            if confirm_exit(d, "You canceled the media selection dialog box."):
157
                sys.exit(0)
158
            else:
159
                media = None
160
                continue
161
        elif code == d.DIALOG_EXTRA:
162
            return '/'
163

    
164
    return media
165

    
166

    
167
def _dialog_form(self, text, height=20, width=60, form_height=15, fields=[],
168
                 **kwargs):
169
    """Display a form box.
170

171
    fields is in the form: [(label1, item1, item_length1), ...]
172
    """
173

    
174
    cmd = ["--form", text, str(height), str(width), str(form_height)]
175

    
176
    label_len = 0
177
    for field in fields:
178
        if len(field[0]) > label_len:
179
            label_len = len(field[0])
180

    
181
    input_len = width - label_len - 1
182

    
183
    line = 1
184
    for field in fields:
185
        label = field[0]
186
        item = field[1]
187
        item_len = field[2]
188
        cmd.extend((label, str(line), str(1), item, str(line),
189
                   str(label_len + 1), str(input_len), str(item_len)))
190
        line += 1
191

    
192
    code, output = self._perform(*(cmd,), **kwargs)
193

    
194
    if not output:
195
        return (code, [])
196

    
197
    return (code, output.splitlines())
198

    
199

    
200
def main():
201

    
202
    d = dialog.Dialog(dialog="dialog")
203

    
204
    # Add extra button in dialog library
205
    dialog._common_args_syntax["extra_button"] = \
206
        lambda enable: dialog._simple_option("--extra-button", enable)
207

    
208
    dialog._common_args_syntax["extra_label"] = \
209
        lambda string: ("--extra-label", string)
210

    
211
    # Allow yes-no label overwriting
212
    dialog._common_args_syntax["yes_label"] = \
213
        lambda string: ("--yes-label", string)
214

    
215
    dialog._common_args_syntax["no_label"] = \
216
        lambda string: ("--no-label", string)
217

    
218
    # Monkey-patch pythondialog to include support for form dialog boxes
219
    if not hasattr(dialog, 'form'):
220
        d.form = types.MethodType(_dialog_form, d)
221

    
222
    usage = "Usage: %prog [options] [<input_media>]"
223
    parser = optparse.OptionParser(version=version, usage=usage)
224
    parser.add_option("-l", "--logfile", type="string", dest="logfile",
225
                      default=None, help="log all messages to FILE",
226
                      metavar="FILE")
227
    parser.add_option("--tmpdir", type="string", dest="tmp", default=None,
228
                      help="create large temporary image files under DIR",
229
                      metavar="DIR")
230

    
231
    options, args = parser.parse_args(sys.argv[1:])
232

    
233
    if len(args) > 1:
234
        parser.error("Wrong number of arguments")
235

    
236
    d.setBackgroundTitle('snf-image-creator')
237

    
238
    try:
239
        if os.geteuid() != 0:
240
            raise FatalError("You must run %s as root" %
241
                             parser.get_prog_name())
242

    
243
        if options.tmp is not None and not os.path.isdir(options.tmp):
244
            raise FatalError("The directory `%s' specified with --tmpdir is "
245
                             "not valid" % options.tmp)
246

    
247
        logfile = None
248
        if options.logfile is not None:
249
            try:
250
                logfile = open(options.logfile, 'w')
251
            except IOError as e:
252
                raise FatalError(
253
                    "Unable to open logfile `%s' for writing. Reason: %s" %
254
                    (options.logfile, e.strerror))
255

    
256
        media = select_file(d, args[0] if len(args) == 1 else None)
257

    
258
        try:
259
            log = SimpleOutput(False, logfile) if logfile is not None \
260
                else Output()
261
            while 1:
262
                try:
263
                    out = CompositeOutput([log])
264
                    out.output("Starting %s v%s ..." %
265
                               (parser.get_prog_name(), version))
266
                    ret = create_image(d, media, out, options.tmp)
267
                    sys.exit(ret)
268
                except Reset:
269
                    log.output("Resetting everything ...")
270
                    continue
271
        finally:
272
            if logfile is not None:
273
                logfile.close()
274
    except FatalError as e:
275
        msg = textwrap.fill(str(e), width=WIDTH)
276
        d.infobox(msg, width=WIDTH, title="Fatal Error")
277
        sys.exit(1)
278

    
279
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :