blob: 2b7fd549eb65d269e067962ba48813c595080c7d [file] [log] [blame]
lmr6f669ce2009-05-31 19:02:42 +00001#!/usr/bin/env python
2import pygtk, gtk, gobject, time, os, commands
3from autotest_lib.client.common_lib import error
4import kvm_utils, logging, ppm_utils, stepeditor
5pygtk.require('2.0')
6"""
7Step file creator/editor.
8
9@copyright: Red Hat Inc 2009
10@author: mgoldish@redhat.com (Michael Goldish)
11@version: "20090401"
12"""
13
14class StepMaker(stepeditor.StepMakerWindow):
15 """
16 Application used to create a step file. It will grab your input to the
17 virtual machine and record it on a 'step file', that can be played
18 making it possible to do unattended installs.
19 """
20 # Constructor
21 def __init__(self, vm, steps_filename, tempdir, params):
22 stepeditor.StepMakerWindow.__init__(self)
23
24 self.vm = vm
25 self.steps_filename = steps_filename
26 self.steps_data_dir = ppm_utils.get_data_dir(steps_filename)
27 self.tempdir = tempdir
28 self.screendump_filename = os.path.join(tempdir, "scrdump.ppm")
29 self.params = params
30
31 if not os.path.exists(self.steps_data_dir):
32 os.makedirs(self.steps_data_dir)
33
34 self.steps_file = open(self.steps_filename, "w")
35 self.vars_file = open(os.path.join(self.steps_data_dir, "vars"), "w")
36
37 self.step_num = 1
38 self.run_time = 0
39 self.update_delay = 1000
40 self.prev_x = 0
41 self.prev_y = 0
42 self.vars = {}
43 self.timer_id = None
44
45 self.time_when_done_clicked = time.time()
46 self.time_when_actions_completed = time.time()
47
48 self.steps_file.write("# Generated by Step Maker version %s\n" %
49 version)
50 self.steps_file.write("# Generated on %s\n" % time.asctime())
51 self.steps_file.write("# uname -a: %s\n" %
52 commands.getoutput("uname -a"))
53 self.steps_file.flush()
54
55 self.vars_file.write("# This file lists the vars used during recording"
56 " with Step Maker\n")
57 self.vars_file.flush()
58
59 # Done/Break HBox
60 hbox = gtk.HBox(spacing=10)
61 self.user_vbox.pack_start(hbox)
62 hbox.show()
63
64 self.button_break = gtk.Button("Break")
65 self.button_break.connect("clicked", self.event_break_clicked)
66 hbox.pack_start(self.button_break)
67 self.button_break.show()
68
69 self.button_done = gtk.Button("Done")
70 self.button_done.connect("clicked", self.event_done_clicked)
71 hbox.pack_start(self.button_done)
72 self.button_done.show()
73
74 # Set window title
75 self.window.set_title("Step Maker")
76
77 # Connect "capture" button
78 self.button_capture.connect("clicked", self.event_capture_clicked)
79
80 # Switch to run mode
81 self.switch_to_run_mode()
82
83
84 def destroy(self, widget):
85 self.vm.send_monitor_cmd("cont")
86 self.steps_file.close()
87 self.vars_file.close()
88 stepeditor.StepMakerWindow.destroy(self, widget)
89
90
91 # Utilities
92 def redirect_timer(self, delay=0, func=None):
93 if self.timer_id != None:
94 gobject.source_remove(self.timer_id)
95 self.timer_id = None
96 if func != None:
97 self.timer_id = gobject.timeout_add(delay, func,
98 priority=gobject.PRIORITY_LOW)
99
100
101 def switch_to_run_mode(self):
102 # Set all widgets to their default states
103 self.clear_state(clear_screendump=False)
104 # Enable/disable some widgets
105 self.button_break.set_sensitive(True)
106 self.button_done.set_sensitive(False)
107 self.data_vbox.set_sensitive(False)
108 # Give focus to the Break button
109 self.button_break.grab_focus()
110 # Start the screendump timer
111 self.redirect_timer(100, self.update)
112 # Resume the VM
113 self.vm.send_monitor_cmd("cont")
114
115
116 def switch_to_step_mode(self):
117 # Set all widgets to their default states
118 self.clear_state(clear_screendump=False)
119 # Enable/disable some widgets
120 self.button_break.set_sensitive(False)
121 self.button_done.set_sensitive(True)
122 self.data_vbox.set_sensitive(True)
123 # Give focus to the keystrokes entry widget
124 self.entry_keys.grab_focus()
125 # Start the screendump timer
126 self.redirect_timer()
127 # Stop the VM
128 self.vm.send_monitor_cmd("stop")
129
130
131 # Events in step mode
132 def update(self):
133 self.redirect_timer()
134
135 if os.path.exists(self.screendump_filename):
136 os.unlink(self.screendump_filename)
137
138 (status, output) = self.vm.send_monitor_cmd("screendump " +
139 self.screendump_filename)
140 if status: # Failure
141 logging.info("Could not fetch screendump")
142 else:
143 self.set_image_from_file(self.screendump_filename)
144
145 self.redirect_timer(self.update_delay, self.update)
146 return True
147
148
149 def event_break_clicked(self, widget):
150 if not self.vm.is_alive():
151 self.message("The VM doesn't seem to be alive.", "Error")
152 return
153 # Switch to step mode
154 self.switch_to_step_mode()
155 # Compute time elapsed since last click on "Done" and add it
156 # to self.run_time
157 self.run_time += time.time() - self.time_when_done_clicked
158 # Set recording time widget
159 self.entry_time.set_text("%.2f" % self.run_time)
160 # Update screendump ID
161 self.update_screendump_id(self.steps_data_dir)
162 # By default, check the barrier checkbox
163 self.check_barrier.set_active(True)
164 # Set default sleep and barrier timeout durations
165 time_delta = time.time() - self.time_when_actions_completed
166 if time_delta < 1.0: time_delta = 1.0
167 self.spin_sleep.set_value(round(time_delta))
168 self.spin_barrier_timeout.set_value(round(time_delta * 5))
169 # Set window title
170 self.window.set_title("Step Maker -- step %d at time %.2f" %
171 (self.step_num, self.run_time))
172
173
174 def event_done_clicked(self, widget):
175 # Get step lines and screendump
176 lines = self.get_step_lines(self.steps_data_dir)
177 if lines == None:
178 return
179
180 # Get var values from user and write them to vars file
181 vars = {}
182 for line in lines.splitlines():
183 words = line.split()
184 if words and words[0] == "var":
185 varname = words[1]
186 if varname in self.vars.keys():
187 val = self.vars[varname]
188 elif varname in vars.keys():
189 val = vars[varname]
190 elif varname in self.params.keys():
191 val = self.params[varname]
192 vars[varname] = val
193 else:
194 val = self.inputdialog("$%s =" % varname, "Variable")
195 if val == None:
196 return
197 vars[varname] = val
198 for varname in vars.keys():
199 self.vars_file.write("%s=%s\n" % (varname, vars[varname]))
200 self.vars.update(vars)
201
202 # Write step lines to file
203 self.steps_file.write("# " + "-" * 32 + "\n")
204 self.steps_file.write(lines)
205
206 # Flush buffers of both files
207 self.steps_file.flush()
208 self.vars_file.flush()
209
210 # Remember the current time
211 self.time_when_done_clicked = time.time()
212
213 # Switch to run mode
214 self.switch_to_run_mode()
215
216 # Send commands to VM
217 for line in lines.splitlines():
218 words = line.split()
219 if not words:
220 continue
221 elif words[0] == "key":
222 self.vm.send_key(words[1])
223 elif words[0] == "var":
224 val = self.vars.get(words[1])
225 if not val:
226 continue
227 self.vm.send_string(val)
228 elif words[0] == "mousemove":
229 self.vm.send_monitor_cmd("mouse_move %d %d" % (-8000,-8000))
230 time.sleep(0.5)
231 self.vm.send_monitor_cmd("mouse_move %s %s" % (words[1],
232 words[2]))
233 time.sleep(0.5)
234 elif words[0] == "mouseclick":
235 self.vm.send_monitor_cmd("mouse_button %s" % words[1])
236 time.sleep(0.1)
237 self.vm.send_monitor_cmd("mouse_button 0")
238
239 # Remember the current time
240 self.time_when_actions_completed = time.time()
241
242 # Move on to next step
243 self.step_num += 1
244
245 def event_capture_clicked(self, widget):
246 self.message("Mouse actions disabled (for now).", "Sorry")
247 return
248
249 self.image_width_backup = self.image_width
250 self.image_height_backup = self.image_height
251 self.image_data_backup = self.image_data
252
253 gtk.gdk.pointer_grab(self.event_box.window, False,
254 gtk.gdk.BUTTON_PRESS_MASK |
255 gtk.gdk.BUTTON_RELEASE_MASK)
256 # Create empty cursor
257 pix = gtk.gdk.Pixmap(self.event_box.window, 1, 1, 1)
258 color = gtk.gdk.Color()
259 cursor = gtk.gdk.Cursor(pix, pix, color, color, 0, 0)
260 self.event_box.window.set_cursor(cursor)
261 gtk.gdk.display_get_default().warp_pointer(gtk.gdk.screen_get_default(),
262 self.prev_x, self.prev_y)
263 self.redirect_event_box_input(
264 self.event_capture_button_press,
265 self.event_capture_button_release,
266 self.event_capture_scroll)
267 self.redirect_timer(10, self.update_capture)
268 self.vm.send_monitor_cmd("cont")
269
270 # Events in mouse capture mode
271
272 def update_capture(self):
273 self.redirect_timer()
274
275 (screen, x, y, flags) = gtk.gdk.display_get_default().get_pointer()
276 self.mouse_click_coords[0] = int(x * self.spin_sensitivity.get_value())
277 self.mouse_click_coords[1] = int(y * self.spin_sensitivity.get_value())
278
279 delay = self.spin_latency.get_value() / 1000
280 if (x, y) != (self.prev_x, self.prev_y):
281 self.vm.send_monitor_cmd("mouse_move %d %d" % (-8000, -8000))
282 time.sleep(delay)
283 self.vm.send_monitor_cmd("mouse_move %d %d" %
284 (self.mouse_click_coords[0],
285 self.mouse_click_coords[1]))
286 time.sleep(delay)
287
288 self.prev_x = x
289 self.prev_y = y
290
291 if os.path.exists(self.screendump_filename):
292 os.unlink(self.screendump_filename)
293
294 (status, output) = self.vm.send_monitor_cmd("screendump " +
295 self.screendump_filename)
296 if status: # Failure
297 logging.info("Could not fetch screendump")
298 else:
299 self.set_image_from_file(self.screendump_filename)
300
301 self.redirect_timer(int(self.spin_latency.get_value()),
302 self.update_capture)
303 return True
304
305 def event_capture_button_press(self, widget,event):
306 pass
307
308 def event_capture_button_release(self, widget,event):
309 gtk.gdk.pointer_ungrab()
310 self.event_box.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.CROSSHAIR))
311 self.redirect_event_box_input(
312 self.event_button_press,
313 self.event_button_release,
314 None,
315 None,
316 self.event_expose)
317 self.redirect_timer()
318 self.vm.send_monitor_cmd("stop")
319 self.mouse_click_captured = True
320 self.mouse_click_button = event.button
321 self.set_image(self.image_width_backup, self.image_height_backup,
322 self.image_data_backup)
323 self.check_mousemove.set_sensitive(True)
324 self.check_mouseclick.set_sensitive(True)
325 self.check_mousemove.set_active(True)
326 self.check_mouseclick.set_active(True)
327 self.update_mouse_click_info()
328
329 def event_capture_scroll(self, widget, event):
330 if event.direction == gtk.gdk.SCROLL_UP:
331 direction = 1
332 else:
333 direction = -1
334 self.spin_sensitivity.set_value(self.spin_sensitivity.get_value() +
335 direction)
336 pass
337
338
339def run_stepmaker(test, params, env):
340 vm = kvm_utils.env_get_vm(env, params.get("main_vm"))
341 if not vm:
342 raise error.TestError("VM object not found in environment")
343 if not vm.is_alive():
344 raise error.TestError("VM seems to be dead; Step Maker requires a"
345 " living VM")
346
347 steps_filename = params.get("steps")
348 if not steps_filename:
349 raise error.TestError("Steps filename not specified")
350 steps_filename = os.path.join(test.bindir, "steps", steps_filename)
351 if os.path.exists(steps_filename):
352 raise error.TestError("Steps file %s already exists" % steps_filename)
353
354 StepMaker(vm, steps_filename, test.debugdir, params)
355 gtk.main()