Statistics
| Branch: | Tag: | Revision:

root / image_creator / dialog_wizard.py @ 5e18a927

History | View | Annotate | Download (13.7 kB)

1 121f3bc0 Nikos Skalkotos
# -*- coding: utf-8 -*-
2 121f3bc0 Nikos Skalkotos
#
3 09ed3d46 Nikos Skalkotos
# Copyright 2012 GRNET S.A. All rights reserved.
4 09ed3d46 Nikos Skalkotos
#
5 09ed3d46 Nikos Skalkotos
# Redistribution and use in source and binary forms, with or
6 09ed3d46 Nikos Skalkotos
# without modification, are permitted provided that the following
7 09ed3d46 Nikos Skalkotos
# conditions are met:
8 09ed3d46 Nikos Skalkotos
#
9 09ed3d46 Nikos Skalkotos
#   1. Redistributions of source code must retain the above
10 09ed3d46 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
11 09ed3d46 Nikos Skalkotos
#      disclaimer.
12 09ed3d46 Nikos Skalkotos
#
13 09ed3d46 Nikos Skalkotos
#   2. Redistributions in binary form must reproduce the above
14 09ed3d46 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
15 09ed3d46 Nikos Skalkotos
#      disclaimer in the documentation and/or other materials
16 09ed3d46 Nikos Skalkotos
#      provided with the distribution.
17 09ed3d46 Nikos Skalkotos
#
18 09ed3d46 Nikos Skalkotos
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 09ed3d46 Nikos Skalkotos
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 09ed3d46 Nikos Skalkotos
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 09ed3d46 Nikos Skalkotos
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 09ed3d46 Nikos Skalkotos
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 09ed3d46 Nikos Skalkotos
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 09ed3d46 Nikos Skalkotos
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 09ed3d46 Nikos Skalkotos
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 09ed3d46 Nikos Skalkotos
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 09ed3d46 Nikos Skalkotos
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 09ed3d46 Nikos Skalkotos
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 09ed3d46 Nikos Skalkotos
# POSSIBILITY OF SUCH DAMAGE.
30 09ed3d46 Nikos Skalkotos
#
31 09ed3d46 Nikos Skalkotos
# The views and conclusions contained in the software and
32 09ed3d46 Nikos Skalkotos
# documentation are those of the authors and should not be
33 09ed3d46 Nikos Skalkotos
# interpreted as representing official policies, either expressed
34 09ed3d46 Nikos Skalkotos
# or implied, of GRNET S.A.
35 09ed3d46 Nikos Skalkotos
36 121f3bc0 Nikos Skalkotos
"""This module implements the "wizard" mode of the dialog-based version of
37 121f3bc0 Nikos Skalkotos
snf-image-creator.
38 121f3bc0 Nikos Skalkotos
"""
39 121f3bc0 Nikos Skalkotos
40 fbdf1d8f Nikos Skalkotos
import time
41 fbdf1d8f Nikos Skalkotos
import StringIO
42 8e58e699 Nikos Skalkotos
import json
43 09ed3d46 Nikos Skalkotos
44 fbdf1d8f Nikos Skalkotos
from image_creator.kamaki_wrapper import Kamaki, ClientError
45 fbdf1d8f Nikos Skalkotos
from image_creator.util import MD5, FatalError
46 fbdf1d8f Nikos Skalkotos
from image_creator.output.cli import OutputWthProgress
47 49c07ce3 Nikos Skalkotos
from image_creator.dialog_util import extract_image, update_background_title, \
48 49c07ce3 Nikos Skalkotos
    add_cloud, edit_cloud
49 09ed3d46 Nikos Skalkotos
50 3c33e68a Nikos Skalkotos
PAGE_WIDTH = 70
51 5a380da9 Nikos Skalkotos
PAGE_HEIGHT = 10
52 09ed3d46 Nikos Skalkotos
53 09ed3d46 Nikos Skalkotos
54 aeb95900 Nikos Skalkotos
class WizardExit(Exception):
55 88f83027 Nikos Skalkotos
    """Exception used to exit the wizard"""
56 aeb95900 Nikos Skalkotos
    pass
57 aeb95900 Nikos Skalkotos
58 aeb95900 Nikos Skalkotos
59 5a380da9 Nikos Skalkotos
class WizardReloadPage(Exception):
60 5a380da9 Nikos Skalkotos
    """Exception that reloads the last WizardPage"""
61 31160dc8 Nikos Skalkotos
    pass
62 31160dc8 Nikos Skalkotos
63 31160dc8 Nikos Skalkotos
64 3c33e68a Nikos Skalkotos
class Wizard:
65 88f83027 Nikos Skalkotos
    """Represents a dialog-based wizard
66 88f83027 Nikos Skalkotos

67 88f83027 Nikos Skalkotos
    The wizard is a collection of pages that have a "Next" and a "Back" button
68 88f83027 Nikos Skalkotos
    on them. The pages are used to collect user data.
69 88f83027 Nikos Skalkotos
    """
70 88f83027 Nikos Skalkotos
71 3c33e68a Nikos Skalkotos
    def __init__(self, session):
72 3c33e68a Nikos Skalkotos
        self.session = session
73 3c33e68a Nikos Skalkotos
        self.pages = []
74 3c33e68a Nikos Skalkotos
        self.session['wizard'] = {}
75 baa62724 Nikos Skalkotos
        self.d = session['dialog']
76 3c33e68a Nikos Skalkotos
77 3c33e68a Nikos Skalkotos
    def add_page(self, page):
78 88f83027 Nikos Skalkotos
        """Add a new page to the wizard"""
79 3c33e68a Nikos Skalkotos
        self.pages.append(page)
80 09ed3d46 Nikos Skalkotos
81 09ed3d46 Nikos Skalkotos
    def run(self):
82 88f83027 Nikos Skalkotos
        """Run the wizard"""
83 3c33e68a Nikos Skalkotos
        idx = 0
84 3c33e68a Nikos Skalkotos
        while True:
85 aeb95900 Nikos Skalkotos
            try:
86 8d66cd4c Nikos Skalkotos
                total = len(self.pages)
87 8d66cd4c Nikos Skalkotos
                title = "(%d/%d) %s" % (idx + 1, total, self.pages[idx].title)
88 8d66cd4c Nikos Skalkotos
                idx += self.pages[idx].run(self.session, title)
89 aeb95900 Nikos Skalkotos
            except WizardExit:
90 aeb95900 Nikos Skalkotos
                return False
91 5a380da9 Nikos Skalkotos
            except WizardReloadPage:
92 31160dc8 Nikos Skalkotos
                continue
93 09ed3d46 Nikos Skalkotos
94 3c33e68a Nikos Skalkotos
            if idx >= len(self.pages):
95 8d66cd4c Nikos Skalkotos
                text = "All necessary information has been gathered:\n\n"
96 baa62724 Nikos Skalkotos
                for page in self.pages:
97 8d66cd4c Nikos Skalkotos
                    text += " * %s\n" % page.info
98 8d66cd4c Nikos Skalkotos
                text += "\nContinue with the image creation process?"
99 baa62724 Nikos Skalkotos
100 baa62724 Nikos Skalkotos
                ret = self.d.yesno(
101 8d66cd4c Nikos Skalkotos
                    text, width=PAGE_WIDTH, height=8 + len(self.pages),
102 66719e36 Nikos Skalkotos
                    ok_label="Yes", cancel="Back", extra_button=1,
103 66719e36 Nikos Skalkotos
                    extra_label="Quit", title="Confirmation")
104 baa62724 Nikos Skalkotos
105 baa62724 Nikos Skalkotos
                if ret == self.d.DIALOG_CANCEL:
106 baa62724 Nikos Skalkotos
                    idx -= 1
107 baa62724 Nikos Skalkotos
                elif ret == self.d.DIALOG_EXTRA:
108 baa62724 Nikos Skalkotos
                    return False
109 baa62724 Nikos Skalkotos
                elif ret == self.d.DIALOG_OK:
110 baa62724 Nikos Skalkotos
                    return True
111 09ed3d46 Nikos Skalkotos
112 3c33e68a Nikos Skalkotos
            if idx < 0:
113 3c33e68a Nikos Skalkotos
                return False
114 09ed3d46 Nikos Skalkotos
115 09ed3d46 Nikos Skalkotos
116 baa62724 Nikos Skalkotos
class WizardPage(object):
117 88f83027 Nikos Skalkotos
    """Represents a page in a wizard"""
118 3c33e68a Nikos Skalkotos
    NEXT = 1
119 3c33e68a Nikos Skalkotos
    PREV = -1
120 fbdf1d8f Nikos Skalkotos
121 8d66cd4c Nikos Skalkotos
    def __init__(self, name, display_name, text, **kargs):
122 8d66cd4c Nikos Skalkotos
        self.name = name
123 8d66cd4c Nikos Skalkotos
        self.display_name = display_name
124 8d66cd4c Nikos Skalkotos
        self.text = text
125 8d66cd4c Nikos Skalkotos
126 8d66cd4c Nikos Skalkotos
        self.title = kargs['title'] if 'title' in kargs else ""
127 8d66cd4c Nikos Skalkotos
        self.default = kargs['default'] if 'default' in kargs else ""
128 8d66cd4c Nikos Skalkotos
        self.extra = kargs['extra'] if 'extra' in kargs else None
129 8d66cd4c Nikos Skalkotos
        self.extra_label = \
130 8d66cd4c Nikos Skalkotos
            kargs['extra_label'] if 'extra_label' in kargs else 'Extra'
131 8d66cd4c Nikos Skalkotos
132 8d66cd4c Nikos Skalkotos
        self.info = "%s: <none>" % self.display_name
133 8d66cd4c Nikos Skalkotos
134 66719e36 Nikos Skalkotos
        validate = kargs['validate'] if 'validate' in kargs else lambda x: x
135 baa62724 Nikos Skalkotos
        setattr(self, "validate", validate)
136 baa62724 Nikos Skalkotos
137 66719e36 Nikos Skalkotos
        display = kargs['display'] if 'display' in kargs else lambda x: x
138 baa62724 Nikos Skalkotos
        setattr(self, "display", display)
139 baa62724 Nikos Skalkotos
140 8d66cd4c Nikos Skalkotos
    def run(self, session, title):
141 88f83027 Nikos Skalkotos
        """Display this wizard page
142 88f83027 Nikos Skalkotos

143 88f83027 Nikos Skalkotos
        This function is used by the wizard program when accessing a page.
144 88f83027 Nikos Skalkotos
        """
145 fbdf1d8f Nikos Skalkotos
        raise NotImplementedError
146 fbdf1d8f Nikos Skalkotos
147 fbdf1d8f Nikos Skalkotos
148 8d66cd4c Nikos Skalkotos
class WizardPageWthChoices(WizardPage):
149 8d66cd4c Nikos Skalkotos
    """Represents a Wizard Page that allows the user to select something from
150 8d66cd4c Nikos Skalkotos
    a list of choices.
151 8d66cd4c Nikos Skalkotos

152 8d66cd4c Nikos Skalkotos
    The available choices are created by a function passed to the class through
153 8d66cd4c Nikos Skalkotos
    the choices variable. If the choices function returns an empty list, a
154 8d66cd4c Nikos Skalkotos
    fallback funtion is executed if available.
155 8d66cd4c Nikos Skalkotos
    """
156 8d66cd4c Nikos Skalkotos
    def __init__(self, name, display_name, text, choices, **kargs):
157 8d66cd4c Nikos Skalkotos
        super(WizardPageWthChoices, self).__init__(name, display_name, text,
158 8d66cd4c Nikos Skalkotos
                                                   **kargs)
159 37ee0098 Nikos Skalkotos
        self.choices = choices
160 8d66cd4c Nikos Skalkotos
        self.fallback = kargs['fallback'] if 'fallback' in kargs else None
161 8d66cd4c Nikos Skalkotos
162 8d66cd4c Nikos Skalkotos
163 8d66cd4c Nikos Skalkotos
class WizardRadioListPage(WizardPageWthChoices):
164 8d66cd4c Nikos Skalkotos
    """Represent a Radio List in a wizard"""
165 37ee0098 Nikos Skalkotos
166 8d66cd4c Nikos Skalkotos
    def run(self, session, title):
167 37ee0098 Nikos Skalkotos
        d = session['dialog']
168 37ee0098 Nikos Skalkotos
        w = session['wizard']
169 37ee0098 Nikos Skalkotos
170 37ee0098 Nikos Skalkotos
        choices = []
171 5a380da9 Nikos Skalkotos
        for choice in self.choices():
172 5a380da9 Nikos Skalkotos
            default = 1 if choice[0] == self.default else 0
173 5a380da9 Nikos Skalkotos
            choices.append((choice[0], choice[1], default))
174 37ee0098 Nikos Skalkotos
175 baa62724 Nikos Skalkotos
        (code, answer) = d.radiolist(
176 8d66cd4c Nikos Skalkotos
            self.text, width=PAGE_WIDTH, ok_label="Next", cancel="Back",
177 8d66cd4c Nikos Skalkotos
            choices=choices, height=PAGE_HEIGHT, title=title)
178 37ee0098 Nikos Skalkotos
179 baa62724 Nikos Skalkotos
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
180 baa62724 Nikos Skalkotos
            return self.PREV
181 37ee0098 Nikos Skalkotos
182 baa62724 Nikos Skalkotos
        w[self.name] = self.validate(answer)
183 baa62724 Nikos Skalkotos
        self.default = answer
184 8d66cd4c Nikos Skalkotos
        self.info = "%s: %s" % (self.display_name, self.display(w[self.name]))
185 37ee0098 Nikos Skalkotos
186 baa62724 Nikos Skalkotos
        return self.NEXT
187 37ee0098 Nikos Skalkotos
188 37ee0098 Nikos Skalkotos
189 fbdf1d8f Nikos Skalkotos
class WizardInputPage(WizardPage):
190 88f83027 Nikos Skalkotos
    """Represents an input field in a wizard"""
191 09ed3d46 Nikos Skalkotos
192 8d66cd4c Nikos Skalkotos
    def run(self, session, title):
193 3c33e68a Nikos Skalkotos
        d = session['dialog']
194 3c33e68a Nikos Skalkotos
        w = session['wizard']
195 09ed3d46 Nikos Skalkotos
196 baa62724 Nikos Skalkotos
        (code, answer) = d.inputbox(
197 8d66cd4c Nikos Skalkotos
            self.text, init=self.default, width=PAGE_WIDTH, ok_label="Next",
198 8d66cd4c Nikos Skalkotos
            cancel="Back", height=PAGE_HEIGHT, title=title)
199 09ed3d46 Nikos Skalkotos
200 baa62724 Nikos Skalkotos
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
201 baa62724 Nikos Skalkotos
            return self.PREV
202 09ed3d46 Nikos Skalkotos
203 baa62724 Nikos Skalkotos
        value = answer.strip()
204 8d66cd4c Nikos Skalkotos
        self.default = value
205 baa62724 Nikos Skalkotos
        w[self.name] = self.validate(value)
206 8d66cd4c Nikos Skalkotos
        self.info = "%s: %s" % (self.display_name, self.display(w[self.name]))
207 09ed3d46 Nikos Skalkotos
208 09ed3d46 Nikos Skalkotos
        return self.NEXT
209 09ed3d46 Nikos Skalkotos
210 09ed3d46 Nikos Skalkotos
211 8d66cd4c Nikos Skalkotos
class WizardMenuPage(WizardPageWthChoices):
212 8d66cd4c Nikos Skalkotos
    """Represents a menu dialog with available choices in a wizard"""
213 5a380da9 Nikos Skalkotos
214 8d66cd4c Nikos Skalkotos
    def run(self, session, title):
215 5a380da9 Nikos Skalkotos
        d = session['dialog']
216 5a380da9 Nikos Skalkotos
        w = session['wizard']
217 5a380da9 Nikos Skalkotos
218 5a380da9 Nikos Skalkotos
        extra_button = 1 if self.extra else 0
219 5a380da9 Nikos Skalkotos
220 5a380da9 Nikos Skalkotos
        choices = self.choices()
221 5a380da9 Nikos Skalkotos
222 5a380da9 Nikos Skalkotos
        if len(choices) == 0:
223 5a380da9 Nikos Skalkotos
            assert self.fallback, "Zero choices and no fallback"
224 5a380da9 Nikos Skalkotos
            if self.fallback():
225 5a380da9 Nikos Skalkotos
                raise WizardReloadPage
226 5a380da9 Nikos Skalkotos
            else:
227 5a380da9 Nikos Skalkotos
                return self.PREV
228 5a380da9 Nikos Skalkotos
229 5a380da9 Nikos Skalkotos
        default_item = self.default if self.default else choices[0][0]
230 5a380da9 Nikos Skalkotos
231 5a380da9 Nikos Skalkotos
        (code, choice) = d.menu(
232 8d66cd4c Nikos Skalkotos
            self.text, width=PAGE_WIDTH, ok_label="Next", cancel="Back",
233 8d66cd4c Nikos Skalkotos
            title=title, choices=choices, height=PAGE_HEIGHT,
234 8d66cd4c Nikos Skalkotos
            default_item=default_item, extra_label=self.extra_label,
235 8d66cd4c Nikos Skalkotos
            extra_button=extra_button)
236 5a380da9 Nikos Skalkotos
237 5a380da9 Nikos Skalkotos
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
238 5a380da9 Nikos Skalkotos
            return self.PREV
239 5a380da9 Nikos Skalkotos
        elif code == d.DIALOG_EXTRA:
240 5a380da9 Nikos Skalkotos
            self.extra()
241 5a380da9 Nikos Skalkotos
            raise WizardReloadPage
242 5a380da9 Nikos Skalkotos
243 5a380da9 Nikos Skalkotos
        self.default = choice
244 5a380da9 Nikos Skalkotos
        w[self.name] = self.validate(choice)
245 8d66cd4c Nikos Skalkotos
        self.info = "%s: %s" % (self.display_name, self.display(w[self.name]))
246 5a380da9 Nikos Skalkotos
247 5a380da9 Nikos Skalkotos
        return self.NEXT
248 5a380da9 Nikos Skalkotos
249 5a380da9 Nikos Skalkotos
250 88f83027 Nikos Skalkotos
def start_wizard(session):
251 88f83027 Nikos Skalkotos
    """Run the image creation wizard"""
252 49c07ce3 Nikos Skalkotos
253 39b0429a Nikos Skalkotos
    distro = session['image'].distro
254 39b0429a Nikos Skalkotos
    ostype = session['image'].ostype
255 5a380da9 Nikos Skalkotos
256 5a380da9 Nikos Skalkotos
    def cloud_choices():
257 5a380da9 Nikos Skalkotos
        choices = []
258 5a380da9 Nikos Skalkotos
        for (name, cloud) in Kamaki.get_clouds().items():
259 5a380da9 Nikos Skalkotos
            descr = cloud['description'] if 'description' in cloud else ''
260 5a380da9 Nikos Skalkotos
            choices.append((name, descr))
261 5a380da9 Nikos Skalkotos
262 5a380da9 Nikos Skalkotos
        return choices
263 5a380da9 Nikos Skalkotos
264 5a380da9 Nikos Skalkotos
    def cloud_add():
265 5a380da9 Nikos Skalkotos
        return add_cloud(session)
266 5a380da9 Nikos Skalkotos
267 5a380da9 Nikos Skalkotos
    def cloud_none_available():
268 5a380da9 Nikos Skalkotos
        if not session['dialog'].yesno(
269 5a380da9 Nikos Skalkotos
                "No available clouds found. Would you like to add one now?",
270 5a380da9 Nikos Skalkotos
                width=PAGE_WIDTH, defaultno=0):
271 5a380da9 Nikos Skalkotos
            return add_cloud(session)
272 5a380da9 Nikos Skalkotos
        return False
273 5a380da9 Nikos Skalkotos
274 5a380da9 Nikos Skalkotos
    def cloud_validate(cloud):
275 5a380da9 Nikos Skalkotos
        if not Kamaki.get_account(cloud):
276 5a380da9 Nikos Skalkotos
            if not session['dialog'].yesno(
277 5a380da9 Nikos Skalkotos
                    "The cloud you have selected is not valid! Would you "
278 5a380da9 Nikos Skalkotos
                    "like to edit it now?", width=PAGE_WIDTH, defaultno=0):
279 5a380da9 Nikos Skalkotos
                if edit_cloud(session, cloud):
280 5a380da9 Nikos Skalkotos
                    return cloud
281 5a380da9 Nikos Skalkotos
282 5a380da9 Nikos Skalkotos
            raise WizardInvalidData
283 5a380da9 Nikos Skalkotos
284 5a380da9 Nikos Skalkotos
        return cloud
285 5a380da9 Nikos Skalkotos
286 5a380da9 Nikos Skalkotos
    cloud = WizardMenuPage(
287 5a380da9 Nikos Skalkotos
        "Cloud", "Cloud",
288 5a380da9 Nikos Skalkotos
        "Please select a cloud account or press <Add> to add a new one:",
289 5a380da9 Nikos Skalkotos
        choices=cloud_choices, extra_label="Add", extra=cloud_add,
290 5a380da9 Nikos Skalkotos
        title="Clouds", validate=cloud_validate, fallback=cloud_none_available)
291 5a380da9 Nikos Skalkotos
292 baa62724 Nikos Skalkotos
    name = WizardInputPage(
293 baa62724 Nikos Skalkotos
        "ImageName", "Image Name", "Please provide a name for the image:",
294 8d66cd4c Nikos Skalkotos
        title="Image Name", default=ostype if distro == "unknown" else distro)
295 baa62724 Nikos Skalkotos
296 37d581b8 Nikos Skalkotos
    descr = WizardInputPage(
297 baa62724 Nikos Skalkotos
        "ImageDescription", "Image Description",
298 baa62724 Nikos Skalkotos
        "Please provide a description for the image:",
299 8d66cd4c Nikos Skalkotos
        title="Image Description", default=session['metadata']['DESCRIPTION']
300 8d66cd4c Nikos Skalkotos
        if 'DESCRIPTION' in session['metadata'] else '')
301 baa62724 Nikos Skalkotos
302 5a380da9 Nikos Skalkotos
    def registration_choices():
303 5a380da9 Nikos Skalkotos
        return [("Private", "Image is accessible only by this user"),
304 5a380da9 Nikos Skalkotos
                ("Public", "Everyone can create VMs from this image")]
305 5a380da9 Nikos Skalkotos
306 37d581b8 Nikos Skalkotos
    registration = WizardRadioListPage(
307 baa62724 Nikos Skalkotos
        "ImageRegistration", "Registration Type",
308 5a380da9 Nikos Skalkotos
        "Please provide a registration type:", registration_choices,
309 37d581b8 Nikos Skalkotos
        title="Registration Type", default="Private")
310 31160dc8 Nikos Skalkotos
311 3c33e68a Nikos Skalkotos
    w = Wizard(session)
312 fbdf1d8f Nikos Skalkotos
313 5a380da9 Nikos Skalkotos
    w.add_page(cloud)
314 3c33e68a Nikos Skalkotos
    w.add_page(name)
315 3c33e68a Nikos Skalkotos
    w.add_page(descr)
316 37d581b8 Nikos Skalkotos
    w.add_page(registration)
317 fbdf1d8f Nikos Skalkotos
318 fbdf1d8f Nikos Skalkotos
    if w.run():
319 5a380da9 Nikos Skalkotos
        create_image(session)
320 fbdf1d8f Nikos Skalkotos
    else:
321 fbdf1d8f Nikos Skalkotos
        return False
322 fbdf1d8f Nikos Skalkotos
323 fbdf1d8f Nikos Skalkotos
    return True
324 fbdf1d8f Nikos Skalkotos
325 fbdf1d8f Nikos Skalkotos
326 5a380da9 Nikos Skalkotos
def create_image(session):
327 88f83027 Nikos Skalkotos
    """Create an image using the information collected by the wizard"""
328 37ee0098 Nikos Skalkotos
    d = session['dialog']
329 f5174d2c Nikos Skalkotos
    image = session['image']
330 fbdf1d8f Nikos Skalkotos
    wizard = session['wizard']
331 fbdf1d8f Nikos Skalkotos
332 789a3763 Nikos Skalkotos
    with_progress = OutputWthProgress(True)
333 f5174d2c Nikos Skalkotos
    out = image.out
334 789a3763 Nikos Skalkotos
    out.add(with_progress)
335 789a3763 Nikos Skalkotos
    try:
336 789a3763 Nikos Skalkotos
        out.clear()
337 fbdf1d8f Nikos Skalkotos
338 789a3763 Nikos Skalkotos
        #Sysprep
339 f5174d2c Nikos Skalkotos
        image.os.do_sysprep()
340 f5174d2c Nikos Skalkotos
        metadata = image.os.meta
341 1d413d1e Nikos Skalkotos
342 789a3763 Nikos Skalkotos
        #Shrink
343 f5174d2c Nikos Skalkotos
        size = image.shrink()
344 3793498a Nikos Skalkotos
        session['shrinked'] = True
345 023e1217 Nikos Skalkotos
        update_background_title(session)
346 fbdf1d8f Nikos Skalkotos
347 f5174d2c Nikos Skalkotos
        metadata.update(image.meta)
348 789a3763 Nikos Skalkotos
        metadata['DESCRIPTION'] = wizard['ImageDescription']
349 fbdf1d8f Nikos Skalkotos
350 789a3763 Nikos Skalkotos
        #MD5
351 789a3763 Nikos Skalkotos
        md5 = MD5(out)
352 f5174d2c Nikos Skalkotos
        session['checksum'] = md5.compute(image.device, size)
353 fbdf1d8f Nikos Skalkotos
354 fbdf1d8f Nikos Skalkotos
        out.output()
355 789a3763 Nikos Skalkotos
        try:
356 ffc64d7c Nikos Skalkotos
            out.output("Uploading image to the cloud:")
357 5a380da9 Nikos Skalkotos
            account = Kamaki.get_account(wizard['Cloud'])
358 5a380da9 Nikos Skalkotos
            assert account, "Cloud: %s is not valid" % wizard['Cloud']
359 49c07ce3 Nikos Skalkotos
            kamaki = Kamaki(account, out)
360 789a3763 Nikos Skalkotos
361 789a3763 Nikos Skalkotos
            name = "%s-%s.diskdump" % (wizard['ImageName'],
362 789a3763 Nikos Skalkotos
                                       time.strftime("%Y%m%d%H%M"))
363 789a3763 Nikos Skalkotos
            pithos_file = ""
364 f5174d2c Nikos Skalkotos
            with open(image.device, 'rb') as f:
365 789a3763 Nikos Skalkotos
                pithos_file = kamaki.upload(f, size, name,
366 8e58e699 Nikos Skalkotos
                                            "(1/3)  Calculating block hashes",
367 8e58e699 Nikos Skalkotos
                                            "(2/3)  Uploading missing blocks")
368 789a3763 Nikos Skalkotos
369 8e58e699 Nikos Skalkotos
            out.output("(3/3)  Uploading md5sum file ...", False)
370 789a3763 Nikos Skalkotos
            md5sumstr = '%s %s\n' % (session['checksum'], name)
371 789a3763 Nikos Skalkotos
            kamaki.upload(StringIO.StringIO(md5sumstr), size=len(md5sumstr),
372 789a3763 Nikos Skalkotos
                          remote_path="%s.%s" % (name, 'md5sum'))
373 789a3763 Nikos Skalkotos
            out.success('done')
374 789a3763 Nikos Skalkotos
            out.output()
375 789a3763 Nikos Skalkotos
376 769526cb Nikos Skalkotos
            is_public = True if wizard['ImageRegistration'] == "Public" else \
377 769526cb Nikos Skalkotos
                False
378 ffc64d7c Nikos Skalkotos
            out.output('Registering %s image with the cloud ...' %
379 769526cb Nikos Skalkotos
                       wizard['ImageRegistration'].lower(), False)
380 8e58e699 Nikos Skalkotos
            result = kamaki.register(wizard['ImageName'], pithos_file,
381 8e58e699 Nikos Skalkotos
                                     metadata, is_public)
382 789a3763 Nikos Skalkotos
            out.success('done')
383 8e58e699 Nikos Skalkotos
            out.output("Uploading metadata file ...", False)
384 8e58e699 Nikos Skalkotos
            metastring = unicode(json.dumps(result, ensure_ascii=False))
385 8e58e699 Nikos Skalkotos
            kamaki.upload(StringIO.StringIO(metastring), size=len(metastring),
386 8e58e699 Nikos Skalkotos
                          remote_path="%s.%s" % (name, 'meta'))
387 8e58e699 Nikos Skalkotos
            out.success('done')
388 8e58e699 Nikos Skalkotos
389 3afe6b44 Nikos Skalkotos
            if is_public:
390 3afe6b44 Nikos Skalkotos
                out.output("Sharing md5sum file ...", False)
391 3afe6b44 Nikos Skalkotos
                kamaki.share("%s.md5sum" % name)
392 3afe6b44 Nikos Skalkotos
                out.success('done')
393 3afe6b44 Nikos Skalkotos
                out.output("Sharing metadata file ...", False)
394 3afe6b44 Nikos Skalkotos
                kamaki.share("%s.meta" % name)
395 3afe6b44 Nikos Skalkotos
                out.success('done')
396 3afe6b44 Nikos Skalkotos
397 789a3763 Nikos Skalkotos
            out.output()
398 789a3763 Nikos Skalkotos
399 789a3763 Nikos Skalkotos
        except ClientError as e:
400 ffc64d7c Nikos Skalkotos
            raise FatalError("Storage service client: %d %s" %
401 ffc64d7c Nikos Skalkotos
                             (e.status, e.message))
402 789a3763 Nikos Skalkotos
    finally:
403 789a3763 Nikos Skalkotos
        out.remove(with_progress)
404 09ed3d46 Nikos Skalkotos
405 ffc64d7c Nikos Skalkotos
    text = "The %s image was successfully uploaded to the storage service " \
406 ffc64d7c Nikos Skalkotos
           "and registered with the compute service of %s. Would you like " \
407 ffc64d7c Nikos Skalkotos
           "to keep a local copy?" % \
408 ffc64d7c Nikos Skalkotos
           (wizard['Cloud'], wizard['ImageRegistration'].lower())
409 ffc64d7c Nikos Skalkotos
410 ffc64d7c Nikos Skalkotos
    if not d.yesno(text, width=PAGE_WIDTH):
411 023e1217 Nikos Skalkotos
        extract_image(session)
412 37ee0098 Nikos Skalkotos
413 09ed3d46 Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :