Statistics
| Branch: | Tag: | Revision:

root / image_creator / dialog_main.py @ 121f3bc0

History | View | Annotate | Download (8.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

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

    
62

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

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

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

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

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

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

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

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

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

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

    
112
        update_background_title(session)
113

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

    
124
            if confirm_exit(d):
125
                break
126

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

    
131
    return 0
132

    
133

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

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

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

    
163
    return media
164

    
165

    
166
def main():
167

    
168
    d = dialog.Dialog(dialog="dialog")
169

    
170
    # Add extra button in dialog library
171
    dialog._common_args_syntax["extra_button"] = \
172
        lambda enable: dialog._simple_option("--extra-button", enable)
173

    
174
    dialog._common_args_syntax["extra_label"] = \
175
        lambda string: ("--extra-label", string)
176

    
177
    # Allow yes-no label overwriting
178
    dialog._common_args_syntax["yes_label"] = \
179
        lambda string: ("--yes-label", string)
180

    
181
    dialog._common_args_syntax["no_label"] = \
182
        lambda string: ("--no-label", string)
183

    
184
    usage = "Usage: %prog [options] [<input_media>]"
185
    parser = optparse.OptionParser(version=version, usage=usage)
186
    parser.add_option("-l", "--logfile", type="string", dest="logfile",
187
                      default=None, help="log all messages to FILE",
188
                      metavar="FILE")
189
    parser.add_option("--tmpdir", type="string", dest="tmp", default=None,
190
                      help="create large temporary image files under DIR",
191
                      metavar="DIR")
192

    
193
    options, args = parser.parse_args(sys.argv[1:])
194

    
195
    if len(args) > 1:
196
        parser.error("Wrong number of arguments")
197

    
198
    d.setBackgroundTitle('snf-image-creator')
199

    
200
    try:
201
        if os.geteuid() != 0:
202
            raise FatalError("You must run %s as root" %
203
                             parser.get_prog_name())
204

    
205
        if options.tmp is not None and not os.path.isdir(options.tmp):
206
            raise FatalError("The directory `%s' specified with --tmpdir is "
207
                             "not valid" % options.tmp)
208

    
209
        logfile = None
210
        if options.logfile is not None:
211
            try:
212
                logfile = open(options.logfile, 'w')
213
            except IOError as e:
214
                raise FatalError(
215
                    "Unable to open logfile `%s' for writing. Reason: %s" %
216
                    (options.logfile, e.strerror))
217

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

    
220
        try:
221
            log = SimpleOutput(False, logfile) if logfile is not None \
222
                else Output()
223
            while 1:
224
                try:
225
                    out = CompositeOutput([log])
226
                    out.output("Starting %s v%s ..." %
227
                               (parser.get_prog_name(), version))
228
                    ret = create_image(d, media, out, options.tmp)
229
                    sys.exit(ret)
230
                except Reset:
231
                    log.output("Resetting everything ...")
232
                    continue
233
        finally:
234
            if logfile is not None:
235
                logfile.close()
236
    except FatalError as e:
237
        msg = textwrap.fill(str(e), width=WIDTH)
238
        d.infobox(msg, width=WIDTH, title="Fatal Error")
239
        sys.exit(1)
240

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