1"""
2High-level KVM test utility functions.
3
4This module is meant to reduce code size by performing common test procedures.
5Generally, code here should look like test code.
6More specifically:
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
9      their returned values).
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
12      interested in (unlike kvm_utils.py and kvm_vm.py which use
13      logging.debug() for anything that isn't an error).
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).
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.
18      For example, a function should not be used where it may display
19      misleading or inaccurate info or debug messages.
20
21@copyright: 2008-2009 Red Hat Inc.
22"""
23
24import time, os, logging, re, signal
25from autotest_lib.client.common_lib import error
26from autotest_lib.client.bin import utils
27from autotest_lib.client.tools import scan_results
28import aexpect, virt_utils, virt_vm
29
30
31def get_living_vm(env, vm_name):
32    """
33    Get a VM object from the environment and make sure it's alive.
34
35    @param env: Dictionary with test environment.
36    @param vm_name: Name of the desired VM object.
37    @return: A VM object.
38    """
39    vm = env.get_vm(vm_name)
40    if not vm:
41        raise error.TestError("VM '%s' not found in environment" % vm_name)
42    if not vm.is_alive():
43        raise error.TestError("VM '%s' seems to be dead; test requires a "
44                              "living VM" % vm_name)
45    return vm
46
47
48def wait_for_login(vm, nic_index=0, timeout=240, start=0, step=2, serial=None):
49    """
50    Try logging into a VM repeatedly.  Stop on success or when timeout expires.
51
52    @param vm: VM object.
53    @param nic_index: Index of NIC to access in the VM.
54    @param timeout: Time to wait before giving up.
55    @param serial: Whether to use a serial connection instead of a remote
56            (ssh, rss) one.
57    @return: A shell session object.
58    """
59    end_time = time.time() + timeout
60    session = None
61    if serial:
62        type = 'serial'
63        logging.info("Trying to log into guest %s using serial connection,"
64                     " timeout %ds", vm.name, timeout)
65        time.sleep(start)
66        while time.time() < end_time:
67            try:
68                session = vm.serial_login()
69                break
70            except virt_utils.LoginError, e:
71                logging.debug(e)
72            time.sleep(step)
73    else:
74        type = 'remote'
75        logging.info("Trying to log into guest %s using remote connection,"
76                     " timeout %ds", vm.name, timeout)
77        time.sleep(start)
78        while time.time() < end_time:
79            try:
80                session = vm.login(nic_index=nic_index)
81                break
82            except (virt_utils.LoginError, virt_vm.VMError), e:
83                logging.debug(e)
84            time.sleep(step)
85    if not session:
86        raise error.TestFail("Could not log into guest %s using %s connection" %
87                             (vm.name, type))
88    logging.info("Logged into guest %s using %s connection", vm.name, type)
89    return session
90
91
92def reboot(vm, session, method="shell", sleep_before_reset=10, nic_index=0,
93           timeout=240):
94    """
95    Reboot the VM and wait for it to come back up by trying to log in until
96    timeout expires.
97
98    @param vm: VM object.
99    @param session: A shell session object.
100    @param method: Reboot method.  Can be "shell" (send a shell reboot
101            command) or "system_reset" (send a system_reset monitor command).
102    @param nic_index: Index of NIC to access in the VM, when logging in after
103            rebooting.
104    @param timeout: Time to wait before giving up (after rebooting).
105    @return: A new shell session object.
106    """
107    if method == "shell":
108        # Send a reboot command to the guest's shell
109        session.sendline(vm.get_params().get("reboot_command"))
110        logging.info("Reboot command sent. Waiting for guest to go down")
111    elif method == "system_reset":
112        # Sleep for a while before sending the command
113        time.sleep(sleep_before_reset)
114        # Clear the event list of all QMP monitors
115        monitors = [m for m in vm.monitors if m.protocol == "qmp"]
116        for m in monitors:
117            m.clear_events()
118        # Send a system_reset monitor command
119        vm.monitor.cmd("system_reset")
120        logging.info("Monitor command system_reset sent. Waiting for guest to "
121                     "go down")
122        # Look for RESET QMP events
123        time.sleep(1)
124        for m in monitors:
125            if not m.get_event("RESET"):
126                raise error.TestFail("RESET QMP event not received after "
127                                     "system_reset (monitor '%s')" % m.name)
128            else:
129                logging.info("RESET QMP event received")
130    else:
131        logging.error("Unknown reboot method: %s", method)
132
133    # Wait for the session to become unresponsive and close it
134    if not virt_utils.wait_for(lambda: not session.is_responsive(timeout=30),
135                              120, 0, 1):
136        raise error.TestFail("Guest refuses to go down")
137    session.close()
138
139    # Try logging into the guest until timeout expires
140    logging.info("Guest is down. Waiting for it to go up again, timeout %ds",
141                 timeout)
142    session = vm.wait_for_login(nic_index, timeout=timeout)
143    logging.info("Guest is up again")
144    return session
145
146
147def migrate(vm, env=None, mig_timeout=3600, mig_protocol="tcp",
148            mig_cancel=False, offline=False, stable_check=False,
149            clean=False, save_path=None, dest_host='localhost', mig_port=None):
150    """
151    Migrate a VM locally and re-register it in the environment.
152
153    @param vm: The VM to migrate.
154    @param env: The environment dictionary.  If omitted, the migrated VM will
155            not be registered.
156    @param mig_timeout: timeout value for migration.
157    @param mig_protocol: migration protocol
158    @param mig_cancel: Test migrate_cancel or not when protocol is tcp.
159    @param dest_host: Destination host (defaults to 'localhost').
160    @param mig_port: Port that will be used for migration.
161    @return: The post-migration VM, in case of same host migration, True in
162            case of multi-host migration.
163    """
164    def mig_finished():
165        o = vm.monitor.info("migrate")
166        if isinstance(o, str):
167            return "status: active" not in o
168        else:
169            return o.get("status") != "active"
170
171    def mig_succeeded():
172        o = vm.monitor.info("migrate")
173        if isinstance(o, str):
174            return "status: completed" in o
175        else:
176            return o.get("status") == "completed"
177
178    def mig_failed():
179        o = vm.monitor.info("migrate")
180        if isinstance(o, str):
181            return "status: failed" in o
182        else:
183            return o.get("status") == "failed"
184
185    def mig_cancelled():
186        o = vm.monitor.info("migrate")
187        if isinstance(o, str):
188            return ("Migration status: cancelled" in o or
189                    "Migration status: canceled" in o)
190        else:
191            return (o.get("status") == "cancelled" or
192                    o.get("status") == "canceled")
193
194    def wait_for_migration():
195        if not virt_utils.wait_for(mig_finished, mig_timeout, 2, 2,
196                                  "Waiting for migration to finish"):
197            raise error.TestFail("Timeout expired while waiting for migration "
198                                 "to finish")
199
200    if dest_host == 'localhost':
201        dest_vm = vm.clone()
202
203    if (dest_host == 'localhost') and stable_check:
204        # Pause the dest vm after creation
205        dest_vm.params['extra_params'] = (dest_vm.params.get('extra_params','')
206                                          + ' -S')
207
208    if dest_host == 'localhost':
209        dest_vm.create(migration_mode=mig_protocol, mac_source=vm)
210
211    try:
212        try:
213            if mig_protocol == "tcp":
214                if dest_host == 'localhost':
215                    uri = "tcp:localhost:%d" % dest_vm.migration_port
216                else:
217                    uri = 'tcp:%s:%d' % (dest_host, mig_port)
218            elif mig_protocol == "unix":
219                uri = "unix:%s" % dest_vm.migration_file
220            elif mig_protocol == "exec":
221                uri = '"exec:nc localhost %s"' % dest_vm.migration_port
222
223            if offline:
224                vm.monitor.cmd("stop")
225            vm.monitor.migrate(uri)
226
227            if mig_cancel:
228                time.sleep(2)
229                vm.monitor.cmd("migrate_cancel")
230                if not virt_utils.wait_for(mig_cancelled, 60, 2, 2,
231                                          "Waiting for migration "
232                                          "cancellation"):
233                    raise error.TestFail("Failed to cancel migration")
234                if offline:
235                    vm.monitor.cmd("cont")
236                if dest_host == 'localhost':
237                    dest_vm.destroy(gracefully=False)
238                return vm
239            else:
240                wait_for_migration()
241                if (dest_host == 'localhost') and stable_check:
242                    save_path = None or "/tmp"
243                    save1 = os.path.join(save_path, "src")
244                    save2 = os.path.join(save_path, "dst")
245
246                    vm.save_to_file(save1)
247                    dest_vm.save_to_file(save2)
248
249                    # Fail if we see deltas
250                    md5_save1 = utils.hash_file(save1)
251                    md5_save2 = utils.hash_file(save2)
252                    if md5_save1 != md5_save2:
253                        raise error.TestFail("Mismatch of VM state before "
254                                             "and after migration")
255
256                if (dest_host == 'localhost') and offline:
257                    dest_vm.monitor.cmd("cont")
258        except:
259            if dest_host == 'localhost':
260                dest_vm.destroy()
261            raise
262
263    finally:
264        if (dest_host == 'localhost') and stable_check and clean:
265            logging.debug("Cleaning the state files")
266            if os.path.isfile(save1):
267                os.remove(save1)
268            if os.path.isfile(save2):
269                os.remove(save2)
270
271    # Report migration status
272    if mig_succeeded():
273        logging.info("Migration finished successfully")
274    elif mig_failed():
275        raise error.TestFail("Migration failed")
276    else:
277        raise error.TestFail("Migration ended with unknown status")
278
279    if dest_host == 'localhost':
280        if "paused" in dest_vm.monitor.info("status"):
281            logging.debug("Destination VM is paused, resuming it")
282            dest_vm.monitor.cmd("cont")
283
284    # Kill the source VM
285    vm.destroy(gracefully=False)
286
287    # Replace the source VM with the new cloned VM
288    if (dest_host == 'localhost') and (env is not None):
289        env.register_vm(vm.name, dest_vm)
290
291    # Return the new cloned VM
292    if dest_host == 'localhost':
293        return dest_vm
294    else:
295        return vm
296
297
298def stop_windows_service(session, service, timeout=120):
299    """
300    Stop a Windows service using sc.
301    If the service is already stopped or is not installed, do nothing.
302
303    @param service: The name of the service
304    @param timeout: Time duration to wait for service to stop
305    @raise error.TestError: Raised if the service can't be stopped
306    """
307    end_time = time.time() + timeout
308    while time.time() < end_time:
309        o = session.cmd_output("sc stop %s" % service, timeout=60)
310        # FAILED 1060 means the service isn't installed.
311        # FAILED 1062 means the service hasn't been started.
312        if re.search(r"\bFAILED (1060|1062)\b", o, re.I):
313            break
314        time.sleep(1)
315    else:
316        raise error.TestError("Could not stop service '%s'" % service)
317
318
319def start_windows_service(session, service, timeout=120):
320    """
321    Start a Windows service using sc.
322    If the service is already running, do nothing.
323    If the service isn't installed, fail.
324
325    @param service: The name of the service
326    @param timeout: Time duration to wait for service to start
327    @raise error.TestError: Raised if the service can't be started
328    """
329    end_time = time.time() + timeout
330    while time.time() < end_time:
331        o = session.cmd_output("sc start %s" % service, timeout=60)
332        # FAILED 1060 means the service isn't installed.
333        if re.search(r"\bFAILED 1060\b", o, re.I):
334            raise error.TestError("Could not start service '%s' "
335                                  "(service not installed)" % service)
336        # FAILED 1056 means the service is already running.
337        if re.search(r"\bFAILED 1056\b", o, re.I):
338            break
339        time.sleep(1)
340    else:
341        raise error.TestError("Could not start service '%s'" % service)
342
343
344def get_time(session, time_command, time_filter_re, time_format):
345    """
346    Return the host time and guest time.  If the guest time cannot be fetched
347    a TestError exception is raised.
348
349    Note that the shell session should be ready to receive commands
350    (i.e. should "display" a command prompt and should be done with all
351    previous commands).
352
353    @param session: A shell session.
354    @param time_command: Command to issue to get the current guest time.
355    @param time_filter_re: Regex filter to apply on the output of
356            time_command in order to get the current time.
357    @param time_format: Format string to pass to time.strptime() with the
358            result of the regex filter.
359    @return: A tuple containing the host time and guest time.
360    """
361    if len(re.findall("ntpdate|w32tm", time_command)) == 0:
362        host_time = time.time()
363        s = session.cmd_output(time_command)
364
365        try:
366            s = re.findall(time_filter_re, s)[0]
367        except IndexError:
368            logging.debug("The time string from guest is:\n%s", s)
369            raise error.TestError("The time string from guest is unexpected.")
370        except Exception, e:
371            logging.debug("(time_filter_re, time_string): (%s, %s)",
372                          time_filter_re, s)
373            raise e
374
375        guest_time = time.mktime(time.strptime(s, time_format))
376    else:
377        o = session.cmd(time_command)
378        if re.match('ntpdate', time_command):
379            offset = re.findall('offset (.*) sec', o)[0]
380            host_main, host_mantissa = re.findall(time_filter_re, o)[0]
381            host_time = (time.mktime(time.strptime(host_main, time_format)) +
382                         float("0.%s" % host_mantissa))
383            guest_time = host_time - float(offset)
384        else:
385            guest_time =  re.findall(time_filter_re, o)[0]
386            offset = re.findall("o:(.*)s", o)[0]
387            if re.match('PM', guest_time):
388                hour = re.findall('\d+ (\d+):', guest_time)[0]
389                hour = str(int(hour) + 12)
390                guest_time = re.sub('\d+\s\d+:', "\d+\s%s:" % hour,
391                                    guest_time)[:-3]
392            else:
393                guest_time = guest_time[:-3]
394            guest_time = time.mktime(time.strptime(guest_time, time_format))
395            host_time = guest_time + float(offset)
396
397    return (host_time, guest_time)
398
399
400def get_memory_info(lvms):
401    """
402    Get memory information from host and guests in format:
403    Host: memfree = XXXM; Guests memsh = {XXX,XXX,...}
404
405    @params lvms: List of VM objects
406    @return: String with memory info report
407    """
408    if not isinstance(lvms, list):
409        raise error.TestError("Invalid list passed to get_stat: %s " % lvms)
410
411    try:
412        meminfo = "Host: memfree = "
413        meminfo += str(int(utils.freememtotal()) / 1024) + "M; "
414        meminfo += "swapfree = "
415        mf = int(utils.read_from_meminfo("SwapFree")) / 1024
416        meminfo += str(mf) + "M; "
417    except Exception, e:
418        raise error.TestFail("Could not fetch host free memory info, "
419                             "reason: %s" % e)
420
421    meminfo += "Guests memsh = {"
422    for vm in lvms:
423        shm = vm.get_shared_meminfo()
424        if shm is None:
425            raise error.TestError("Could not get shared meminfo from "
426                                  "VM %s" % vm)
427        meminfo += "%dM; " % shm
428    meminfo = meminfo[0:-2] + "}"
429
430    return meminfo
431
432
433def run_autotest(vm, session, control_path, timeout, outputdir, params):
434    """
435    Run an autotest control file inside a guest (linux only utility).
436
437    @param vm: VM object.
438    @param session: A shell session on the VM provided.
439    @param control_path: A path to an autotest control file.
440    @param timeout: Timeout under which the autotest control file must complete.
441    @param outputdir: Path on host where we should copy the guest autotest
442            results to.
443
444    The following params is used by the migration
445    @param params: Test params used in the migration test
446    """
447    def copy_if_hash_differs(vm, local_path, remote_path):
448        """
449        Copy a file to a guest if it doesn't exist or if its MD5sum differs.
450
451        @param vm: VM object.
452        @param local_path: Local path.
453        @param remote_path: Remote path.
454
455        @return: Whether the hash differs (True) or not (False).
456        """
457        hash_differs = False
458        local_hash = utils.hash_file(local_path)
459        basename = os.path.basename(local_path)
460        output = session.cmd_output("md5sum %s" % remote_path)
461        if "such file" in output:
462            remote_hash = "0"
463        elif output:
464            remote_hash = output.split()[0]
465        else:
466            logging.warning("MD5 check for remote path %s did not return.",
467                            remote_path)
468            # Let's be a little more lenient here and see if it wasn't a
469            # temporary problem
470            remote_hash = "0"
471        if remote_hash != local_hash:
472            hash_differs = True
473            logging.debug("Copying %s to guest "
474                          "(remote hash: %s, local hash:%s)",
475                          basename, remote_hash, local_hash)
476            vm.copy_files_to(local_path, remote_path)
477        return hash_differs
478
479
480    def extract(vm, remote_path, dest_dir):
481        """
482        Extract the autotest .tar.bz2 file on the guest, ensuring the final
483        destination path will be dest_dir.
484
485        @param vm: VM object
486        @param remote_path: Remote file path
487        @param dest_dir: Destination dir for the contents
488        """
489        basename = os.path.basename(remote_path)
490        logging.debug("Extracting %s on VM %s", basename, vm.name)
491        session.cmd("rm -rf %s" % dest_dir)
492        dirname = os.path.dirname(remote_path)
493        session.cmd("cd %s" % dirname)
494        session.cmd("mkdir -p %s" % os.path.dirname(dest_dir))
495        e_cmd = "tar xjvf %s -C %s" % (basename, os.path.dirname(dest_dir))
496        output = session.cmd(e_cmd, timeout=120)
497        autotest_dirname = ""
498        for line in output.splitlines():
499            autotest_dirname = line.split("/")[0]
500            break
501        if autotest_dirname != os.path.basename(dest_dir):
502            session.cmd("cd %s" % os.path.dirname(dest_dir))
503            session.cmd("mv %s %s" %
504                        (autotest_dirname, os.path.basename(dest_dir)))
505
506
507    def get_results(guest_autotest_path):
508        """
509        Copy autotest results present on the guest back to the host.
510        """
511        logging.debug("Trying to copy autotest results from guest")
512        guest_results_dir = os.path.join(outputdir, "guest_autotest_results")
513        if not os.path.exists(guest_results_dir):
514            os.mkdir(guest_results_dir)
515        vm.copy_files_from("%s/results/default/*" % guest_autotest_path,
516                           guest_results_dir)
517
518
519    def get_results_summary(guest_autotest_path):
520        """
521        Get the status of the tests that were executed on the host and close
522        the session where autotest was being executed.
523        """
524        session.cmd("cd %s" % guest_autotest_path)
525        output = session.cmd_output("cat results/*/status")
526        try:
527            results = scan_results.parse_results(output)
528            # Report test results
529            logging.info("Results (test, status, duration, info):")
530            for result in results:
531                logging.info(str(result))
532            session.close()
533            return results
534        except Exception, e:
535            logging.error("Error processing guest autotest results: %s", e)
536            return None
537
538
539    if not os.path.isfile(control_path):
540        raise error.TestError("Invalid path to autotest control file: %s" %
541                              control_path)
542
543    migrate_background = params.get("migrate_background") == "yes"
544    if migrate_background:
545        mig_timeout = float(params.get("mig_timeout", "3600"))
546        mig_protocol = params.get("migration_protocol", "tcp")
547
548    compressed_autotest_path = "/tmp/autotest.tar.bz2"
549    destination_autotest_path = "/usr/local/autotest"
550
551    # To avoid problems, let's make the test use the current AUTODIR
552    # (autotest client path) location
553    autotest_path = os.environ['AUTODIR']
554    autotest_basename = os.path.basename(autotest_path)
555    autotest_parentdir = os.path.dirname(autotest_path)
556
557    # tar the contents of bindir/autotest
558    cmd = ("cd %s; tar cvjf %s %s/*" %
559           (autotest_parentdir, compressed_autotest_path, autotest_basename))
560    # Until we have nested virtualization, we don't need the kvm test :)
561    cmd += " --exclude=%s/tests/kvm" % autotest_basename
562    cmd += " --exclude=%s/results" % autotest_basename
563    cmd += " --exclude=%s/tmp" % autotest_basename
564    cmd += " --exclude=%s/control*" % autotest_basename
565    cmd += " --exclude=*.pyc"
566    cmd += " --exclude=*.svn"
567    cmd += " --exclude=*.git"
568    utils.run(cmd)
569
570    # Copy autotest.tar.bz2
571    update = copy_if_hash_differs(vm, compressed_autotest_path,
572                                  compressed_autotest_path)
573
574    # Extract autotest.tar.bz2
575    if update:
576        extract(vm, compressed_autotest_path, destination_autotest_path)
577
578    vm.copy_files_to(control_path,
579                     os.path.join(destination_autotest_path, 'control'))
580
581    # Run the test
582    logging.info("Running autotest control file %s on guest, timeout %ss",
583                 os.path.basename(control_path), timeout)
584    session.cmd("cd %s" % destination_autotest_path)
585    try:
586        session.cmd("rm -f control.state")
587        session.cmd("rm -rf results/*")
588    except aexpect.ShellError:
589        pass
590    try:
591        bg = None
592        try:
593            logging.info("---------------- Test output ----------------")
594            if migrate_background:
595                mig_timeout = float(params.get("mig_timeout", "3600"))
596                mig_protocol = params.get("migration_protocol", "tcp")
597
598                bg = virt_utils.Thread(session.cmd_output,
599                                      kwargs={'cmd': "bin/autotest control",
600                                              'timeout': timeout,
601                                              'print_func': logging.info})
602
603                bg.start()
604
605                while bg.is_alive():
606                    logging.info("Autotest job did not end, start a round of "
607                                 "migration")
608                    vm.migrate(timeout=mig_timeout, protocol=mig_protocol)
609            else:
610                session.cmd_output("bin/autotest control", timeout=timeout,
611                                   print_func=logging.info)
612        finally:
613            logging.info("------------- End of test output ------------")
614            if migrate_background and bg:
615                bg.join()
616    except aexpect.ShellTimeoutError:
617        if vm.is_alive():
618            get_results(destination_autotest_path)
619            get_results_summary(destination_autotest_path)
620            raise error.TestError("Timeout elapsed while waiting for job to "
621                                  "complete")
622        else:
623            raise error.TestError("Autotest job on guest failed "
624                                  "(VM terminated during job)")
625    except aexpect.ShellProcessTerminatedError:
626        get_results(destination_autotest_path)
627        raise error.TestError("Autotest job on guest failed "
628                              "(Remote session terminated during job)")
629
630    results = get_results_summary(destination_autotest_path)
631    get_results(destination_autotest_path)
632
633    # Make a list of FAIL/ERROR/ABORT results (make sure FAIL results appear
634    # before ERROR results, and ERROR results appear before ABORT results)
635    bad_results = [r[0] for r in results if r[1] == "FAIL"]
636    bad_results += [r[0] for r in results if r[1] == "ERROR"]
637    bad_results += [r[0] for r in results if r[1] == "ABORT"]
638
639    # Fail the test if necessary
640    if not results:
641        raise error.TestFail("Autotest control file run did not produce any "
642                             "recognizable results")
643    if bad_results:
644        if len(bad_results) == 1:
645            e_msg = ("Test %s failed during control file execution" %
646                     bad_results[0])
647        else:
648            e_msg = ("Tests %s failed during control file execution" %
649                     " ".join(bad_results))
650        raise error.TestFail(e_msg)
651
652
653def get_loss_ratio(output):
654    """
655    Get the packet loss ratio from the output of ping
656.
657    @param output: Ping output.
658    """
659    try:
660        return int(re.findall('(\d+)% packet loss', output)[0])
661    except IndexError:
662        logging.debug(output)
663        return -1
664
665
666def raw_ping(command, timeout, session, output_func):
667    """
668    Low-level ping command execution.
669
670    @param command: Ping command.
671    @param timeout: Timeout of the ping command.
672    @param session: Local executon hint or session to execute the ping command.
673    """
674    if session is None:
675        process = aexpect.run_bg(command, output_func=output_func,
676                                        timeout=timeout)
677
678        # Send SIGINT signal to notify the timeout of running ping process,
679        # Because ping have the ability to catch the SIGINT signal so we can
680        # always get the packet loss ratio even if timeout.
681        if process.is_alive():
682            virt_utils.kill_process_tree(process.get_pid(), signal.SIGINT)
683
684        status = process.get_status()
685        output = process.get_output()
686
687        process.close()
688        return status, output
689    else:
690        output = ""
691        try:
692            output = session.cmd_output(command, timeout=timeout,
693                                        print_func=output_func)
694        except aexpect.ShellTimeoutError:
695            # Send ctrl+c (SIGINT) through ssh session
696            session.send("\003")
697            try:
698                output2 = session.read_up_to_prompt(print_func=output_func)
699                output += output2
700            except aexpect.ExpectTimeoutError, e:
701                output += e.output
702                # We also need to use this session to query the return value
703                session.send("\003")
704
705        session.sendline(session.status_test_command)
706        try:
707            o2 = session.read_up_to_prompt()
708        except aexpect.ExpectError:
709            status = -1
710        else:
711            try:
712                status = int(re.findall("\d+", o2)[0])
713            except:
714                status = -1
715
716        return status, output
717
718
719def ping(dest=None, count=None, interval=None, interface=None,
720         packetsize=None, ttl=None, hint=None, adaptive=False,
721         broadcast=False, flood=False, timeout=0,
722         output_func=logging.debug, session=None):
723    """
724    Wrapper of ping.
725
726    @param dest: Destination address.
727    @param count: Count of icmp packet.
728    @param interval: Interval of two icmp echo request.
729    @param interface: Specified interface of the source address.
730    @param packetsize: Packet size of icmp.
731    @param ttl: IP time to live.
732    @param hint: Path mtu discovery hint.
733    @param adaptive: Adaptive ping flag.
734    @param broadcast: Broadcast ping flag.
735    @param flood: Flood ping flag.
736    @param timeout: Timeout for the ping command.
737    @param output_func: Function used to log the result of ping.
738    @param session: Local executon hint or session to execute the ping command.
739    """
740    if dest is not None:
741        command = "ping %s " % dest
742    else:
743        command = "ping localhost "
744    if count is not None:
745        command += " -c %s" % count
746    if interval is not None:
747        command += " -i %s" % interval
748    if interface is not None:
749        command += " -I %s" % interface
750    if packetsize is not None:
751        command += " -s %s" % packetsize
752    if ttl is not None:
753        command += " -t %s" % ttl
754    if hint is not None:
755        command += " -M %s" % hint
756    if adaptive:
757        command += " -A"
758    if broadcast:
759        command += " -b"
760    if flood:
761        command += " -f -q"
762        output_func = None
763
764    return raw_ping(command, timeout, session, output_func)
765
766
767def get_linux_ifname(session, mac_address):
768    """
769    Get the interface name through the mac address.
770
771    @param session: session to the virtual machine
772    @mac_address: the macaddress of nic
773    """
774
775    output = session.cmd_output("ifconfig -a")
776
777    try:
778        ethname = re.findall("(\w+)\s+Link.*%s" % mac_address, output,
779                             re.IGNORECASE)[0]
780        return ethname
781    except:
782        return None
783