Statistics
| Branch: | Tag: | Revision:

root / image_creator / dialog_wizard.py @ e36b5b14

History | View | Annotate | Download (9.8 kB)

1 09ed3d46 Nikos Skalkotos
#!/usr/bin/env python
2 09ed3d46 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 09ed3d46 Nikos Skalkotos
import dialog
37 fbdf1d8f Nikos Skalkotos
import time
38 fbdf1d8f Nikos Skalkotos
import StringIO
39 09ed3d46 Nikos Skalkotos
40 fbdf1d8f Nikos Skalkotos
from image_creator.kamaki_wrapper import Kamaki, ClientError
41 fbdf1d8f Nikos Skalkotos
from image_creator.util import MD5, FatalError
42 fbdf1d8f Nikos Skalkotos
from image_creator.output.cli import OutputWthProgress
43 023e1217 Nikos Skalkotos
from image_creator.dialog_util import extract_image, update_background_title
44 09ed3d46 Nikos Skalkotos
45 3c33e68a Nikos Skalkotos
PAGE_WIDTH = 70
46 09ed3d46 Nikos Skalkotos
47 09ed3d46 Nikos Skalkotos
48 aeb95900 Nikos Skalkotos
class WizardExit(Exception):
49 aeb95900 Nikos Skalkotos
    pass
50 aeb95900 Nikos Skalkotos
51 aeb95900 Nikos Skalkotos
52 3c33e68a Nikos Skalkotos
class Wizard:
53 3c33e68a Nikos Skalkotos
    def __init__(self, session):
54 3c33e68a Nikos Skalkotos
        self.session = session
55 3c33e68a Nikos Skalkotos
        self.pages = []
56 3c33e68a Nikos Skalkotos
        self.session['wizard'] = {}
57 3c33e68a Nikos Skalkotos
58 3c33e68a Nikos Skalkotos
    def add_page(self, page):
59 3c33e68a Nikos Skalkotos
        self.pages.append(page)
60 09ed3d46 Nikos Skalkotos
61 09ed3d46 Nikos Skalkotos
    def run(self):
62 3c33e68a Nikos Skalkotos
        idx = 0
63 3c33e68a Nikos Skalkotos
        while True:
64 aeb95900 Nikos Skalkotos
            try:
65 aeb95900 Nikos Skalkotos
                idx += self.pages[idx].run(self.session, idx, len(self.pages))
66 aeb95900 Nikos Skalkotos
            except WizardExit:
67 aeb95900 Nikos Skalkotos
                return False
68 09ed3d46 Nikos Skalkotos
69 3c33e68a Nikos Skalkotos
            if idx >= len(self.pages):
70 3c33e68a Nikos Skalkotos
                break
71 09ed3d46 Nikos Skalkotos
72 3c33e68a Nikos Skalkotos
            if idx < 0:
73 3c33e68a Nikos Skalkotos
                return False
74 3c33e68a Nikos Skalkotos
        return True
75 09ed3d46 Nikos Skalkotos
76 09ed3d46 Nikos Skalkotos
77 3c33e68a Nikos Skalkotos
class WizardPage:
78 3c33e68a Nikos Skalkotos
    NEXT = 1
79 3c33e68a Nikos Skalkotos
    PREV = -1
80 fbdf1d8f Nikos Skalkotos
81 fbdf1d8f Nikos Skalkotos
    def run(self, session, index, total):
82 fbdf1d8f Nikos Skalkotos
        raise NotImplementedError
83 fbdf1d8f Nikos Skalkotos
84 fbdf1d8f Nikos Skalkotos
85 37ee0098 Nikos Skalkotos
class WizardRadioListPage(WizardPage):
86 37ee0098 Nikos Skalkotos
87 37ee0098 Nikos Skalkotos
    def __init__(self, name, message, choices, **kargs):
88 37ee0098 Nikos Skalkotos
        self.name = name
89 37ee0098 Nikos Skalkotos
        self.message = message
90 37ee0098 Nikos Skalkotos
        self.choices = choices
91 37ee0098 Nikos Skalkotos
        self.title = kargs['title'] if 'title' in kargs else ''
92 37ee0098 Nikos Skalkotos
        self.default = kargs['default'] if 'default' in kargs else 0
93 37ee0098 Nikos Skalkotos
94 37ee0098 Nikos Skalkotos
    def run(self, session, index, total):
95 37ee0098 Nikos Skalkotos
        d = session['dialog']
96 37ee0098 Nikos Skalkotos
        w = session['wizard']
97 37ee0098 Nikos Skalkotos
98 37ee0098 Nikos Skalkotos
        choices = []
99 37ee0098 Nikos Skalkotos
        for i in range(len(self.choices)):
100 37ee0098 Nikos Skalkotos
            default = 1 if i == self.default else 0
101 37ee0098 Nikos Skalkotos
            choices.append((self.choices[i][0], self.choices[i][1], default))
102 37ee0098 Nikos Skalkotos
103 37ee0098 Nikos Skalkotos
        while True:
104 023e1217 Nikos Skalkotos
            (code, answer) = \
105 023e1217 Nikos Skalkotos
                d.radiolist(self.message, width=PAGE_WIDTH,
106 023e1217 Nikos Skalkotos
                            ok_label="Next", cancel="Back", choices=choices,
107 023e1217 Nikos Skalkotos
                            title="(%d/%d) %s" % (index + 1, total, self.title)
108 023e1217 Nikos Skalkotos
                            )
109 37ee0098 Nikos Skalkotos
110 37ee0098 Nikos Skalkotos
            if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
111 37ee0098 Nikos Skalkotos
                return self.PREV
112 37ee0098 Nikos Skalkotos
113 37ee0098 Nikos Skalkotos
            for i in range(len(choices)):
114 37ee0098 Nikos Skalkotos
                if self.choices[i] == answer:
115 37ee0098 Nikos Skalkotos
                    self.default = i
116 37ee0098 Nikos Skalkotos
                    w[name] = i
117 37ee0098 Nikos Skalkotos
                    break
118 37ee0098 Nikos Skalkotos
119 37ee0098 Nikos Skalkotos
            return self.NEXT
120 37ee0098 Nikos Skalkotos
121 37ee0098 Nikos Skalkotos
122 fbdf1d8f Nikos Skalkotos
class WizardInputPage(WizardPage):
123 09ed3d46 Nikos Skalkotos
124 3c33e68a Nikos Skalkotos
    def __init__(self, name, message, **kargs):
125 3c33e68a Nikos Skalkotos
        self.name = name
126 3c33e68a Nikos Skalkotos
        self.message = message
127 3c33e68a Nikos Skalkotos
        self.title = kargs['title'] if 'title' in kargs else ''
128 3c33e68a Nikos Skalkotos
        self.init_value = kargs['init'] if 'init' in kargs else ''
129 3c33e68a Nikos Skalkotos
        self.allow_empty = kargs['empty'] if 'empty' in kargs else False
130 09ed3d46 Nikos Skalkotos
131 3c33e68a Nikos Skalkotos
    def run(self, session, index, total):
132 3c33e68a Nikos Skalkotos
        d = session['dialog']
133 3c33e68a Nikos Skalkotos
        w = session['wizard']
134 09ed3d46 Nikos Skalkotos
135 3c33e68a Nikos Skalkotos
        init = w[self.name] if self.name in w else self.init_value
136 3c33e68a Nikos Skalkotos
        while True:
137 023e1217 Nikos Skalkotos
            (code, answer) = \
138 023e1217 Nikos Skalkotos
                d.inputbox(self.message, init=init,
139 023e1217 Nikos Skalkotos
                           width=PAGE_WIDTH, ok_label="Next", cancel="Back",
140 023e1217 Nikos Skalkotos
                           title="(%d/%d) %s" % (index + 1, total, self.title))
141 09ed3d46 Nikos Skalkotos
142 09ed3d46 Nikos Skalkotos
            if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
143 09ed3d46 Nikos Skalkotos
                return self.PREV
144 09ed3d46 Nikos Skalkotos
145 3c33e68a Nikos Skalkotos
            value = answer.strip()
146 3c33e68a Nikos Skalkotos
            if len(value) == 0 and self.allow_empty is False:
147 3c33e68a Nikos Skalkotos
                d.msgbox("The value cannot be empty!", width=PAGE_WIDTH)
148 3c33e68a Nikos Skalkotos
                continue
149 3c33e68a Nikos Skalkotos
            w[self.name] = value
150 09ed3d46 Nikos Skalkotos
            break
151 09ed3d46 Nikos Skalkotos
152 09ed3d46 Nikos Skalkotos
        return self.NEXT
153 09ed3d46 Nikos Skalkotos
154 09ed3d46 Nikos Skalkotos
155 fbdf1d8f Nikos Skalkotos
class WizardYesNoPage(WizardPage):
156 fbdf1d8f Nikos Skalkotos
157 fbdf1d8f Nikos Skalkotos
    def __init__(self, message, **kargs):
158 fbdf1d8f Nikos Skalkotos
        self.message = message
159 fbdf1d8f Nikos Skalkotos
        self.title = kargs['title'] if 'title' in kargs else ''
160 fbdf1d8f Nikos Skalkotos
161 fbdf1d8f Nikos Skalkotos
    def run(self, session, index, total):
162 fbdf1d8f Nikos Skalkotos
        d = session['dialog']
163 fbdf1d8f Nikos Skalkotos
164 fbdf1d8f Nikos Skalkotos
        while True:
165 fbdf1d8f Nikos Skalkotos
            ret = d.yesno(self.message, width=PAGE_WIDTH, ok_label="Yes",
166 023e1217 Nikos Skalkotos
                          cancel="Back", extra_button=1, extra_label="Quit",
167 023e1217 Nikos Skalkotos
                          title="(%d/%d) %s" % (index + 1, total, self.title))
168 fbdf1d8f Nikos Skalkotos
169 fbdf1d8f Nikos Skalkotos
            if ret == d.DIALOG_CANCEL:
170 fbdf1d8f Nikos Skalkotos
                return self.PREV
171 fbdf1d8f Nikos Skalkotos
            elif ret == d.DIALOG_EXTRA:
172 aeb95900 Nikos Skalkotos
                raise WizardExit
173 fbdf1d8f Nikos Skalkotos
            elif ret == d.DIALOG_OK:
174 fbdf1d8f Nikos Skalkotos
                return self.NEXT
175 fbdf1d8f Nikos Skalkotos
176 fbdf1d8f Nikos Skalkotos
177 09ed3d46 Nikos Skalkotos
def wizard(session):
178 8bd0cbb6 Nikos Skalkotos
    init_account = Kamaki.get_account()
179 8bd0cbb6 Nikos Skalkotos
    if init_account is None:
180 8bd0cbb6 Nikos Skalkotos
        init_account = ""
181 8bd0cbb6 Nikos Skalkotos
182 8bd0cbb6 Nikos Skalkotos
    init_token = Kamaki.get_token()
183 8bd0cbb6 Nikos Skalkotos
    if init_token is None:
184 8bd0cbb6 Nikos Skalkotos
        init_token = ""
185 09ed3d46 Nikos Skalkotos
186 fbdf1d8f Nikos Skalkotos
    name = WizardInputPage("ImageName", "Please provide a name for the image:",
187 023e1217 Nikos Skalkotos
                           title="Image Name", init=session['device'].distro)
188 fbdf1d8f Nikos Skalkotos
    descr = WizardInputPage("ImageDescription",
189 023e1217 Nikos Skalkotos
                            "Please provide a description for the image:",
190 023e1217 Nikos Skalkotos
                            title="Image Description", empty=True,
191 023e1217 Nikos Skalkotos
                            init=session['metadata']['DESCRIPTION'] if
192 023e1217 Nikos Skalkotos
                            'DESCRIPTION' in session['metadata'] else '')
193 fbdf1d8f Nikos Skalkotos
    account = WizardInputPage("account",
194 023e1217 Nikos Skalkotos
                              "Please provide your ~okeanos account e-mail:",
195 023e1217 Nikos Skalkotos
                              title="~okeanos account information",
196 8bd0cbb6 Nikos Skalkotos
                              init=init_account)
197 fbdf1d8f Nikos Skalkotos
    token = WizardInputPage("token",
198 023e1217 Nikos Skalkotos
                            "Please provide your ~okeanos account token:",
199 023e1217 Nikos Skalkotos
                            title="~okeanos account token",
200 8bd0cbb6 Nikos Skalkotos
                            init=init_token)
201 3c33e68a Nikos Skalkotos
202 a95930f8 Nikos Skalkotos
    msg = "All necessary information has been gathered. Confirm and Proceed."
203 fbdf1d8f Nikos Skalkotos
    proceed = WizardYesNoPage(msg, title="Confirmation")
204 fbdf1d8f Nikos Skalkotos
205 3c33e68a Nikos Skalkotos
    w = Wizard(session)
206 fbdf1d8f Nikos Skalkotos
207 3c33e68a Nikos Skalkotos
    w.add_page(name)
208 3c33e68a Nikos Skalkotos
    w.add_page(descr)
209 3c33e68a Nikos Skalkotos
    w.add_page(account)
210 3c33e68a Nikos Skalkotos
    w.add_page(token)
211 fbdf1d8f Nikos Skalkotos
    w.add_page(proceed)
212 fbdf1d8f Nikos Skalkotos
213 fbdf1d8f Nikos Skalkotos
    if w.run():
214 023e1217 Nikos Skalkotos
        create_image(session)
215 fbdf1d8f Nikos Skalkotos
    else:
216 fbdf1d8f Nikos Skalkotos
        return False
217 fbdf1d8f Nikos Skalkotos
218 fbdf1d8f Nikos Skalkotos
    return True
219 fbdf1d8f Nikos Skalkotos
220 fbdf1d8f Nikos Skalkotos
221 023e1217 Nikos Skalkotos
def create_image(session):
222 37ee0098 Nikos Skalkotos
    d = session['dialog']
223 fbdf1d8f Nikos Skalkotos
    disk = session['disk']
224 fbdf1d8f Nikos Skalkotos
    device = session['device']
225 fbdf1d8f Nikos Skalkotos
    snapshot = session['snapshot']
226 fbdf1d8f Nikos Skalkotos
    image_os = session['image_os']
227 fbdf1d8f Nikos Skalkotos
    wizard = session['wizard']
228 fbdf1d8f Nikos Skalkotos
229 8bd0cbb6 Nikos Skalkotos
    # Save Kamaki credentials
230 8bd0cbb6 Nikos Skalkotos
    Kamaki.save_account(wizard['account'])
231 8bd0cbb6 Nikos Skalkotos
    Kamaki.save_token(wizard['token'])
232 8bd0cbb6 Nikos Skalkotos
233 789a3763 Nikos Skalkotos
    with_progress = OutputWthProgress(True)
234 789a3763 Nikos Skalkotos
    out = disk.out
235 789a3763 Nikos Skalkotos
    out.add(with_progress)
236 789a3763 Nikos Skalkotos
    try:
237 789a3763 Nikos Skalkotos
        out.clear()
238 fbdf1d8f Nikos Skalkotos
239 789a3763 Nikos Skalkotos
        #Sysprep
240 789a3763 Nikos Skalkotos
        device.mount(False)
241 789a3763 Nikos Skalkotos
        image_os.do_sysprep()
242 789a3763 Nikos Skalkotos
        metadata = image_os.meta
243 789a3763 Nikos Skalkotos
        device.umount()
244 1d413d1e Nikos Skalkotos
245 789a3763 Nikos Skalkotos
        #Shrink
246 789a3763 Nikos Skalkotos
        size = device.shrink()
247 3793498a Nikos Skalkotos
        session['shrinked'] = True
248 023e1217 Nikos Skalkotos
        update_background_title(session)
249 fbdf1d8f Nikos Skalkotos
250 789a3763 Nikos Skalkotos
        metadata.update(device.meta)
251 789a3763 Nikos Skalkotos
        metadata['DESCRIPTION'] = wizard['ImageDescription']
252 fbdf1d8f Nikos Skalkotos
253 789a3763 Nikos Skalkotos
        #MD5
254 789a3763 Nikos Skalkotos
        md5 = MD5(out)
255 789a3763 Nikos Skalkotos
        session['checksum'] = md5.compute(snapshot, size)
256 fbdf1d8f Nikos Skalkotos
257 789a3763 Nikos Skalkotos
        #Metadata
258 789a3763 Nikos Skalkotos
        metastring = '\n'.join(
259 789a3763 Nikos Skalkotos
            ['%s=%s' % (key, value) for (key, value) in metadata.items()])
260 789a3763 Nikos Skalkotos
        metastring += '\n'
261 fbdf1d8f Nikos Skalkotos
262 fbdf1d8f Nikos Skalkotos
        out.output()
263 789a3763 Nikos Skalkotos
        try:
264 789a3763 Nikos Skalkotos
            out.output("Uploading image to pithos:")
265 789a3763 Nikos Skalkotos
            kamaki = Kamaki(wizard['account'], wizard['token'], out)
266 789a3763 Nikos Skalkotos
267 789a3763 Nikos Skalkotos
            name = "%s-%s.diskdump" % (wizard['ImageName'],
268 789a3763 Nikos Skalkotos
                                       time.strftime("%Y%m%d%H%M"))
269 789a3763 Nikos Skalkotos
            pithos_file = ""
270 789a3763 Nikos Skalkotos
            with open(snapshot, 'rb') as f:
271 789a3763 Nikos Skalkotos
                pithos_file = kamaki.upload(f, size, name,
272 023e1217 Nikos Skalkotos
                                            "(1/4)  Calculating block hashes",
273 023e1217 Nikos Skalkotos
                                            "(2/4)  Uploading missing blocks")
274 789a3763 Nikos Skalkotos
275 663f5f80 Nikos Skalkotos
            out.output("(3/4)  Uploading metadata file ...", False)
276 789a3763 Nikos Skalkotos
            kamaki.upload(StringIO.StringIO(metastring), size=len(metastring),
277 789a3763 Nikos Skalkotos
                          remote_path="%s.%s" % (name, 'meta'))
278 789a3763 Nikos Skalkotos
            out.success('done')
279 663f5f80 Nikos Skalkotos
            out.output("(4/4)  Uploading md5sum file ...", False)
280 789a3763 Nikos Skalkotos
            md5sumstr = '%s %s\n' % (session['checksum'], name)
281 789a3763 Nikos Skalkotos
            kamaki.upload(StringIO.StringIO(md5sumstr), size=len(md5sumstr),
282 789a3763 Nikos Skalkotos
                          remote_path="%s.%s" % (name, 'md5sum'))
283 789a3763 Nikos Skalkotos
            out.success('done')
284 789a3763 Nikos Skalkotos
            out.output()
285 789a3763 Nikos Skalkotos
286 663f5f80 Nikos Skalkotos
            out.output('Registering image with ~okeanos ...', False)
287 789a3763 Nikos Skalkotos
            kamaki.register(wizard['ImageName'], pithos_file, metadata)
288 789a3763 Nikos Skalkotos
            out.success('done')
289 789a3763 Nikos Skalkotos
            out.output()
290 789a3763 Nikos Skalkotos
291 789a3763 Nikos Skalkotos
        except ClientError as e:
292 789a3763 Nikos Skalkotos
            raise FatalError("Pithos client: %d %s" % (e.status, e.message))
293 789a3763 Nikos Skalkotos
    finally:
294 789a3763 Nikos Skalkotos
        out.remove(with_progress)
295 09ed3d46 Nikos Skalkotos
296 dcf9274b Vangelis Koukis
    msg = "The image was successfully uploaded and registered with " \
297 37ee0098 Nikos Skalkotos
          "~okeanos. Would you like to keep a local copy of the image?"
298 37ee0098 Nikos Skalkotos
    if not d.yesno(msg, width=PAGE_WIDTH):
299 023e1217 Nikos Skalkotos
        extract_image(session)
300 37ee0098 Nikos Skalkotos
301 09ed3d46 Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :