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