Skip to content
Snippets Groups Projects
dialog_util.py 8.91 KiB
Newer Older
Nikos Skalkotos's avatar
Nikos Skalkotos committed
# -*- coding: utf-8 -*-
#
# Copyright 2012 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
#   1. Redistributions of source code must retain the above
#      copyright notice, this list of conditions and the following
#      disclaimer.
#
#   2. Redistributions in binary form must reproduce the above
#      copyright notice, this list of conditions and the following
#      disclaimer in the documentation and/or other materials
#      provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.

Nikos Skalkotos's avatar
Nikos Skalkotos committed
"""Module providing useful functions for the dialog-based version of
snf-image-creator.
"""

import os
Nikos Skalkotos's avatar
Nikos Skalkotos committed
import re
from image_creator.output.dialog import GaugeOutput
from image_creator.util import MD5
Nikos Skalkotos's avatar
Nikos Skalkotos committed
from image_creator.kamaki_wrapper import Kamaki

SMALL_WIDTH = 60
WIDTH = 70


def update_background_title(session):
Nikos Skalkotos's avatar
Nikos Skalkotos committed
    """Update the backgroud title of the dialog page"""
    d = session['dialog']
    disk = session['disk']
    image = session['image']
    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, Source: %s" % \
            (image.ostype, image.distro, size, postfix,
             os.path.abspath(disk.source))

    d.setBackgroundTitle(title)


def confirm_exit(d, msg=''):
Nikos Skalkotos's avatar
Nikos Skalkotos committed
    """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):
Nikos Skalkotos's avatar
Nikos Skalkotos committed
    """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):
Nikos Skalkotos's avatar
Nikos Skalkotos committed
    """Exception used to reset the program"""
def extract_metadata_string(session):
Nikos Skalkotos's avatar
Nikos Skalkotos committed
    """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):
Nikos Skalkotos's avatar
Nikos Skalkotos committed
    """Dump the image to a local file"""
    d = session['dialog']
    dir = os.getcwd()
    while 1:
        if dir and dir[-1] != os.sep:
            dir = dir + os.sep

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

        if os.path.isdir(path):
            dir = path
            continue

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

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

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

        dir = basedir
        if len(name) == 0:
            continue

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

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

        gauge = GaugeOutput(d, "Image Extraction", "Extracting image...")
        try:
            image = session['image']
            out = image.out
            out.add(gauge)
            try:
                if "checksum" not in session:
                    md5 = MD5(out)
                    session['checksum'] = md5.compute(image.device, image.size)

                # Extract image file
                image.dump(path)

                # Extract 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 ...")
                md5str = "%s %s\n" % (session['checksum'], name)
                with open('%s.md5sum' % path, 'w') as f:
                    f.write(md5str)
                out.success("done")
            finally:
                out.remove(gauge)
        finally:
            gauge.cleanup()
        d.msgbox("Image file `%s' was successfully extracted!" % path,
                 width=SMALL_WIDTH)
        break

    return True

Nikos Skalkotos's avatar
Nikos Skalkotos committed

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
    assert 'url' in info, "Cloud: `%s' does not have a url attr" % name
    assert 'token' in info, "Cloud: `%s' does not have a token attr" % name

    description = info['description'] if 'description' in info else ""
    url = info['url']
    token = info['token']

    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 :