Statistics
| Branch: | Tag: | Revision:

root / image_creator / dialog_main.py @ 41bf4a8f

History | View | Annotate | Download (8.2 kB)

1
#!/usr/bin/env python
2

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

    
36
import dialog
37
import sys
38
import os
39
import stat
40
import textwrap
41
import signal
42
import optparse
43

    
44
from image_creator import __version__ as version
45
from image_creator.util import FatalError
46
from image_creator.output import Output
47
from image_creator.output.cli import SimpleOutput
48
from image_creator.output.dialog import GaugeOutput
49
from image_creator.output.composite import CompositeOutput
50
from image_creator.disk import Disk
51
from image_creator.os_type import os_cls
52
from image_creator.dialog_wizard import wizard
53
from image_creator.dialog_menu import main_menu
54
from image_creator.dialog_util import SMALL_WIDTH, WIDTH, confirm_exit, \
55
    Reset, update_background_title
56

    
57

    
58
def image_creator(d, media, out, tmp):
59

    
60
    d.setBackgroundTitle('snf-image-creator')
61

    
62
    gauge = GaugeOutput(d, "Initialization", "Initializing...")
63
    out.add(gauge)
64
    disk = Disk(media, out, tmp)
65

    
66
    def signal_handler(signum, frame):
67
        gauge.cleanup()
68
        disk.cleanup()
69

    
70
    signal.signal(signal.SIGINT, signal_handler)
71
    signal.signal(signal.SIGTERM, signal_handler)
72
    try:
73
        snapshot = disk.snapshot()
74
        dev = disk.get_device(snapshot)
75

    
76
        metadata = {}
77
        for (key, value) in dev.meta.items():
78
            metadata[str(key)] = str(value)
79

    
80
        dev.mount(readonly=True)
81
        out.output("Collecting image metadata...")
82
        cls = os_cls(dev.distro, dev.ostype)
83
        image_os = cls(dev.root, dev.g, out)
84
        dev.umount()
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
                   "snapshot": snapshot,
101
                   "device": dev,
102
                   "image_os": image_os,
103
                   "metadata": metadata}
104

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

    
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 main():
168

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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