Statistics
| Branch: | Tag: | Revision:

root / image_creator / dialog_wizard.py @ 12c97404

History | View | Annotate | Download (16.2 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 d415dda2 Nikos Skalkotos
class WizardFormPage(WizardPage):
212 d415dda2 Nikos Skalkotos
    """Represents a Form in a wizard"""
213 d415dda2 Nikos Skalkotos
214 d415dda2 Nikos Skalkotos
    def __init__(self, name, display_name, text, fields, **kargs):
215 d415dda2 Nikos Skalkotos
        super(WizardFormPage, self).__init__(name, display_name, text, **kargs)
216 d415dda2 Nikos Skalkotos
        self.fields = fields
217 d415dda2 Nikos Skalkotos
218 d415dda2 Nikos Skalkotos
    def run(self, session, title):
219 d415dda2 Nikos Skalkotos
        d = session['dialog']
220 d415dda2 Nikos Skalkotos
        w = session['wizard']
221 d415dda2 Nikos Skalkotos
222 d415dda2 Nikos Skalkotos
        field_lenght = len(self.fields())
223 d415dda2 Nikos Skalkotos
        form_height = field_lenght if field_lenght < PAGE_HEIGHT - 4 \
224 d415dda2 Nikos Skalkotos
            else PAGET_HEIGHT - 4
225 d415dda2 Nikos Skalkotos
226 d415dda2 Nikos Skalkotos
        (code, output) = d.form(
227 d415dda2 Nikos Skalkotos
            self.text, width=PAGE_WIDTH, height=PAGE_HEIGHT,
228 d415dda2 Nikos Skalkotos
            form_height=form_height, ok_label="Next", cancel="Back",
229 d415dda2 Nikos Skalkotos
            fields=self.fields(), title=title)
230 d415dda2 Nikos Skalkotos
231 d415dda2 Nikos Skalkotos
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
232 d415dda2 Nikos Skalkotos
            return self.PREV
233 d415dda2 Nikos Skalkotos
234 d415dda2 Nikos Skalkotos
        w[self.name] = self.validate(output)
235 d415dda2 Nikos Skalkotos
        self.default = output
236 d415dda2 Nikos Skalkotos
        self.info = "%s: %s" % (self.display_name, self.display(w[self.name]))
237 d415dda2 Nikos Skalkotos
238 d415dda2 Nikos Skalkotos
        return self.NEXT
239 d415dda2 Nikos Skalkotos
240 d415dda2 Nikos Skalkotos
241 8d66cd4c Nikos Skalkotos
class WizardMenuPage(WizardPageWthChoices):
242 8d66cd4c Nikos Skalkotos
    """Represents a menu dialog with available choices in a wizard"""
243 5a380da9 Nikos Skalkotos
244 8d66cd4c Nikos Skalkotos
    def run(self, session, title):
245 5a380da9 Nikos Skalkotos
        d = session['dialog']
246 5a380da9 Nikos Skalkotos
        w = session['wizard']
247 5a380da9 Nikos Skalkotos
248 5a380da9 Nikos Skalkotos
        extra_button = 1 if self.extra else 0
249 5a380da9 Nikos Skalkotos
250 5a380da9 Nikos Skalkotos
        choices = self.choices()
251 5a380da9 Nikos Skalkotos
252 5a380da9 Nikos Skalkotos
        if len(choices) == 0:
253 5a380da9 Nikos Skalkotos
            assert self.fallback, "Zero choices and no fallback"
254 5a380da9 Nikos Skalkotos
            if self.fallback():
255 5a380da9 Nikos Skalkotos
                raise WizardReloadPage
256 5a380da9 Nikos Skalkotos
            else:
257 5a380da9 Nikos Skalkotos
                return self.PREV
258 5a380da9 Nikos Skalkotos
259 5a380da9 Nikos Skalkotos
        default_item = self.default if self.default else choices[0][0]
260 5a380da9 Nikos Skalkotos
261 5a380da9 Nikos Skalkotos
        (code, choice) = d.menu(
262 8d66cd4c Nikos Skalkotos
            self.text, width=PAGE_WIDTH, ok_label="Next", cancel="Back",
263 8d66cd4c Nikos Skalkotos
            title=title, choices=choices, height=PAGE_HEIGHT,
264 8d66cd4c Nikos Skalkotos
            default_item=default_item, extra_label=self.extra_label,
265 8d66cd4c Nikos Skalkotos
            extra_button=extra_button)
266 5a380da9 Nikos Skalkotos
267 5a380da9 Nikos Skalkotos
        if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
268 5a380da9 Nikos Skalkotos
            return self.PREV
269 5a380da9 Nikos Skalkotos
        elif code == d.DIALOG_EXTRA:
270 5a380da9 Nikos Skalkotos
            self.extra()
271 5a380da9 Nikos Skalkotos
            raise WizardReloadPage
272 5a380da9 Nikos Skalkotos
273 5a380da9 Nikos Skalkotos
        self.default = choice
274 5a380da9 Nikos Skalkotos
        w[self.name] = self.validate(choice)
275 8d66cd4c Nikos Skalkotos
        self.info = "%s: %s" % (self.display_name, self.display(w[self.name]))
276 5a380da9 Nikos Skalkotos
277 5a380da9 Nikos Skalkotos
        return self.NEXT
278 5a380da9 Nikos Skalkotos
279 5a380da9 Nikos Skalkotos
280 88f83027 Nikos Skalkotos
def start_wizard(session):
281 88f83027 Nikos Skalkotos
    """Run the image creation wizard"""
282 49c07ce3 Nikos Skalkotos
283 d415dda2 Nikos Skalkotos
    image = session['image']
284 d415dda2 Nikos Skalkotos
    distro = image.distro
285 d415dda2 Nikos Skalkotos
    ostype = image.ostype
286 5a380da9 Nikos Skalkotos
287 d415dda2 Nikos Skalkotos
    # Create Cloud Wizard Page
288 5a380da9 Nikos Skalkotos
    def cloud_choices():
289 5a380da9 Nikos Skalkotos
        choices = []
290 5a380da9 Nikos Skalkotos
        for (name, cloud) in Kamaki.get_clouds().items():
291 5a380da9 Nikos Skalkotos
            descr = cloud['description'] if 'description' in cloud else ''
292 5a380da9 Nikos Skalkotos
            choices.append((name, descr))
293 5a380da9 Nikos Skalkotos
294 5a380da9 Nikos Skalkotos
        return choices
295 5a380da9 Nikos Skalkotos
296 5a380da9 Nikos Skalkotos
    def cloud_add():
297 5a380da9 Nikos Skalkotos
        return add_cloud(session)
298 5a380da9 Nikos Skalkotos
299 5a380da9 Nikos Skalkotos
    def cloud_none_available():
300 5a380da9 Nikos Skalkotos
        if not session['dialog'].yesno(
301 5a380da9 Nikos Skalkotos
                "No available clouds found. Would you like to add one now?",
302 5a380da9 Nikos Skalkotos
                width=PAGE_WIDTH, defaultno=0):
303 5a380da9 Nikos Skalkotos
            return add_cloud(session)
304 5a380da9 Nikos Skalkotos
        return False
305 5a380da9 Nikos Skalkotos
306 5a380da9 Nikos Skalkotos
    def cloud_validate(cloud):
307 5a380da9 Nikos Skalkotos
        if not Kamaki.get_account(cloud):
308 5a380da9 Nikos Skalkotos
            if not session['dialog'].yesno(
309 5a380da9 Nikos Skalkotos
                    "The cloud you have selected is not valid! Would you "
310 5a380da9 Nikos Skalkotos
                    "like to edit it now?", width=PAGE_WIDTH, defaultno=0):
311 5a380da9 Nikos Skalkotos
                if edit_cloud(session, cloud):
312 5a380da9 Nikos Skalkotos
                    return cloud
313 5a380da9 Nikos Skalkotos
314 d415dda2 Nikos Skalkotos
            raise WizardReloadPage
315 5a380da9 Nikos Skalkotos
316 5a380da9 Nikos Skalkotos
        return cloud
317 5a380da9 Nikos Skalkotos
318 5a380da9 Nikos Skalkotos
    cloud = WizardMenuPage(
319 5a380da9 Nikos Skalkotos
        "Cloud", "Cloud",
320 5a380da9 Nikos Skalkotos
        "Please select a cloud account or press <Add> to add a new one:",
321 5a380da9 Nikos Skalkotos
        choices=cloud_choices, extra_label="Add", extra=cloud_add,
322 5a380da9 Nikos Skalkotos
        title="Clouds", validate=cloud_validate, fallback=cloud_none_available)
323 5a380da9 Nikos Skalkotos
324 d415dda2 Nikos Skalkotos
    # Create Image Name Wizard Page
325 baa62724 Nikos Skalkotos
    name = WizardInputPage(
326 baa62724 Nikos Skalkotos
        "ImageName", "Image Name", "Please provide a name for the image:",
327 8d66cd4c Nikos Skalkotos
        title="Image Name", default=ostype if distro == "unknown" else distro)
328 baa62724 Nikos Skalkotos
329 d415dda2 Nikos Skalkotos
    # Create Image Description Wizard Page
330 37d581b8 Nikos Skalkotos
    descr = WizardInputPage(
331 baa62724 Nikos Skalkotos
        "ImageDescription", "Image Description",
332 baa62724 Nikos Skalkotos
        "Please provide a description for the image:",
333 8d66cd4c Nikos Skalkotos
        title="Image Description", default=session['metadata']['DESCRIPTION']
334 8d66cd4c Nikos Skalkotos
        if 'DESCRIPTION' in session['metadata'] else '')
335 baa62724 Nikos Skalkotos
336 d415dda2 Nikos Skalkotos
    # Create Sysprep Params Wizard Page
337 d415dda2 Nikos Skalkotos
    needed = image.os.needed_sysprep_params()
338 d415dda2 Nikos Skalkotos
339 d415dda2 Nikos Skalkotos
    def sysprep_params_fields():
340 d415dda2 Nikos Skalkotos
        fields = []
341 d415dda2 Nikos Skalkotos
        available = image.os.sysprep_params
342 d415dda2 Nikos Skalkotos
        for param in needed:
343 d415dda2 Nikos Skalkotos
            text = param.description
344 d415dda2 Nikos Skalkotos
            default = available[param.name] if param.name in available else ""
345 d415dda2 Nikos Skalkotos
            fields.append(("%s: " % text, default, param.length))
346 d415dda2 Nikos Skalkotos
        return fields
347 d415dda2 Nikos Skalkotos
348 d415dda2 Nikos Skalkotos
    def sysprep_params_validate(answer):
349 d415dda2 Nikos Skalkotos
        params = {}
350 d415dda2 Nikos Skalkotos
        for i in range(len(answer)):
351 d415dda2 Nikos Skalkotos
            if needed[i].validator(answer):
352 d415dda2 Nikos Skalkotos
                params[needed[i].name] = answer[i]
353 d415dda2 Nikos Skalkotos
            else:
354 d415dda2 Nikos Skalkotos
                session['dialog'].msgbox("Invalid value for parameter `%s'" %
355 d415dda2 Nikos Skalkotos
                                         needed[i].name)
356 d415dda2 Nikos Skalkotos
                raise WizardReloadPage
357 d415dda2 Nikos Skalkotos
        return params
358 d415dda2 Nikos Skalkotos
359 d415dda2 Nikos Skalkotos
    def sysprep_params_display(params):
360 d415dda2 Nikos Skalkotos
        return ",".join(["%s=%s" % (key, val) for key, val in params.items()])
361 d415dda2 Nikos Skalkotos
362 d415dda2 Nikos Skalkotos
    sysprep_params = WizardFormPage(
363 d415dda2 Nikos Skalkotos
        "SysprepParams", "Sysprep Parameters",
364 d415dda2 Nikos Skalkotos
        "Prease fill in the following system preparation parameters:",
365 d415dda2 Nikos Skalkotos
        title="System Preparation Parameters", fields=sysprep_params_fields,
366 d415dda2 Nikos Skalkotos
        display=sysprep_params_display, validate=sysprep_params_validate
367 d415dda2 Nikos Skalkotos
    ) if len(needed) != 0 else None
368 d415dda2 Nikos Skalkotos
369 d415dda2 Nikos Skalkotos
    # Create Image Registration Wizard Page
370 5a380da9 Nikos Skalkotos
    def registration_choices():
371 5a380da9 Nikos Skalkotos
        return [("Private", "Image is accessible only by this user"),
372 5a380da9 Nikos Skalkotos
                ("Public", "Everyone can create VMs from this image")]
373 5a380da9 Nikos Skalkotos
374 37d581b8 Nikos Skalkotos
    registration = WizardRadioListPage(
375 baa62724 Nikos Skalkotos
        "ImageRegistration", "Registration Type",
376 5a380da9 Nikos Skalkotos
        "Please provide a registration type:", registration_choices,
377 37d581b8 Nikos Skalkotos
        title="Registration Type", default="Private")
378 31160dc8 Nikos Skalkotos
379 3c33e68a Nikos Skalkotos
    w = Wizard(session)
380 fbdf1d8f Nikos Skalkotos
381 5a380da9 Nikos Skalkotos
    w.add_page(cloud)
382 3c33e68a Nikos Skalkotos
    w.add_page(name)
383 3c33e68a Nikos Skalkotos
    w.add_page(descr)
384 d415dda2 Nikos Skalkotos
    if sysprep_params is not None:
385 d415dda2 Nikos Skalkotos
        w.add_page(sysprep_params)
386 37d581b8 Nikos Skalkotos
    w.add_page(registration)
387 fbdf1d8f Nikos Skalkotos
388 fbdf1d8f Nikos Skalkotos
    if w.run():
389 5a380da9 Nikos Skalkotos
        create_image(session)
390 fbdf1d8f Nikos Skalkotos
    else:
391 fbdf1d8f Nikos Skalkotos
        return False
392 fbdf1d8f Nikos Skalkotos
393 fbdf1d8f Nikos Skalkotos
    return True
394 fbdf1d8f Nikos Skalkotos
395 fbdf1d8f Nikos Skalkotos
396 5a380da9 Nikos Skalkotos
def create_image(session):
397 88f83027 Nikos Skalkotos
    """Create an image using the information collected by the wizard"""
398 37ee0098 Nikos Skalkotos
    d = session['dialog']
399 f5174d2c Nikos Skalkotos
    image = session['image']
400 fbdf1d8f Nikos Skalkotos
    wizard = session['wizard']
401 fbdf1d8f Nikos Skalkotos
402 789a3763 Nikos Skalkotos
    with_progress = OutputWthProgress(True)
403 f5174d2c Nikos Skalkotos
    out = image.out
404 789a3763 Nikos Skalkotos
    out.add(with_progress)
405 789a3763 Nikos Skalkotos
    try:
406 789a3763 Nikos Skalkotos
        out.clear()
407 fbdf1d8f Nikos Skalkotos
408 789a3763 Nikos Skalkotos
        #Sysprep
409 d415dda2 Nikos Skalkotos
        image.os.sysprep_params.update(wizard['SysprepParams'])
410 f5174d2c Nikos Skalkotos
        image.os.do_sysprep()
411 f5174d2c Nikos Skalkotos
        metadata = image.os.meta
412 1d413d1e Nikos Skalkotos
413 789a3763 Nikos Skalkotos
        #Shrink
414 f5174d2c Nikos Skalkotos
        size = image.shrink()
415 3793498a Nikos Skalkotos
        session['shrinked'] = True
416 023e1217 Nikos Skalkotos
        update_background_title(session)
417 fbdf1d8f Nikos Skalkotos
418 f5174d2c Nikos Skalkotos
        metadata.update(image.meta)
419 789a3763 Nikos Skalkotos
        metadata['DESCRIPTION'] = wizard['ImageDescription']
420 fbdf1d8f Nikos Skalkotos
421 789a3763 Nikos Skalkotos
        #MD5
422 789a3763 Nikos Skalkotos
        md5 = MD5(out)
423 f5174d2c Nikos Skalkotos
        session['checksum'] = md5.compute(image.device, size)
424 fbdf1d8f Nikos Skalkotos
425 fbdf1d8f Nikos Skalkotos
        out.output()
426 789a3763 Nikos Skalkotos
        try:
427 ffc64d7c Nikos Skalkotos
            out.output("Uploading image to the cloud:")
428 5a380da9 Nikos Skalkotos
            account = Kamaki.get_account(wizard['Cloud'])
429 5a380da9 Nikos Skalkotos
            assert account, "Cloud: %s is not valid" % wizard['Cloud']
430 49c07ce3 Nikos Skalkotos
            kamaki = Kamaki(account, out)
431 789a3763 Nikos Skalkotos
432 789a3763 Nikos Skalkotos
            name = "%s-%s.diskdump" % (wizard['ImageName'],
433 789a3763 Nikos Skalkotos
                                       time.strftime("%Y%m%d%H%M"))
434 789a3763 Nikos Skalkotos
            pithos_file = ""
435 f5174d2c Nikos Skalkotos
            with open(image.device, 'rb') as f:
436 789a3763 Nikos Skalkotos
                pithos_file = kamaki.upload(f, size, name,
437 8e58e699 Nikos Skalkotos
                                            "(1/3)  Calculating block hashes",
438 8e58e699 Nikos Skalkotos
                                            "(2/3)  Uploading missing blocks")
439 789a3763 Nikos Skalkotos
440 8e58e699 Nikos Skalkotos
            out.output("(3/3)  Uploading md5sum file ...", False)
441 789a3763 Nikos Skalkotos
            md5sumstr = '%s %s\n' % (session['checksum'], name)
442 789a3763 Nikos Skalkotos
            kamaki.upload(StringIO.StringIO(md5sumstr), size=len(md5sumstr),
443 789a3763 Nikos Skalkotos
                          remote_path="%s.%s" % (name, 'md5sum'))
444 789a3763 Nikos Skalkotos
            out.success('done')
445 789a3763 Nikos Skalkotos
            out.output()
446 789a3763 Nikos Skalkotos
447 769526cb Nikos Skalkotos
            is_public = True if wizard['ImageRegistration'] == "Public" else \
448 769526cb Nikos Skalkotos
                False
449 ffc64d7c Nikos Skalkotos
            out.output('Registering %s image with the cloud ...' %
450 769526cb Nikos Skalkotos
                       wizard['ImageRegistration'].lower(), False)
451 8e58e699 Nikos Skalkotos
            result = kamaki.register(wizard['ImageName'], pithos_file,
452 8e58e699 Nikos Skalkotos
                                     metadata, is_public)
453 789a3763 Nikos Skalkotos
            out.success('done')
454 8e58e699 Nikos Skalkotos
            out.output("Uploading metadata file ...", False)
455 8e58e699 Nikos Skalkotos
            metastring = unicode(json.dumps(result, ensure_ascii=False))
456 8e58e699 Nikos Skalkotos
            kamaki.upload(StringIO.StringIO(metastring), size=len(metastring),
457 8e58e699 Nikos Skalkotos
                          remote_path="%s.%s" % (name, 'meta'))
458 8e58e699 Nikos Skalkotos
            out.success('done')
459 8e58e699 Nikos Skalkotos
460 3afe6b44 Nikos Skalkotos
            if is_public:
461 3afe6b44 Nikos Skalkotos
                out.output("Sharing md5sum file ...", False)
462 3afe6b44 Nikos Skalkotos
                kamaki.share("%s.md5sum" % name)
463 3afe6b44 Nikos Skalkotos
                out.success('done')
464 3afe6b44 Nikos Skalkotos
                out.output("Sharing metadata file ...", False)
465 3afe6b44 Nikos Skalkotos
                kamaki.share("%s.meta" % name)
466 3afe6b44 Nikos Skalkotos
                out.success('done')
467 3afe6b44 Nikos Skalkotos
468 789a3763 Nikos Skalkotos
            out.output()
469 789a3763 Nikos Skalkotos
470 789a3763 Nikos Skalkotos
        except ClientError as e:
471 ffc64d7c Nikos Skalkotos
            raise FatalError("Storage service client: %d %s" %
472 ffc64d7c Nikos Skalkotos
                             (e.status, e.message))
473 789a3763 Nikos Skalkotos
    finally:
474 789a3763 Nikos Skalkotos
        out.remove(with_progress)
475 09ed3d46 Nikos Skalkotos
476 ffc64d7c Nikos Skalkotos
    text = "The %s image was successfully uploaded to the storage service " \
477 ffc64d7c Nikos Skalkotos
           "and registered with the compute service of %s. Would you like " \
478 ffc64d7c Nikos Skalkotos
           "to keep a local copy?" % \
479 ffc64d7c Nikos Skalkotos
           (wizard['Cloud'], wizard['ImageRegistration'].lower())
480 ffc64d7c Nikos Skalkotos
481 ffc64d7c Nikos Skalkotos
    if not d.yesno(text, width=PAGE_WIDTH):
482 023e1217 Nikos Skalkotos
        extract_image(session)
483 37ee0098 Nikos Skalkotos
484 09ed3d46 Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :