1 # Copyright (c) 2009 The Chromium OS Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import logging, os, socket, subprocess, tempfile, threading, time 6 7 from autotest_lib.client.common_lib import error 8 from autotest_lib.server import autotest, hosts, site_host_attributes 9 from autotest_lib.server import subcommand, test, utils 10 11 12 class WolWake(threading.Thread): 13 """Class to allow waking of DUT via Wake-on-LAN capabilities (WOL).""" 14 15 16 def __init__(self, hostname, mac_addr, sleep_secs): 17 """Constructor for waking DUT. 18 19 Args: 20 mac_addr: string of mac address tuple 21 sleep_secs: seconds to sleep prior to attempting WOL 22 """ 23 threading.Thread.__init__(self) 24 self._hostname = hostname 25 self._mac_addr = mac_addr 26 self._sleep_secs = sleep_secs 27 28 29 # TODO(tbroch) Borrowed from class ServoTest. Refactor for code re-use 30 def _ping_test(self, hostname, timeout=5): 31 """Verify whether a host responds to a ping. 32 33 Args: 34 hostname: Hostname to ping. 35 timeout: Time in seconds to wait for a response. 36 37 Returns: True if success False otherwise 38 """ 39 with open(os.devnull, 'w') as fnull: 40 ping_good = False 41 elapsed_time = 0 42 while not ping_good and elapsed_time < timeout: 43 ping_good = subprocess.call( 44 ['ping', '-c', '1', '-W', str(timeout), hostname], 45 stdout=fnull, stderr=fnull) == 0 46 time.sleep(1) 47 elapsed_time += 1 48 return ping_good 49 50 51 def _send_wol_magic_packet(self): 52 """Perform Wake-on-LAN magic wake. 53 54 WOL magic packet consists of: 55 0xff repeated for 6 bytes 56 <mac addr> repeated 16 times 57 58 Sent as a broadcast packet. 59 """ 60 mac_tuple = self._mac_addr.split(':') 61 assert len(mac_tuple) == 6 62 magic = '\xff' * 6 63 submagic = ''.join("%c" % int(value, 16) for value in mac_tuple) 64 magic += submagic * 16 65 assert len(magic) == 102 66 67 sock=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 68 sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) 69 sock.sendto(magic, ('<broadcast>', 7)) 70 sock.close() 71 logging.info("Wake thread sent WOL wakeup") 72 73 74 def run(self): 75 # ping device to make sure its network is off presumably from suspend 76 # not another malfunction. 77 ping_secs = 0 78 while self._ping_test(self._hostname, timeout=2) and \ 79 ping_secs < self._sleep_secs: 80 time.sleep(1) 81 ping_secs += 1 82 83 self._send_wol_magic_packet() 84 85 86 class network_EthCapsServer(test.test): 87 version = 1 88 89 def _parse_ifconfig(self, filename): 90 """Retrieve ifconfig information. 91 92 Raises 93 error.TestError if unable to parse mac address 94 """ 95 self._mac_addr = None 96 97 fd = open(filename) 98 for ln in fd.readlines(): 99 info_str = ln.strip() 100 logging.debug(ln) 101 index = info_str.find('HWaddr ') 102 if index != -1: 103 self._mac_addr = info_str[index + len('HWaddr '):] 104 logging.info("mac addr = %s" % self._mac_addr) 105 break 106 fd.close() 107 108 if not self._mac_addr: 109 raise error.TestError("Unable to find mac addresss") 110 111 112 def _client_cmd(self, cmd, results=None): 113 """Execute a command on the client. 114 115 Args: 116 results: string of filename to save results on client. 117 118 Returns: 119 string of filename on server side with stdout results of command 120 """ 121 if results: 122 client_tmpdir = self._client.get_tmp_dir() 123 client_results = os.path.join(client_tmpdir, "%s" % results) 124 cmd = "%s > %s 2>&1" % (cmd, client_results) 125 126 logging.info("Client cmd = %s", cmd) 127 self._client.run(cmd) 128 129 if results: 130 server_tmpfile = tempfile.NamedTemporaryFile(delete=False) 131 server_tmpfile.close() 132 self._client.get_file(client_results, server_tmpfile.name) 133 return server_tmpfile.name 134 135 return None 136 137 138 def run_once(self, client_ip=None, ethname='eth0'): 139 """Run the test. 140 141 Args: 142 client_ip: string of client's ip address 143 ethname: string of ethernet device under test 144 """ 145 if not client_ip: 146 error.TestError("Must provide client's IP address to test") 147 148 sleep_secs = 20 149 150 self._ethname = ethname 151 self._client_ip = client_ip 152 self._client = hosts.create_host(client_ip) 153 client_at = autotest.Autotest(self._client) 154 155 # retrieve ifconfig info for mac address of client 156 cmd = "ifconfig %s" % self._ethname 157 ifconfig_filename = self._client_cmd(cmd, results="ifconfig.log") 158 self._parse_ifconfig(ifconfig_filename) 159 160 # thread to wake the device using WOL 161 wol_wake = WolWake(self._client_ip, self._mac_addr, sleep_secs) 162 wol_wake.start() 163 164 # create and run client test to prepare and suspend device 165 client_at.run_test("network_EthCaps", ethname=ethname, 166 threshold_secs=sleep_secs * 2) 167 168 wol_wake.join() 169