Revision 5a380da9 image_creator/dialog_wizard.py
b/image_creator/dialog_wizard.py | ||
---|---|---|
48 | 48 |
add_cloud, edit_cloud |
49 | 49 |
|
50 | 50 |
PAGE_WIDTH = 70 |
51 |
PAGE_HEIGHT = 10 |
|
51 | 52 |
|
52 | 53 |
|
53 | 54 |
class WizardExit(Exception): |
... | ... | |
55 | 56 |
pass |
56 | 57 |
|
57 | 58 |
|
58 |
class WizardInvalidData(Exception):
|
|
59 |
"""Exception triggered when the user provided data are invalid"""
|
|
59 |
class WizardReloadPage(Exception):
|
|
60 |
"""Exception that reloads the last WizardPage"""
|
|
60 | 61 |
pass |
61 | 62 |
|
62 | 63 |
|
... | ... | |
85 | 86 |
idx += self.pages[idx].run(self.session, idx, len(self.pages)) |
86 | 87 |
except WizardExit: |
87 | 88 |
return False |
88 |
except WizardInvalidData:
|
|
89 |
except WizardReloadPage:
|
|
89 | 90 |
continue |
90 | 91 |
|
91 | 92 |
if idx >= len(self.pages): |
... | ... | |
146 | 147 |
w = session['wizard'] |
147 | 148 |
|
148 | 149 |
choices = [] |
149 |
for i in range(len(self.choices)):
|
|
150 |
default = 1 if self.choices[i][0] == self.default else 0
|
|
151 |
choices.append((self.choices[i][0], self.choices[i][1], default))
|
|
150 |
for choice in self.choices():
|
|
151 |
default = 1 if choice[0] == self.default else 0
|
|
152 |
choices.append((choice[0], choice[1], default))
|
|
152 | 153 |
|
153 | 154 |
(code, answer) = d.radiolist( |
154 |
self.message, height=10, width=PAGE_WIDTH, ok_label="Next",
|
|
155 |
cancel="Back", choices=choices,
|
|
155 |
self.message, width=PAGE_WIDTH, ok_label="Next", cancel="Back",
|
|
156 |
choices=choices, height=PAGE_HEIGHT,
|
|
156 | 157 |
title="(%d/%d) %s" % (index + 1, total, self.title)) |
157 | 158 |
|
158 | 159 |
if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): |
... | ... | |
182 | 183 |
|
183 | 184 |
(code, answer) = d.inputbox( |
184 | 185 |
self.message, init=self.init, width=PAGE_WIDTH, ok_label="Next", |
185 |
cancel="Back", title="(%d/%d) %s" % (index + 1, total, self.title)) |
|
186 |
cancel="Back", height=PAGE_HEIGHT, |
|
187 |
title="(%d/%d) %s" % (index + 1, total, self.title)) |
|
186 | 188 |
|
187 | 189 |
if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): |
188 | 190 |
return self.PREV |
... | ... | |
195 | 197 |
return self.NEXT |
196 | 198 |
|
197 | 199 |
|
200 |
class WizardMenuPage(WizardPage): |
|
201 |
"""Represents a menu dialog in a wizard""" |
|
202 |
def __init__(self, name, printable, message, choices, **kargs): |
|
203 |
super(WizardMenuPage, self).__init__(**kargs) |
|
204 |
self.name = name |
|
205 |
self.printable = printable |
|
206 |
self.message = message |
|
207 |
self.info = "%s: <none>" % self.printable |
|
208 |
self.choices = choices |
|
209 |
self.title = kargs['title'] if 'title' in kargs else '' |
|
210 |
self.default = kargs['default'] if 'default' in kargs else "" |
|
211 |
self.extra = kargs['extra'] if 'extra' in kargs else None |
|
212 |
self.extra_label = \ |
|
213 |
kargs['extra_label'] if 'extra_label' in kargs else 'Extra' |
|
214 |
self.fallback = kargs['fallback'] if 'fallback' in kargs else None |
|
215 |
|
|
216 |
def run(self, session, index, total): |
|
217 |
d = session['dialog'] |
|
218 |
w = session['wizard'] |
|
219 |
|
|
220 |
extra_button = 1 if self.extra else 0 |
|
221 |
|
|
222 |
choices = self.choices() |
|
223 |
|
|
224 |
if len(choices) == 0: |
|
225 |
assert self.fallback, "Zero choices and no fallback" |
|
226 |
if self.fallback(): |
|
227 |
raise WizardReloadPage |
|
228 |
else: |
|
229 |
return self.PREV |
|
230 |
|
|
231 |
default_item = self.default if self.default else choices[0][0] |
|
232 |
|
|
233 |
(code, choice) = d.menu( |
|
234 |
self.message, width=PAGE_WIDTH, ok_label="Next", cancel="Back", |
|
235 |
title="(%d/%d) %s" % (index + 1, total, self.title), |
|
236 |
choices=choices, height=PAGE_HEIGHT, default_item=default_item, |
|
237 |
extra_label=self.extra_label, extra_button=extra_button) |
|
238 |
|
|
239 |
if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): |
|
240 |
return self.PREV |
|
241 |
elif code == d.DIALOG_EXTRA: |
|
242 |
self.extra() |
|
243 |
raise WizardReloadPage |
|
244 |
|
|
245 |
self.default = choice |
|
246 |
w[self.name] = self.validate(choice) |
|
247 |
self.info = "%s: %s" % (self.printable, self.display(w[self.name])) |
|
248 |
|
|
249 |
return self.NEXT |
|
250 |
|
|
251 |
|
|
198 | 252 |
def start_wizard(session): |
199 | 253 |
"""Run the image creation wizard""" |
200 | 254 |
|
201 |
d = session['dialog'] |
|
202 |
clouds = Kamaki.get_clouds() |
|
203 |
if not len(clouds): |
|
204 |
if not add_cloud(session): |
|
205 |
return False |
|
206 |
else: |
|
207 |
while 1: |
|
208 |
choices = [] |
|
209 |
for (name, cloud) in clouds.items(): |
|
210 |
descr = cloud['description'] if 'description' in cloud else '' |
|
211 |
choices.append((name, descr)) |
|
212 |
|
|
213 |
(code, choice) = d.menu( |
|
214 |
"In this menu you can select existing cloud account to use " |
|
215 |
" or add new ones. Press <Select> to select an existing " |
|
216 |
"account or <Add> to add a new one.", height=18, |
|
217 |
width=PAGE_WIDTH, choices=choices, menu_height=10, |
|
218 |
ok_label="Select", extra_button=1, extra_label="Add", |
|
219 |
title="Clouds") |
|
220 |
|
|
221 |
if code in (d.DIALOG_CANCEL, d.DIALOG_ESC): |
|
222 |
return False |
|
223 |
elif code == d.DIALOG_OK: # Select button |
|
224 |
account = Kamaki.get_account(choice) |
|
225 |
if not account: |
|
226 |
if not d.yesno("Then cloud you have selected is not " |
|
227 |
"valid! Would you like to edit it?", |
|
228 |
width=PAGE_WIDTH, defaultno=0): |
|
229 |
edit_cloud(session, choice) |
|
230 |
continue |
|
231 |
break |
|
232 |
elif code == d.DIALOG_EXTRA: # Add button |
|
233 |
add_cloud(session) |
|
234 |
|
|
235 | 255 |
distro = session['image'].distro |
236 | 256 |
ostype = session['image'].ostype |
257 |
|
|
258 |
def cloud_choices(): |
|
259 |
choices = [] |
|
260 |
for (name, cloud) in Kamaki.get_clouds().items(): |
|
261 |
descr = cloud['description'] if 'description' in cloud else '' |
|
262 |
choices.append((name, descr)) |
|
263 |
|
|
264 |
return choices |
|
265 |
|
|
266 |
def cloud_add(): |
|
267 |
return add_cloud(session) |
|
268 |
|
|
269 |
def cloud_none_available(): |
|
270 |
if not session['dialog'].yesno( |
|
271 |
"No available clouds found. Would you like to add one now?", |
|
272 |
width=PAGE_WIDTH, defaultno=0): |
|
273 |
return add_cloud(session) |
|
274 |
return False |
|
275 |
|
|
276 |
def cloud_validate(cloud): |
|
277 |
if not Kamaki.get_account(cloud): |
|
278 |
if not session['dialog'].yesno( |
|
279 |
"The cloud you have selected is not valid! Would you " |
|
280 |
"like to edit it now?", width=PAGE_WIDTH, defaultno=0): |
|
281 |
if edit_cloud(session, cloud): |
|
282 |
return cloud |
|
283 |
|
|
284 |
raise WizardInvalidData |
|
285 |
|
|
286 |
return cloud |
|
287 |
|
|
288 |
cloud = WizardMenuPage( |
|
289 |
"Cloud", "Cloud", |
|
290 |
"Please select a cloud account or press <Add> to add a new one:", |
|
291 |
choices=cloud_choices, extra_label="Add", extra=cloud_add, |
|
292 |
title="Clouds", validate=cloud_validate, fallback=cloud_none_available) |
|
293 |
|
|
237 | 294 |
name = WizardInputPage( |
238 | 295 |
"ImageName", "Image Name", "Please provide a name for the image:", |
239 | 296 |
title="Image Name", init=ostype if distro == "unknown" else distro) |
... | ... | |
244 | 301 |
title="Image Description", init=session['metadata']['DESCRIPTION'] if |
245 | 302 |
'DESCRIPTION' in session['metadata'] else '') |
246 | 303 |
|
304 |
def registration_choices(): |
|
305 |
return [("Private", "Image is accessible only by this user"), |
|
306 |
("Public", "Everyone can create VMs from this image")] |
|
307 |
|
|
247 | 308 |
registration = WizardRadioListPage( |
248 | 309 |
"ImageRegistration", "Registration Type", |
249 |
"Please provide a registration type:", |
|
250 |
[("Private", "Image is accessible only by this user"), |
|
251 |
("Public", "Everyone can create VMs from this image")], |
|
310 |
"Please provide a registration type:", registration_choices, |
|
252 | 311 |
title="Registration Type", default="Private") |
253 | 312 |
|
254 | 313 |
w = Wizard(session) |
255 | 314 |
|
315 |
w.add_page(cloud) |
|
256 | 316 |
w.add_page(name) |
257 | 317 |
w.add_page(descr) |
258 | 318 |
w.add_page(registration) |
259 | 319 |
|
260 | 320 |
if w.run(): |
261 |
create_image(session, account)
|
|
321 |
create_image(session) |
|
262 | 322 |
else: |
263 | 323 |
return False |
264 | 324 |
|
265 | 325 |
return True |
266 | 326 |
|
267 | 327 |
|
268 |
def create_image(session, account):
|
|
328 |
def create_image(session): |
|
269 | 329 |
"""Create an image using the information collected by the wizard""" |
270 | 330 |
d = session['dialog'] |
271 | 331 |
image = session['image'] |
... | ... | |
296 | 356 |
out.output() |
297 | 357 |
try: |
298 | 358 |
out.output("Uploading image to pithos:") |
359 |
account = Kamaki.get_account(wizard['Cloud']) |
|
360 |
assert account, "Cloud: %s is not valid" % wizard['Cloud'] |
|
299 | 361 |
kamaki = Kamaki(account, out) |
300 | 362 |
|
301 | 363 |
name = "%s-%s.diskdump" % (wizard['ImageName'], |
... | ... | |
342 | 404 |
out.remove(with_progress) |
343 | 405 |
|
344 | 406 |
msg = "The %s image was successfully uploaded to Pithos and registered " \ |
345 |
"with Cyclades. Would you like to keep a local copy of the image?" \
|
|
407 |
"with Cyclades. Would you like to keep a local copy?" \ |
|
346 | 408 |
% wizard['ImageRegistration'].lower() |
347 | 409 |
if not d.yesno(msg, width=PAGE_WIDTH): |
348 | 410 |
extract_image(session) |
Also available in: Unified diff