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