Bump version to 0.2.8
[snf-image-creator] / image_creator / output / dialog.py
1 # Copyright 2012 GRNET S.A. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
5 # conditions are met:
6 #
7 #   1. Redistributions of source code must retain the above
8 #      copyright notice, this list of conditions and the following
9 #      disclaimer.
10 #
11 #   2. Redistributions in binary form must reproduce the above
12 #      copyright notice, this list of conditions and the following
13 #      disclaimer in the documentation and/or other materials
14 #      provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28 #
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
33
34 from image_creator.output import Output
35 import time
36 import fcntl
37
38
39 class GaugeOutput(Output):
40     """Output class implemented using dialog's gauge widget"""
41     def __init__(self, dialog, title, msg=''):
42         self.d = dialog
43         self.msg = msg
44         self.percent = 0
45         self.d.gauge_start(self.msg, title=title)
46
47         # Open pipe workaround. A fork will dublicate the open file descriptor.
48         # The FD_CLOEXEC flag makes sure that the gauge internal fd will be
49         # closed if execve is executed. This is needed because libguestfs will
50         # fork/exec the kvm process. If this fd stays open in the kvm process,
51         # then doing a gauge_stop will make this process wait forever for
52         # a dialog process that is blocked waiting for input from the kvm
53         # process's open file descriptor.
54         fd = self.d._gauge_process['stdin'].fileno()
55         flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0)
56         fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
57
58     def output(self, msg='', new_line=True):
59         """Print msg as normal output"""
60         self.msg = msg
61         self.percent = 0
62         self.d.gauge_update(self.percent, self.msg, update_text=True)
63         time.sleep(0.4)
64
65     def success(self, result, new_line=True):
66         """Print result after a successfull action"""
67         self.percent = 100
68         self.d.gauge_update(self.percent, "%s %s" % (self.msg, result),
69                             update_text=True)
70         time.sleep(0.4)
71
72     def warn(self, msg, new_line=True):
73         """Print a warning"""
74         self.d.gauge_update(self.percent, "%s Warning: %s" % (self.msg, msg),
75                             update_text=True)
76         time.sleep(0.4)
77
78     def cleanup(self):
79         """Cleanup the GaugeOutput instance"""
80         self.d.gauge_stop()
81
82     class _Progress(Output._Progress):
83         """Progress class for dialog's gauge widget"""
84         template = {
85             'default': '%(index)d/%(size)d',
86             'percent': '',
87             'b': "%(index)d/%(size)d B",
88             'kb': "%(index)d/%(size)d KB",
89             'mb': "%(index)d/%(size)d MB"
90         }
91
92         def __init__(self, size, title, bar_type='default'):
93             self.output.size = size
94             self.bar_type = bar_type
95             self.output.msg = "%s ..." % title
96             self.goto(0)
97
98         def _postfix(self):
99             return self.template[self.bar_type] % self.output.__dict__
100
101         def goto(self, dest):
102             """Move progress bar to a specific position"""
103             self.output.index = dest
104             self.output.percent = self.output.index * 100 // self.output.size
105             msg = "%s %s" % (self.output.msg, self._postfix())
106             self.output.d.gauge_update(self.output.percent, msg,
107                                        update_text=True)
108
109         def next(self):
110             """Move progress bar one step forward"""
111             self.goto(self.output.index + 1)
112
113
114 class InfoBoxOutput(Output):
115     """Output class implemented using dialog's infobox widget"""
116     def __init__(self, dialog, title, msg='', height=20, width=70):
117         self.d = dialog
118         self.title = title
119         self.msg = msg
120         self.width = width
121         self.height = height
122         self.d.infobox(self.msg, title=self.title)
123
124     def output(self, msg='', new_line=True):
125         """Print msg as normal output"""
126         nl = '\n' if new_line else ''
127         self.msg += "%s%s" % (msg, nl)
128         # If output is long, only output the last lines that fit in the box
129         lines = self.msg.splitlines()
130         h = self.height
131         display = self.msg if len(lines) <= h else "\n".join(lines[-h:])
132         self.d.infobox(display, title=self.title, height=self.height,
133                        width=self.width)
134
135     def success(self, result, new_line=True):
136         """Print result after an action is completed successfully"""
137         self.output(result, new_line)
138
139     def warn(self, msg, new_line=True):
140         """Print a warning message"""
141         self.output("Warning: %s" % msg, new_line)
142
143     def finalize(self):
144         """Finalize the output. After this is called, the InfoboxOutput
145         instance should be destroyed
146         """
147         self.d.msgbox(self.msg, title=self.title, height=(self.height + 2),
148                       width=self.width)
149
150 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :