-#!/usr/bin/env python
-
+# -*- coding: utf-8 -*-
+#
# Copyright 2012 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
+"""Module providing useful functions for the dialog-based version of
+snf-image-creator.
+"""
+
import os
+import re
+import json
+from image_creator.output.dialog import GaugeOutput
+from image_creator.util import MD5
+from image_creator.kamaki_wrapper import Kamaki
SMALL_WIDTH = 60
WIDTH = 70
def update_background_title(session):
+ """Update the backgroud title of the dialog page"""
d = session['dialog']
- dev = session['device']
+ disk = session['disk']
+ image = session['image']
MB = 2 ** 20
- size = (dev.size + MB - 1) // MB
+ size = (image.size + MB - 1) // MB
shrinked = 'shrinked' in session and session['shrinked']
postfix = " (shrinked)" if shrinked else ''
- title = "OS: %s, Distro: %s, Size: %dMB%s" % \
- (dev.ostype, dev.distro, size, postfix)
+ title = "OS: %s, Distro: %s, Size: %dMB%s, Source: %s" % \
+ (image.ostype, image.distro, size, postfix,
+ os.path.abspath(disk.source))
d.setBackgroundTitle(title)
def confirm_exit(d, msg=''):
+ """Ask the user to confirm when exiting the program"""
return not d.yesno("%s Do you want to exit?" % msg, width=SMALL_WIDTH)
def confirm_reset(d):
+ """Ask the user to confirm a reset action"""
return not d.yesno("Are you sure you want to reset everything?",
width=SMALL_WIDTH, defaultno=1)
class Reset(Exception):
+ """Exception used to reset the program"""
pass
+def extract_metadata_string(session):
+ """Convert image metadata to text"""
+ metadata = {}
+ metadata.update(session['metadata'])
+ if 'task_metadata' in session:
+ for key in session['task_metadata']:
+ metadata[key] = 'yes'
+
+ return unicode(json.dumps({'properties': metadata,
+ 'disk-format': 'diskdump'}, ensure_ascii=False))
+
+
def extract_image(session):
+ """Dump the image to a local file"""
d = session['dialog']
dir = os.getcwd()
while 1:
gauge = GaugeOutput(d, "Image Extraction", "Extracting image...")
try:
- dev = session['device']
- out = dev.out
+ image = session['image']
+ out = image.out
out.add(gauge)
try:
if "checksum" not in session:
- size = dev.size
md5 = MD5(out)
- session['checksum'] = md5.compute(session['snapshot'],
- size)
+ session['checksum'] = md5.compute(image.device, image.size)
# Extract image file
- dev.dump(path)
+ image.dump(path)
# Extract metadata file
- out.output("Extracting metadata file...")
+ out.output("Extracting metadata file ...")
with open('%s.meta' % path, 'w') as f:
f.write(extract_metadata_string(session))
out.success('done')
# Extract md5sum file
- out.output("Extracting md5sum file...")
+ out.output("Extracting md5sum file ...")
md5str = "%s %s\n" % (session['checksum'], name)
with open('%s.md5sum' % path, 'w') as f:
f.write(md5str)
return True
+
+def _check_cloud(session, name, description, url, token):
+ """Checks if the provided info for a cloud are valid"""
+ d = session['dialog']
+ regexp = re.compile('^[a-zA-Z0-9_]+$')
+
+ if not re.match(regexp, name):
+ d.msgbox("Allowed characters for name: [a-zA-Z0-9_]", width=WIDTH)
+ return False
+
+ if len(url) == 0:
+ d.msgbox("Url cannot be empty!", width=WIDTH)
+ return False
+
+ if len(token) == 0:
+ d.msgbox("Token cannot be empty!", width=WIDTH)
+ return False
+
+ if Kamaki.create_account(url, token) is None:
+ d.msgbox("The cloud info you provided is not valid. Please check the "
+ "Authentication URL and the token values again!", width=WIDTH)
+ return False
+
+ return True
+
+
+def add_cloud(session):
+ """Add a new cloud account"""
+
+ d = session['dialog']
+
+ name = ""
+ description = ""
+ url = ""
+ token = ""
+
+ while 1:
+ fields = [
+ ("Name:", name, 60),
+ ("Description (optional): ", description, 80),
+ ("Authentication URL: ", url, 200),
+ ("Token:", token, 100)]
+
+ (code, output) = d.form("Add a new cloud account:", height=13,
+ width=WIDTH, form_height=4, fields=fields)
+
+ if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
+ return False
+
+ name, description, url, token = output
+
+ name = name.strip()
+ description = description.strip()
+ url = url.strip()
+ token = token.strip()
+
+ if _check_cloud(session, name, description, url, token):
+ if name in Kamaki.get_clouds().keys():
+ d.msgbox("A cloud with name `%s' already exists. If you want "
+ "to edit the existing cloud account, use the edit "
+ "menu." % name, width=WIDTH)
+ else:
+ Kamaki.save_cloud(name, url, token, description)
+ break
+
+ continue
+
+ return True
+
+
+def edit_cloud(session, name):
+ """Edit a cloud account"""
+
+ info = Kamaki.get_cloud_by_name(name)
+
+ assert info, "Cloud: `%s' does not exist" % name
+
+ description = info['description'] if 'description' in info else ""
+ url = info['url'] if 'url' in info else ""
+ token = info['token'] if 'token' in info else ""
+
+ d = session['dialog']
+
+ while 1:
+ fields = [
+ ("Description (optional): ", description, 80),
+ ("Authentication URL: ", url, 200),
+ ("Token:", token, 100)]
+
+ (code, output) = d.form("Edit cloud account: `%s'" % name, height=13,
+ width=WIDTH, form_height=3, fields=fields)
+
+ if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
+ return False
+
+ description, url, token = output
+
+ description = description.strip()
+ url = url.strip()
+ token = token.strip()
+
+ if _check_cloud(session, name, description, url, token):
+ Kamaki.save_cloud(name, url, token, description)
+ break
+
+ continue
+
+ return True
+
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :