Statistics
| Branch: | Tag: | Revision:

root / image_creator / dialog_util.py @ 5e18a927

History | View | Annotate | Download (8.8 kB)

1
# -*- coding: utf-8 -*-
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
"""Module providing useful functions for the dialog-based version of
37
snf-image-creator.
38
"""
39

    
40
import os
41
import re
42
import json
43
from image_creator.output.dialog import GaugeOutput
44
from image_creator.util import MD5
45
from image_creator.kamaki_wrapper import Kamaki
46

    
47
SMALL_WIDTH = 60
48
WIDTH = 70
49

    
50

    
51
def update_background_title(session):
52
    """Update the backgroud title of the dialog page"""
53
    d = session['dialog']
54
    disk = session['disk']
55
    image = session['image']
56

    
57
    MB = 2 ** 20
58

    
59
    size = (image.size + MB - 1) // MB
60
    shrinked = 'shrinked' in session and session['shrinked']
61
    postfix = " (shrinked)" if shrinked else ''
62

    
63
    title = "OS: %s, Distro: %s, Size: %dMB%s, Source: %s" % \
64
            (image.ostype, image.distro, size, postfix,
65
             os.path.abspath(disk.source))
66

    
67
    d.setBackgroundTitle(title)
68

    
69

    
70
def confirm_exit(d, msg=''):
71
    """Ask the user to confirm when exiting the program"""
72
    return not d.yesno("%s Do you want to exit?" % msg, width=SMALL_WIDTH)
73

    
74

    
75
def confirm_reset(d):
76
    """Ask the user to confirm a reset action"""
77
    return not d.yesno("Are you sure you want to reset everything?",
78
                       width=SMALL_WIDTH, defaultno=1)
79

    
80

    
81
class Reset(Exception):
82
    """Exception used to reset the program"""
83
    pass
84

    
85

    
86
def extract_metadata_string(session):
87
    """Convert image metadata to text"""
88
    metadata = {}
89
    metadata.update(session['metadata'])
90
    if 'task_metadata' in session:
91
        for key in session['task_metadata']:
92
            metadata[key] = 'yes'
93

    
94
    return unicode(json.dumps({'properties': metadata,
95
                               'disk-format': 'diskdump'}, ensure_ascii=False))
96

    
97

    
98
def extract_image(session):
99
    """Dump the image to a local file"""
100
    d = session['dialog']
101
    dir = os.getcwd()
102
    while 1:
103
        if dir and dir[-1] != os.sep:
104
            dir = dir + os.sep
105

    
106
        (code, path) = d.fselect(dir, 10, 50, title="Save image as...")
107
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
108
            return False
109

    
110
        if os.path.isdir(path):
111
            dir = path
112
            continue
113

    
114
        if os.path.isdir("%s.meta" % path):
115
            d.msgbox("Can't overwrite directory `%s.meta'" % path,
116
                     width=SMALL_WIDTH)
117
            continue
118

    
119
        if os.path.isdir("%s.md5sum" % path):
120
            d.msgbox("Can't overwrite directory `%s.md5sum'" % path,
121
                     width=SMALL_WIDTH)
122
            continue
123

    
124
        basedir = os.path.dirname(path)
125
        name = os.path.basename(path)
126
        if not os.path.exists(basedir):
127
            d.msgbox("Directory `%s' does not exist" % basedir,
128
                     width=SMALL_WIDTH)
129
            continue
130

    
131
        dir = basedir
132
        if len(name) == 0:
133
            continue
134

    
135
        files = ["%s%s" % (path, ext) for ext in ('', '.meta', '.md5sum')]
136
        overwrite = filter(os.path.exists, files)
137

    
138
        if len(overwrite) > 0:
139
            if d.yesno("The following file(s) exist:\n"
140
                       "%s\nDo you want to overwrite them?" %
141
                       "\n".join(overwrite), width=SMALL_WIDTH):
142
                continue
143

    
144
        gauge = GaugeOutput(d, "Image Extraction", "Extracting image...")
145
        try:
146
            image = session['image']
147
            out = image.out
148
            out.add(gauge)
149
            try:
150
                if "checksum" not in session:
151
                    md5 = MD5(out)
152
                    session['checksum'] = md5.compute(image.device, image.size)
153

    
154
                # Extract image file
155
                image.dump(path)
156

    
157
                # Extract metadata file
158
                out.output("Extracting metadata file ...")
159
                with open('%s.meta' % path, 'w') as f:
160
                    f.write(extract_metadata_string(session))
161
                out.success('done')
162

    
163
                # Extract md5sum file
164
                out.output("Extracting md5sum file ...")
165
                md5str = "%s %s\n" % (session['checksum'], name)
166
                with open('%s.md5sum' % path, 'w') as f:
167
                    f.write(md5str)
168
                out.success("done")
169
            finally:
170
                out.remove(gauge)
171
        finally:
172
            gauge.cleanup()
173
        d.msgbox("Image file `%s' was successfully extracted!" % path,
174
                 width=SMALL_WIDTH)
175
        break
176

    
177
    return True
178

    
179

    
180
def _check_cloud(session, name, description, url, token):
181
    """Checks if the provided info for a cloud are valid"""
182
    d = session['dialog']
183
    regexp = re.compile('^[a-zA-Z0-9_]+$')
184

    
185
    if not re.match(regexp, name):
186
        d.msgbox("Allowed characters for name: [a-zA-Z0-9_]", width=WIDTH)
187
        return False
188

    
189
    if len(url) == 0:
190
        d.msgbox("Url cannot be empty!", width=WIDTH)
191
        return False
192

    
193
    if len(token) == 0:
194
        d.msgbox("Token cannot be empty!", width=WIDTH)
195
        return False
196

    
197
    if Kamaki.create_account(url, token) is None:
198
        d.msgbox("The cloud info you provided is not valid. Please check the "
199
                 "Authentication URL and the token values again!", width=WIDTH)
200
        return False
201

    
202
    return True
203

    
204

    
205
def add_cloud(session):
206
    """Add a new cloud account"""
207

    
208
    d = session['dialog']
209

    
210
    name = ""
211
    description = ""
212
    url = ""
213
    token = ""
214

    
215
    while 1:
216
        fields = [
217
            ("Name:", name, 60),
218
            ("Description (optional): ", description, 80),
219
            ("Authentication URL: ", url, 200),
220
            ("Token:", token, 100)]
221

    
222
        (code, output) = d.form("Add a new cloud account:", height=13,
223
                                width=WIDTH, form_height=4, fields=fields)
224

    
225
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
226
            return False
227

    
228
        name, description, url, token = output
229

    
230
        name = name.strip()
231
        description = description.strip()
232
        url = url.strip()
233
        token = token.strip()
234

    
235
        if _check_cloud(session, name, description, url, token):
236
            if name in Kamaki.get_clouds().keys():
237
                d.msgbox("A cloud with name `%s' already exists. If you want "
238
                         "to edit the existing cloud account, use the edit "
239
                         "menu." % name, width=WIDTH)
240
            else:
241
                Kamaki.save_cloud(name, url, token, description)
242
                break
243

    
244
        continue
245

    
246
    return True
247

    
248

    
249
def edit_cloud(session, name):
250
    """Edit a cloud account"""
251

    
252
    info = Kamaki.get_cloud_by_name(name)
253

    
254
    assert info, "Cloud: `%s' does not exist" % name
255

    
256
    description = info['description'] if 'description' in info else ""
257
    url = info['url'] if 'url' in info else ""
258
    token = info['token'] if 'token' in info else ""
259

    
260
    d = session['dialog']
261

    
262
    while 1:
263
        fields = [
264
            ("Description (optional): ", description, 80),
265
            ("Authentication URL: ", url, 200),
266
            ("Token:", token, 100)]
267

    
268
        (code, output) = d.form("Edit cloud account: `%s'" % name, height=13,
269
                                width=WIDTH, form_height=3, fields=fields)
270

    
271
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
272
            return False
273

    
274
        description, url, token = output
275

    
276
        description = description.strip()
277
        url = url.strip()
278
        token = token.strip()
279

    
280
        if _check_cloud(session, name, description, url, token):
281
            Kamaki.save_cloud(name, url, token, description)
282
            break
283

    
284
        continue
285

    
286
    return True
287

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