OLD | NEW |
1 """ | 1 """ |
2 High-level KVM test utility functions. | 2 High-level KVM test utility functions. |
3 | 3 |
4 This module is meant to reduce code size by performing common test procedures. | 4 This module is meant to reduce code size by performing common test procedures. |
5 Generally, code here should look like test code. | 5 Generally, code here should look like test code. |
6 More specifically: | 6 More specifically: |
7 - Functions in this module should raise exceptions if things go wrong | 7 - Functions in this module should raise exceptions if things go wrong |
8 (unlike functions in kvm_utils.py and kvm_vm.py which report failure via | 8 (unlike functions in kvm_utils.py and kvm_vm.py which report failure via |
9 their returned values). | 9 their returned values). |
10 - Functions in this module may use logging.info(), in addition to | 10 - Functions in this module may use logging.info(), in addition to |
11 logging.debug() and logging.error(), to log messages the user may be | 11 logging.debug() and logging.error(), to log messages the user may be |
12 interested in (unlike kvm_utils.py and kvm_vm.py which use | 12 interested in (unlike kvm_utils.py and kvm_vm.py which use |
13 logging.debug() for anything that isn't an error). | 13 logging.debug() for anything that isn't an error). |
14 - Functions in this module typically use functions and classes from | 14 - Functions in this module typically use functions and classes from |
15 lower-level modules (e.g. kvm_utils.py, kvm_vm.py, kvm_subprocess.py). | 15 lower-level modules (e.g. kvm_utils.py, kvm_vm.py, kvm_subprocess.py). |
16 - Functions in this module should not be used by lower-level modules. | 16 - Functions in this module should not be used by lower-level modules. |
17 - Functions in this module should be used in the right context. | 17 - Functions in this module should be used in the right context. |
18 For example, a function should not be used where it may display | 18 For example, a function should not be used where it may display |
19 misleading or inaccurate info or debug messages. | 19 misleading or inaccurate info or debug messages. |
20 | 20 |
21 @copyright: 2008-2009 Red Hat Inc. | 21 @copyright: 2008-2009 Red Hat Inc. |
22 """ | 22 """ |
23 | 23 |
24 import time, os, logging, re, commands | 24 import time, os, logging, re, commands, signal |
25 from autotest_lib.client.common_lib import error | 25 from autotest_lib.client.common_lib import error |
26 from autotest_lib.client.bin import utils | 26 from autotest_lib.client.bin import utils |
27 import kvm_utils, kvm_vm, kvm_subprocess, scan_results | 27 import kvm_utils, kvm_vm, kvm_subprocess, scan_results |
28 | 28 |
29 | 29 |
30 def get_living_vm(env, vm_name): | 30 def get_living_vm(env, vm_name): |
31 """ | 31 """ |
32 Get a VM object from the environment and make sure it's alive. | 32 Get a VM object from the environment and make sure it's alive. |
33 | 33 |
34 @param env: Dictionary with test environment. | 34 @param env: Dictionary with test environment. |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
150 def mig_failed(): | 150 def mig_failed(): |
151 o = vm.monitor.info("migrate") | 151 o = vm.monitor.info("migrate") |
152 if isinstance(o, str): | 152 if isinstance(o, str): |
153 return "status: failed" in o | 153 return "status: failed" in o |
154 else: | 154 else: |
155 return o.get("status") == "failed" | 155 return o.get("status") == "failed" |
156 | 156 |
157 def mig_cancelled(): | 157 def mig_cancelled(): |
158 o = vm.monitor.info("migrate") | 158 o = vm.monitor.info("migrate") |
159 if isinstance(o, str): | 159 if isinstance(o, str): |
160 return "Migration status: cancelled" in o | 160 return ("Migration status: cancelled" in o or |
| 161 "Migration status: canceled" in o) |
161 else: | 162 else: |
162 return o.get("status") == "cancelled" | 163 return (o.get("status") == "cancelled" or |
| 164 o.get("status") == "canceled") |
163 | 165 |
164 def wait_for_migration(): | 166 def wait_for_migration(): |
165 if not kvm_utils.wait_for(mig_finished, mig_timeout, 2, 2, | 167 if not kvm_utils.wait_for(mig_finished, mig_timeout, 2, 2, |
166 "Waiting for migration to finish..."): | 168 "Waiting for migration to finish..."): |
167 raise error.TestFail("Timeout expired while waiting for migration " | 169 raise error.TestFail("Timeout expired while waiting for migration " |
168 "to finish") | 170 "to finish") |
169 | 171 |
| 172 dest_vm = vm.clone() |
170 | 173 |
171 migration_file = os.path.join("/tmp/", | 174 if mig_protocol == "exec": |
172 mig_protocol + time.strftime("%Y%m%d-%H%M%S")) | |
173 if mig_protocol == "tcp": | |
174 mig_extra_params = " -incoming tcp:0:%d" | |
175 elif mig_protocol == "unix": | |
176 mig_extra_params = " -incoming unix:%s" | |
177 elif mig_protocol == "exec": | |
178 # Exec is a little different from other migrate methods - first we | 175 # Exec is a little different from other migrate methods - first we |
179 # ask the monitor the migration, then the vm state is dumped to a | 176 # ask the monitor the migration, then the vm state is dumped to a |
180 # compressed file, then we start the dest vm with -incoming pointing | 177 # compressed file, then we start the dest vm with -incoming pointing |
181 # to it | 178 # to it |
182 mig_extra_params = " -incoming \"exec: gzip -c -d %s\"" % migration_file | 179 try: |
183 uri = "\"exec:gzip -c > %s\"" % migration_file | 180 exec_file = "/tmp/exec-%s.gz" % kvm_utils.generate_random_string(8) |
184 vm.monitor.cmd("stop") | 181 exec_cmd = "gzip -c -d %s" % exec_file |
185 o = vm.monitor.migrate(uri) | 182 uri = '"exec:gzip -c > %s"' % exec_file |
186 wait_for_migration() | 183 vm.monitor.cmd("stop") |
| 184 vm.monitor.migrate(uri) |
| 185 wait_for_migration() |
187 | 186 |
188 # Clone the source VM and ask the clone to wait for incoming migration | 187 if not dest_vm.create(migration_mode=mig_protocol, |
189 dest_vm = vm.clone() | 188 migration_exec_cmd=exec_cmd, mac_source=vm): |
190 if not dest_vm.create(extra_params=mig_extra_params): | 189 raise error.TestError("Could not create dest VM") |
191 raise error.TestError("Could not create dest VM") | 190 finally: |
| 191 logging.debug("Removing migration file %s", exec_file) |
| 192 try: |
| 193 os.remove(exec_file) |
| 194 except OSError: |
| 195 pass |
| 196 else: |
| 197 if not dest_vm.create(migration_mode=mig_protocol, mac_source=vm): |
| 198 raise error.TestError("Could not create dest VM") |
| 199 try: |
| 200 if mig_protocol == "tcp": |
| 201 uri = "tcp:localhost:%d" % dest_vm.migration_port |
| 202 elif mig_protocol == "unix": |
| 203 uri = "unix:%s" % dest_vm.migration_file |
| 204 vm.monitor.migrate(uri) |
192 | 205 |
193 try: | 206 if mig_cancel: |
194 if mig_protocol == "tcp": | |
195 uri = "tcp:localhost:%d" % dest_vm.migration_port | |
196 elif mig_protocol == "unix": | |
197 uri = "unix:%s" % dest_vm.migration_file | |
198 | |
199 if mig_protocol != "exec": | |
200 o = vm.monitor.migrate(uri) | |
201 | |
202 if mig_protocol == "tcp" and mig_cancel: | |
203 time.sleep(2) | 207 time.sleep(2) |
204 o = vm.monitor.cmd("migrate_cancel") | 208 vm.monitor.cmd("migrate_cancel") |
205 if not kvm_utils.wait_for(mig_cancelled, 60, 2, 2, | 209 if not kvm_utils.wait_for(mig_cancelled, 60, 2, 2, |
206 "Waiting for migration cancel"): | 210 "Waiting for migration " |
207 raise error.TestFail("Fail to cancel migration") | 211 "cancellation"): |
| 212 raise error.TestFail("Failed to cancel migration") |
208 dest_vm.destroy(gracefully=False) | 213 dest_vm.destroy(gracefully=False) |
209 return vm | 214 return vm |
| 215 else: |
| 216 wait_for_migration() |
| 217 except: |
| 218 dest_vm.destroy() |
| 219 raise |
210 | 220 |
211 wait_for_migration() | 221 # Report migration status |
| 222 if mig_succeeded(): |
| 223 logging.info("Migration finished successfully") |
| 224 elif mig_failed(): |
| 225 raise error.TestFail("Migration failed") |
| 226 else: |
| 227 raise error.TestFail("Migration ended with unknown status") |
212 | 228 |
213 # Report migration status | 229 if "paused" in dest_vm.monitor.info("status"): |
214 if mig_succeeded(): | 230 logging.debug("Destination VM is paused, resuming it...") |
215 logging.info("Migration finished successfully") | 231 dest_vm.monitor.cmd("cont") |
216 elif mig_failed(): | |
217 raise error.TestFail("Migration failed") | |
218 else: | |
219 raise error.TestFail("Migration ended with unknown status") | |
220 | 232 |
221 o = dest_vm.monitor.info("status") | 233 # Kill the source VM |
222 if "paused" in o: | 234 vm.destroy(gracefully=False) |
223 logging.debug("Destination VM is paused, resuming it...") | |
224 dest_vm.monitor.cmd("cont") | |
225 | 235 |
226 if os.path.exists(migration_file): | 236 # Replace the source VM with the new cloned VM |
227 logging.debug("Removing migration file %s", migration_file) | 237 if env is not None: |
228 os.remove(migration_file) | 238 kvm_utils.env_register_vm(env, vm.name, dest_vm) |
229 | 239 |
230 # Kill the source VM | 240 # Return the new cloned VM |
231 vm.destroy(gracefully=False) | 241 return dest_vm |
232 | 242 |
233 # Replace the source VM with the new cloned VM | |
234 if env is not None: | |
235 kvm_utils.env_register_vm(env, vm.name, dest_vm) | |
236 | 243 |
237 # Return the new cloned VM | 244 def stop_windows_service(session, service, timeout=120): |
238 return dest_vm | 245 """ |
| 246 Stop a Windows service using sc. |
| 247 If the service is already stopped or is not installed, do nothing. |
239 | 248 |
240 except: | 249 @param service: The name of the service |
241 dest_vm.destroy() | 250 @param timeout: Time duration to wait for service to stop |
242 raise | 251 @raise error.TestError: Raised if the service can't be stopped |
| 252 """ |
| 253 end_time = time.time() + timeout |
| 254 while time.time() < end_time: |
| 255 o = session.get_command_output("sc stop %s" % service, timeout=60) |
| 256 # FAILED 1060 means the service isn't installed. |
| 257 # FAILED 1062 means the service hasn't been started. |
| 258 if re.search(r"\bFAILED (1060|1062)\b", o, re.I): |
| 259 break |
| 260 time.sleep(1) |
| 261 else: |
| 262 raise error.TestError("Could not stop service '%s'" % service) |
| 263 |
| 264 |
| 265 def start_windows_service(session, service, timeout=120): |
| 266 """ |
| 267 Start a Windows service using sc. |
| 268 If the service is already running, do nothing. |
| 269 If the service isn't installed, fail. |
| 270 |
| 271 @param service: The name of the service |
| 272 @param timeout: Time duration to wait for service to start |
| 273 @raise error.TestError: Raised if the service can't be started |
| 274 """ |
| 275 end_time = time.time() + timeout |
| 276 while time.time() < end_time: |
| 277 o = session.get_command_output("sc start %s" % service, timeout=60) |
| 278 # FAILED 1060 means the service isn't installed. |
| 279 if re.search(r"\bFAILED 1060\b", o, re.I): |
| 280 raise error.TestError("Could not start service '%s' " |
| 281 "(service not installed)" % service) |
| 282 # FAILED 1056 means the service is already running. |
| 283 if re.search(r"\bFAILED 1056\b", o, re.I): |
| 284 break |
| 285 time.sleep(1) |
| 286 else: |
| 287 raise error.TestError("Could not start service '%s'" % service) |
243 | 288 |
244 | 289 |
245 def get_time(session, time_command, time_filter_re, time_format): | 290 def get_time(session, time_command, time_filter_re, time_format): |
246 """ | 291 """ |
247 Return the host time and guest time. If the guest time cannot be fetched | 292 Return the host time and guest time. If the guest time cannot be fetched |
248 a TestError exception is raised. | 293 a TestError exception is raised. |
249 | 294 |
250 Note that the shell session should be ready to receive commands | 295 Note that the shell session should be ready to receive commands |
251 (i.e. should "display" a command prompt and should be done with all | 296 (i.e. should "display" a command prompt and should be done with all |
252 previous commands). | 297 previous commands). |
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 raise error.TestFail("Autotest control file run did not produce any " | 543 raise error.TestFail("Autotest control file run did not produce any " |
499 "recognizable results") | 544 "recognizable results") |
500 if bad_results: | 545 if bad_results: |
501 if len(bad_results) == 1: | 546 if len(bad_results) == 1: |
502 e_msg = ("Test %s failed during control file execution" % | 547 e_msg = ("Test %s failed during control file execution" % |
503 bad_results[0]) | 548 bad_results[0]) |
504 else: | 549 else: |
505 e_msg = ("Tests %s failed during control file execution" % | 550 e_msg = ("Tests %s failed during control file execution" % |
506 " ".join(bad_results)) | 551 " ".join(bad_results)) |
507 raise error.TestFail(e_msg) | 552 raise error.TestFail(e_msg) |
| 553 |
| 554 |
| 555 def get_loss_ratio(output): |
| 556 """ |
| 557 Get the packet loss ratio from the output of ping |
| 558 . |
| 559 @param output: Ping output. |
| 560 """ |
| 561 try: |
| 562 return int(re.findall('(\d+)% packet loss', output)[0]) |
| 563 except IndexError: |
| 564 logging.debug(output) |
| 565 return -1 |
| 566 |
| 567 |
| 568 def raw_ping(command, timeout, session, output_func): |
| 569 """ |
| 570 Low-level ping command execution. |
| 571 |
| 572 @param command: Ping command. |
| 573 @param timeout: Timeout of the ping command. |
| 574 @param session: Local executon hint or session to execute the ping command. |
| 575 """ |
| 576 if session is None: |
| 577 process = kvm_subprocess.run_bg(command, output_func=output_func, |
| 578 timeout=timeout) |
| 579 |
| 580 # Send SIGINT signal to notify the timeout of running ping process, |
| 581 # Because ping have the ability to catch the SIGINT signal so we can |
| 582 # always get the packet loss ratio even if timeout. |
| 583 if process.is_alive(): |
| 584 kvm_utils.kill_process_tree(process.get_pid(), signal.SIGINT) |
| 585 |
| 586 status = process.get_status() |
| 587 output = process.get_output() |
| 588 |
| 589 process.close() |
| 590 return status, output |
| 591 else: |
| 592 session.sendline(command) |
| 593 status, output = session.read_up_to_prompt(timeout=timeout, |
| 594 print_func=output_func) |
| 595 if not status: |
| 596 # Send ctrl+c (SIGINT) through ssh session |
| 597 session.send("\003") |
| 598 status, output2 = session.read_up_to_prompt(print_func=output_func) |
| 599 output += output2 |
| 600 if not status: |
| 601 # We also need to use this session to query the return value |
| 602 session.send("\003") |
| 603 |
| 604 session.sendline(session.status_test_command) |
| 605 s2, o2 = session.read_up_to_prompt() |
| 606 if not s2: |
| 607 status = -1 |
| 608 else: |
| 609 try: |
| 610 status = int(re.findall("\d+", o2)[0]) |
| 611 except: |
| 612 status = -1 |
| 613 |
| 614 return status, output |
| 615 |
| 616 |
| 617 def ping(dest=None, count=None, interval=None, interface=None, |
| 618 packetsize=None, ttl=None, hint=None, adaptive=False, |
| 619 broadcast=False, flood=False, timeout=0, |
| 620 output_func=logging.debug, session=None): |
| 621 """ |
| 622 Wrapper of ping. |
| 623 |
| 624 @param dest: Destination address. |
| 625 @param count: Count of icmp packet. |
| 626 @param interval: Interval of two icmp echo request. |
| 627 @param interface: Specified interface of the source address. |
| 628 @param packetsize: Packet size of icmp. |
| 629 @param ttl: IP time to live. |
| 630 @param hint: Path mtu discovery hint. |
| 631 @param adaptive: Adaptive ping flag. |
| 632 @param broadcast: Broadcast ping flag. |
| 633 @param flood: Flood ping flag. |
| 634 @param timeout: Timeout for the ping command. |
| 635 @param output_func: Function used to log the result of ping. |
| 636 @param session: Local executon hint or session to execute the ping command. |
| 637 """ |
| 638 if dest is not None: |
| 639 command = "ping %s " % dest |
| 640 else: |
| 641 command = "ping localhost " |
| 642 if count is not None: |
| 643 command += " -c %s" % count |
| 644 if interval is not None: |
| 645 command += " -i %s" % interval |
| 646 if interface is not None: |
| 647 command += " -I %s" % interface |
| 648 if packetsize is not None: |
| 649 command += " -s %s" % packetsize |
| 650 if ttl is not None: |
| 651 command += " -t %s" % ttl |
| 652 if hint is not None: |
| 653 command += " -M %s" % hint |
| 654 if adaptive: |
| 655 command += " -A" |
| 656 if broadcast: |
| 657 command += " -b" |
| 658 if flood: |
| 659 command += " -f -q" |
| 660 output_func = None |
| 661 |
| 662 return raw_ping(command, timeout, session, output_func) |
| 663 |
| 664 |
| 665 def get_linux_ifname(session, mac_address): |
| 666 """ |
| 667 Get the interface name through the mac address. |
| 668 |
| 669 @param session: session to the virtual machine |
| 670 @mac_address: the macaddress of nic |
| 671 """ |
| 672 |
| 673 output = session.get_command_output("ifconfig -a") |
| 674 |
| 675 try: |
| 676 ethname = re.findall("(\w+)\s+Link.*%s" % mac_address, output, |
| 677 re.IGNORECASE)[0] |
| 678 return ethname |
| 679 except: |
| 680 return None |
OLD | NEW |