1 # -*- coding: utf-8 -*-
3 # Copyright 2012 GRNET S.A. All rights reserved.
5 # Redistribution and use in source and binary forms, with or
6 # without modification, are permitted provided that the following
9 # 1. Redistributions of source code must retain the above
10 # copyright notice, this list of conditions and the following
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.
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.
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.
36 """Module providing useful functions for the dialog-based version of
43 from image_creator.output.dialog import GaugeOutput
44 from image_creator.util import MD5
45 from image_creator.kamaki_wrapper import Kamaki
51 def update_background_title(session):
52 """Update the backgroud title of the dialog page"""
54 disk = session['disk']
55 image = session['image']
59 size = (image.size + MB - 1) // MB
60 shrinked = 'shrinked' in session and session['shrinked']
61 postfix = " (shrinked)" if shrinked else ''
63 title = "OS: %s, Distro: %s, Size: %dMB%s, Source: %s" % \
64 (image.ostype, image.distro, size, postfix,
65 os.path.abspath(disk.source))
67 d.setBackgroundTitle(title)
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)
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)
81 class Reset(Exception):
82 """Exception used to reset the program"""
86 def extract_metadata_string(session):
87 """Convert image metadata to text"""
89 metadata.update(session['metadata'])
90 if 'task_metadata' in session:
91 for key in session['task_metadata']:
94 return unicode(json.dumps({'properties': metadata,
95 'disk-format': 'diskdump'}, ensure_ascii=False))
98 def extract_image(session):
99 """Dump the image to a local file"""
100 d = session['dialog']
103 if dir and dir[-1] != os.sep:
106 (code, path) = d.fselect(dir, 10, 50, title="Save image as...")
107 if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
110 if os.path.isdir(path):
114 if os.path.isdir("%s.meta" % path):
115 d.msgbox("Can't overwrite directory `%s.meta'" % path,
119 if os.path.isdir("%s.md5sum" % path):
120 d.msgbox("Can't overwrite directory `%s.md5sum'" % path,
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,
135 files = ["%s%s" % (path, ext) for ext in ('', '.meta', '.md5sum')]
136 overwrite = filter(os.path.exists, files)
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):
144 gauge = GaugeOutput(d, "Image Extraction", "Extracting image...")
146 image = session['image']
150 if "checksum" not in session:
152 session['checksum'] = md5.compute(image.device, image.size)
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))
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:
173 d.msgbox("Image file `%s' was successfully extracted!" % path,
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_]+$')
185 if not re.match(regexp, name):
186 d.msgbox("Allowed characters for name: [a-zA-Z0-9_]", width=WIDTH)
190 d.msgbox("Url cannot be empty!", width=WIDTH)
194 d.msgbox("Token cannot be empty!", width=WIDTH)
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)
205 def add_cloud(session):
206 """Add a new cloud account"""
208 d = session['dialog']
218 ("Description (optional): ", description, 80),
219 ("Authentication URL: ", url, 200),
220 ("Token:", token, 100)]
222 (code, output) = d.form("Add a new cloud account:", height=13,
223 width=WIDTH, form_height=4, fields=fields)
225 if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
228 name, description, url, token = output
231 description = description.strip()
233 token = token.strip()
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)
241 Kamaki.save_cloud(name, url, token, description)
249 def edit_cloud(session, name):
250 """Edit a cloud account"""
252 info = Kamaki.get_cloud_by_name(name)
254 assert info, "Cloud: `%s' does not exist" % name
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 ""
260 d = session['dialog']
264 ("Description (optional): ", description, 80),
265 ("Authentication URL: ", url, 200),
266 ("Token:", token, 100)]
268 (code, output) = d.form("Edit cloud account: `%s'" % name, height=13,
269 width=WIDTH, form_height=3, fields=fields)
271 if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
274 description, url, token = output
276 description = description.strip()
278 token = token.strip()
280 if _check_cloud(session, name, description, url, token):
281 Kamaki.save_cloud(name, url, token, description)
288 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :