Skip to content
Snippets Groups Projects
dialog_menu.py 38.5 KiB
Newer Older
Nikos Skalkotos's avatar
Nikos Skalkotos committed
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011-2015 GRNET S.A.
Nikos Skalkotos's avatar
Nikos Skalkotos committed
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
Nikos Skalkotos's avatar
Nikos Skalkotos committed
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
Nikos Skalkotos's avatar
Nikos Skalkotos committed
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
Nikos Skalkotos's avatar
Nikos Skalkotos committed
"""This module implements the "expert" mode of the dialog-based version of
snf-image-creator.
"""

import os
import textwrap
import StringIO
import tempfile

from image_creator import __version__ as version
from image_creator.util import FatalError, virtio_versions
from image_creator.output.dialog import GaugeOutput, InfoBoxOutput
from image_creator.kamaki_wrapper import Kamaki, ClientError, CONTAINER
from image_creator.help import get_help_file
from image_creator.dialog_util import SMALL_WIDTH, WIDTH, \
    update_background_title, confirm_reset, confirm_exit, Reset, \
    extract_image, add_cloud, edit_cloud, update_sysprep_param, select_file, \
    copy_file

CONFIGURATION_TASKS = [
    ("Partition table manipulation", ["FixPartitionTable"], lambda x: True),
    ("File system resize",
     ["FilesystemResizeUnmounted", "FilesystemResizeMounted"], lambda x: True),
    ("Swap partition configuration", ["AddSwap"], lambda x: x == 'linux'),
    ("SSH keys removal", ["DeleteSSHKeys"], lambda x: x != 'windows'),
    ("Temporal RDP disabling", ["DisableRemoteDesktopConnections"],
     lambda x: x == "windows"),
    ("SELinux relabeling at next boot", ["SELinuxAutorelabel"],
     lambda x: x == "linux"),
    ("Hostname/Computer Name assignment", ["AssignHostname"], lambda x: True),
    ("Password change", ["ChangePassword"], lambda x: True),
    ("Network configuration", ["ConfigureNetwork"], lambda x: x != 'windows'),
    ("File injection", ["EnforcePersonality"], lambda x: True)
SYSPREP_PARAM_MAXLEN = 20
class MetadataMonitor(object):
Nikos Skalkotos's avatar
Nikos Skalkotos committed
    """Monitors image metadata chages"""
    def __init__(self, session, meta):
        self.session = session
        self.meta = meta
        self.old = {}

    def __enter__(self):
        self.old = {}
        for (k, v) in self.meta.items():
            self.old[k] = v

    def __exit__(self, exc_type, exc_val, exc_tb):
        d = self.session['dialog']

        altered = {}
        added = {}

        for (k, v) in self.meta.items():
            if k not in self.old:
                added[k] = v
            elif self.old[k] != v:
                altered[k] = v

        if not (len(added) or len(altered)):
            return

        msg = "The last action has changed some image properties:\n\n"
        if len(added):
            msg += "New image properties:\n"
            for (k, v) in added.items():
                msg += '    %s: "%s"\n' % (k, v)
            msg += "\n"
        if len(altered):
            msg += "Updated image properties:\n"
            for (k, v) in altered.items():
                msg += '    %s: "%s" -> "%s"\n' % (k, self.old[k], v)
            msg += "\n"

        d.msgbox(msg, title="Image Property Changes", width=SMALL_WIDTH)


def upload_image(session):
    """Upload the image to the storage service"""
    d = session["dialog"]
    image = session['image']

    if "account" not in session:
Nikos Skalkotos's avatar
Nikos Skalkotos committed
        d.msgbox("You need to select a valid cloud before you can upload "
                 "images to it", width=SMALL_WIDTH)
        return False

    while 1:
        if 'upload' in session:
            init = session['upload']
        elif 'OS' in session['image'].meta:
            init = "%s.diskdump" % session['image'].meta['OS']
        else:
            init = ""
        (code, answer) = d.inputbox("Please provide a filename:", init=init,
                                    width=WIDTH)

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

        filename = answer.strip()
        if len(filename) == 0:
            d.msgbox("Filename cannot be empty", width=SMALL_WIDTH)
            continue

        kamaki = Kamaki(session['account'], None)
        overwrite = []
        for f in (filename, "%s.md5sum" % filename, "%s.meta" % filename):
            if kamaki.object_exists(f):
                overwrite.append(f)

        if len(overwrite) > 0:
            if d.yesno("The following storage service object(s) already "
                       "exist(s):\n%s\nDo you want to overwrite them?" %
                       "\n".join(overwrite), width=WIDTH, defaultno=1):
                continue

        session['upload'] = filename
        break

Nikos Skalkotos's avatar
Nikos Skalkotos committed
    gauge = GaugeOutput(d, "Image Upload", "Uploading ...")
        out = image.out
        try:
            if 'checksum' not in session:
                session['checksum'] = image.md5()

            try:
                # Upload image file
Nikos Skalkotos's avatar
Nikos Skalkotos committed
                with image.raw_device() as raw:
                    with open(raw, 'rb') as f:
                        session["pithos_uri"] = \
                            kamaki.upload(f, image.size, filename, CONTAINER,
Nikos Skalkotos's avatar
Nikos Skalkotos committed
                                          "Calculating block hashes",
                                          "Uploading missing blocks")
                # Upload md5sum file
                out.info("Uploading md5sum file ...")
                md5str = "%s %s\n" % (session['checksum'], filename)
                kamaki.upload(StringIO.StringIO(md5str), size=len(md5str),
                              remote_path="%s.md5sum" % filename,
                              container=CONTAINER)
                out.success("done")

            except ClientError as e:
                d.msgbox(
                    "Error in storage service client: %s" % e.message,
                    title="Storage Service Client Error", width=SMALL_WIDTH)
                if 'pithos_uri' in session:
                    del session['pithos_uri']
                return False
        finally:
            out.remove(gauge)
    finally:
        gauge.cleanup()

    d.msgbox("Image file `%s' was successfully uploaded" % filename,
             width=SMALL_WIDTH)

    return True


def register_image(session):
    """Register image with the compute service"""
    d = session["dialog"]
    image = session['image']
Nikos Skalkotos's avatar
Nikos Skalkotos committed
    is_public = False

    if "account" not in session:
Nikos Skalkotos's avatar
Nikos Skalkotos committed
        d.msgbox("You need to select a valid cloud before you "
                 "can register an images with it", width=SMALL_WIDTH)
        return False

    if "pithos_uri" not in session:
        d.msgbox("You need to upload the image to the cloud before you can "
Loading
Loading full blame...