Revision 672cabb9

b/image_creator/dialog_main.py
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 textwrap
40
import signal
41

  
42
from image_creator import __version__ as version
43
from image_creator.util import FatalError, MD5
44
from image_creator.output.dialog import InitializationOutput
45
from image_creator.disk import Disk
46
from image_creator.os_type import os_cls
47

  
48
MSGBOX_WIDTH = 60
49
YESNO_WIDTH = 50
50
MENU_WIDTH = 70
51

  
52

  
53
class Reset(Exception):
54
    pass
55

  
56

  
57
def confirm_exit(d, msg=''):
58
    return not d.yesno("%s Do you want to exit?" % msg, width=YESNO_WIDTH)
59

  
60

  
61
def confirm_reset(d):
62
    return not d.yesno(
63
        "Are you sure you want to reset everything?",
64
        width=YESNO_WIDTH)
65

  
66

  
67
def upload_image(session):
68
    d = session["dialog"]
69

  
70
    if "account" not in session:
71
        d.msgbox("You need to provide your ~okeanos login username before you "
72
                 "can upload images to pithos+", width=MSGBOX_WIDTH)
73
        return False
74

  
75
    if "token" not in session:
76
        d.msgbox("You need to provide your ~okeanos account authentication "
77
                 "token before you can upload images to pithos+",
78
                 width=MSGBOX_WIDTH)
79
        return False
80

  
81
    while 1:
82
        (code, answer) = d.inputbox("Please provide a filename:",
83
                        init=session["upload"] if "upload" in session else '')
84
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
85
            return False
86

  
87
        answer = answer.strip()
88
        if len(answer) == 0:
89
            d.msgbox("Filename cannot be empty", width=MSGBOX_WIDTH)
90
            continue
91

  
92
        session["upload"] = answer
93
        return True
94

  
95

  
96
def register_image(session):
97
    d = session["dialog"]
98

  
99
    if "account" not in session:
100
        d.msgbox("You need to provide your ~okeanos login username before you "
101
                 "can register an images to cyclades",
102
                 width=MSGBOX_WIDTH)
103
        return False
104

  
105
    if "token" not in session:
106
        d.msgbox("You need to provide your ~okeanos account authentication "
107
                 "token before you can register an images to cyclades",
108
                 width=MSGBOX_WIDTH)
109
        return False
110

  
111
    if "upload" not in session:
112
        d.msgbox("You need to have an image uploaded to pithos+ before you "
113
                 "can register it to cyclades",
114
                 width=MSGBOX_WIDTH)
115
        return False
116

  
117
    return True
118

  
119

  
120
def kamaki_menu(session):
121
    d = session['dialog']
122
    default_item = "Account"
123
    while 1:
124
        account = session["account"] if "account" in session else "<none>"
125
        token = session["token"] if "token" in session else "<none>"
126
        upload = session["upload"] if "upload" in session else "<none>"
127
        (code, choice) = d.menu(
128
            "Choose one of the following or press <Back> to go back.",
129
            width=MENU_WIDTH,
130
            choices=[("Account", "Change your ~okeanos username: %s" %
131
                      account),
132
                     ("Token", "Change your ~okeanos token: %s" %
133
                      token),
134
                     ("Upload", "Upload image to pithos+"),
135
                     ("Register", "Register image to cyclades: %s" % upload)],
136
            cancel="Back",
137
            default_item=default_item,
138
            title="Image Registration Menu")
139

  
140
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
141
            return False
142

  
143
        if choice == "Account":
144
            default_item = "Account"
145
            (code, answer) = d.inputbox(
146
                "Please provide your ~okeanos account e-mail address:",
147
                init=session["account"] if "account" in session else '',
148
                width=70)
149
            if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
150
                continue
151
            if len(answer) == 0 and "account" in session:
152
                    del session["account"]
153
            else:
154
                session["account"] = answer.strip()
155
                default_item = "Token"
156
        elif choice == "Token":
157
            default_item = "Token"
158
            (code, answer) = d.inputbox(
159
                "Please provide your ~okeanos account authetication token:",
160
                init=session["token"] if "token" in session else '',
161
                width=70)
162
            if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
163
                continue
164
            if len(answer) == 0 and "token" in session:
165
                del session["token"]
166
            else:
167
                session["token"] = answer.strip()
168
                default_item = "Upload"
169
        elif choice == "Upload":
170
            if upload_image(session):
171
                default_item = "Register"
172
            else:
173
                default_item = "Upload"
174
        elif choice == "Register":
175
            if register_image(session):
176
                return True
177
            else:
178
                default_item = "Register"
179

  
180

  
181
def main_menu(session):
182
    d = session['dialog']
183
    dev = session['device']
184
    d.setBackgroundTitle("OS: %s, Distro: %s" % (dev.ostype, dev.distro))
185
    actions = {"Register": kamaki_menu}
186
    default_item = "Customize"
187

  
188
    while 1:
189
        (code, choice) = d.menu(
190
            "Choose one of the following or press <Exit> to exit.",
191
            width=MENU_WIDTH,
192
            choices=[("Customize", "Run various image customization tasks"),
193
                     ("Deploy", "Configure ~okeanos image deployment options"),
194
                     ("Register", "Register image to ~okeanos"),
195
                     ("Extract", "Dump image to local file system"),
196
                     ("Reset", "Reset everything and start over again"),
197
                     ("Help", "Get help for using snf-image-creator")],
198
            cancel="Exit",
199
            default_item=default_item,
200
            title="Image Creator for ~okeanos (snf-image-creator version %s)" %
201
                  version)
202

  
203
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
204
            if confirm_exit(d):
205
                break
206
            else:
207
                continue
208

  
209
        if choice == "Reset":
210
            if confirm_reset(d):
211
                d.infobox("Resetting snf-image-creator. Please wait...")
212
                raise Reset
213
            else:
214
                continue
215
        elif choice in actions:
216
            actions[choice](session)
217

  
218

  
219
def select_file(d):
220
    root = os.sep
221
    while 1:
222
        (code, path) = d.fselect(root, 10, 50,
223
                                 title="Please select input media")
224
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
225
            if confirm_exit(d, "You canceled the media selection dialog box."):
226
                sys.exit(0)
227
            else:
228
                continue
229

  
230
        if not os.path.exists(path):
231
            d.msgbox("The file you choose does not exist", width=MSGBOX_WIDTH)
232
            continue
233
        else:
234
            break
235

  
236
    return path
237

  
238

  
239
def collect_metadata(dev, out):
240

  
241
    dev.mount(readonly=True)
242
    out.output("Collecting image metadata...")
243
    cls = os_cls(dev.distro, dev.ostype)
244
    image_os = cls(dev.root, dev.g, out)
245
    out.success("done")
246
    dev.umount()
247

  
248
    return image_os.meta
249

  
250

  
251
def image_creator(d):
252
    basename = os.path.basename(sys.argv[0])
253
    usage = "Usage: %s [input_media]" % basename
254
    if len(sys.argv) > 2:
255
        sys.stderr.write("%s\n" % usage)
256
        return 1
257

  
258
    if os.geteuid() != 0:
259
        raise FatalError("You must run %s as root" % basename)
260

  
261
    media = sys.argv[1] if len(sys.argv) == 2 else select_file(d)
262

  
263
    out = InitializationOutput(d)
264
    disk = Disk(media, out)
265

  
266
    def signal_handler(signum, fram):
267
        out.cleanup()
268
        disk.cleanup()
269

  
270
    signal.signal(signal.SIGINT, signal_handler)
271
    try:
272
        snapshot = disk.snapshot()
273
        dev = disk.get_device(snapshot)
274

  
275
        metadata = collect_metadata(dev, out)
276
        out.cleanup()
277

  
278
        # Make sure the signal handler does not call out.cleanup again
279
        def dummy(self):
280
            pass
281
        instancemethod = type(InitializationOutput.cleanup)
282
        out.cleanup = instancemethod(dummy, out, InitializationOutput)
283

  
284
        session = {"dialog": d,
285
                   "disk": disk,
286
                   "device": dev,
287
                   "metadata": metadata}
288

  
289
        main_menu(session)
290
        d.infobox("Thank you for using snf-image-creator. Bye", width=53)
291
    finally:
292
        disk.cleanup()
293

  
294
    return 0
295

  
296

  
297
def main():
298

  
299
    d = dialog.Dialog(dialog="dialog")
300

  
301
    while 1:
302
        try:
303
            try:
304
                ret = image_creator(d)
305
                sys.exit(ret)
306
            except FatalError as e:
307
                msg = textwrap.fill(str(e), width=70)
308
                d.infobox(msg, width=70, title="Fatal Error")
309
                sys.exit(1)
310
        except Reset:
311
            continue
312

  
313
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
b/setup.py
45 45
#    long_description=open('README.rst').read(),
46 46
    url='https://code.grnet.gr/projects/snf-image-creator',
47 47
    license='BSD',
48
    packages=['image_creator', 'image_creator.os_type'],
48
    packages=['image_creator', 'image_creator.os_type',
49
              'image_creator.output'],
49 50
    include_package_data=True,
50 51
    install_requires=['pbs', 'ansicolors', 'progress', 'pysendfile'],
51 52
    entry_points={
52
        'console_scripts': ['snf-image-creator = image_creator.main:main']
53
        'console_scripts': [
54
                'snf-image-creator = image_creator.main:main',
55
                'snf-image-creator-dialog = image_creator.dialog_main:main']
53 56
    }
54 57
)
58
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :

Also available in: Unified diff