| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import copy | 5 import copy |
| 6 import dbus | 6 import dbus |
| 7 import logging | 7 import logging |
| 8 import os | 8 import os |
| 9 import time | 9 import time |
| 10 | 10 |
| 11 from chromeos.power_strip import PowerStrip | 11 from chromeos.power_strip import PowerStrip |
| 12 import pyauto | 12 import pyauto |
| 13 import pyauto_errors | 13 import pyauto_errors |
| 14 | 14 |
| 15 class WifiPowerStrip(PowerStrip): | |
| 16 """Manages the power state of wifi routers connected to a power strip. | |
| 17 | |
| 18 This class provides additional functionality over PowerStrip by providing | |
| 19 a timeout feature for wifi routers connected to the strip. This is to prevent | |
| 20 repeated on/off calls to the same router which may put the router in an | |
| 21 undesired state. | |
| 22 """ | |
| 23 | |
| 24 def __init__ (self, host, routers): | |
| 25 """Initializes a WifiPowerStrip object. | |
| 26 | |
| 27 Args: | |
| 28 host: IP of the switch that the routers are attached to. | |
| 29 routers: Dictionary of wifi routers in the following format: | |
| 30 { | |
| 31 '< router name >': { | |
| 32 'strip_id' : '.aX' # where X is the port number | |
| 33 < additional fields may be added here for each router > | |
| 34 } | |
| 35 } | |
| 36 """ | |
| 37 self._router_dict = routers | |
| 38 | |
| 39 # Each router will have a timestamp associated to it regarding whether | |
| 40 # or not an action can be performed on it yet. This is to prevent | |
| 41 # the spamming of power on/off calls on a particular router. | |
| 42 # The WifiPowerStrip_UsableTime field specifies the earliest time | |
| 43 # after which the router may be used. We will initialize it to now | |
| 44 # since they should all be usable at init. | |
| 45 for router_info in self._router_dict.values(): | |
| 46 router_info['WifiPowerStrip_UsableTime'] = time.time() | |
| 47 | |
| 48 # _routers_used keeps track of which routers were used during the lifetime | |
| 49 # of the WifiPowerStrip instance. Adding used routers occurs when | |
| 50 # a wifi router has been turned on. Otherwise, it get clears upon | |
| 51 # the TurnOffUsedRouters call. | |
| 52 self._routers_used = set() | |
| 53 PowerStrip.__init__(self, host) | |
| 54 | |
| 55 def GetRouterConfig(self, router_name): | |
| 56 """Returns the configuration for the specified router. | |
| 57 | |
| 58 Args: | |
| 59 router_name: A string specifying the router. | |
| 60 | |
| 61 Returns: | |
| 62 The config dictionary for the given router if the router is defined. | |
| 63 None otherwise. | |
| 64 """ | |
| 65 return copy.deepcopy(self._router_dict.get(router_name)) | |
| 66 | |
| 67 def RouterPower(self, router_name, power_state, pause_after=5): | |
| 68 """Executes PowerStrip commands. | |
| 69 | |
| 70 Args: | |
| 71 router_name: The name of the router to perform the action on. | |
| 72 power_state: A boolean value where True represents turning the router on | |
| 73 and False represents turning the router off. | |
| 74 pause_after: Specified in seconds, and specifies the time to sleep | |
| 75 after a command is run. This is to prevent spamming of | |
| 76 power on/off of the same router which has put the router | |
| 77 in an undesirable state. | |
| 78 | |
| 79 Raises: | |
| 80 Exception if router_name is not a valid router. | |
| 81 """ | |
| 82 router = self.GetRouterConfig(router_name) | |
| 83 if not router: raise Exception('Invalid router name \'%s\'.' % router_name) | |
| 84 | |
| 85 # Hidden routers will always be on. Don't allow controlling of the power | |
| 86 # for these networks. | |
| 87 if router.get('hidden'): | |
| 88 return | |
| 89 | |
| 90 sleep_time = router['WifiPowerStrip_UsableTime'] - time.time() | |
| 91 if sleep_time > 0: | |
| 92 time.sleep(sleep_time) | |
| 93 | |
| 94 if power_state: | |
| 95 self._routers_used |= set([router_name]) | |
| 96 logging.debug('Turning on router %s:%s.' % | |
| 97 (router['strip_id'], router_name)) | |
| 98 self.PowerOn(router['strip_id']) | |
| 99 else: | |
| 100 logging.debug('Turning off router %s:%s.' % | |
| 101 (router['strip_id'], router_name)) | |
| 102 self.PowerOff(router['strip_id']) | |
| 103 | |
| 104 # Set the Usable time of the particular router to pause_after | |
| 105 # seconds after the current time. | |
| 106 router['WifiPowerStrip_UsableTime'] = time.time() + pause_after | |
| 107 | |
| 108 def TurnOffAllRouters(self): | |
| 109 """Turns off all the routers.""" | |
| 110 for router in self._router_dict: | |
| 111 self.RouterPower(router, False, pause_after=0) | |
| 112 | |
| 113 def TurnOffUsedRouters(self): | |
| 114 """Turns off the routers that were once turned on.""" | |
| 115 for router in self._routers_used: | |
| 116 self.RouterPower(router, False, pause_after=0) | |
| 117 | |
| 118 self._routers_used = set() | |
| 119 | |
| 120 | |
| 121 class PyNetworkUITest(pyauto.PyUITest): | 15 class PyNetworkUITest(pyauto.PyUITest): |
| 122 """A subclass of PyUITest for Chrome OS network tests. | 16 """A subclass of PyUITest for Chrome OS network tests. |
| 123 | 17 |
| 124 A subclass of PyUITest that automatically sets the flimflam | 18 A subclass of PyUITest that automatically sets the flimflam |
| 125 priorities to put wifi connections first before starting tests. | 19 priorities to put wifi connections first before starting tests. |
| 126 This is for convenience when writing wifi tests. | 20 This is for convenience when writing wifi tests. |
| 127 """ | 21 """ |
| 128 _ROUTER_CONFIG_FILE = os.path.join(pyauto.PyUITest.DataDir(), | 22 _ROUTER_CONFIG_FILE = os.path.join(pyauto.PyUITest.DataDir(), |
| 129 'pyauto_private', 'chromeos', 'network', | 23 'pyauto_private', 'chromeos', 'network', |
| 130 'wifi_testbed_config') | 24 'wifi_testbed_config') |
| 131 _FLIMFLAM_PATH = 'org.chromium.flimflam' | 25 _FLIMFLAM_PATH = 'org.chromium.flimflam' |
| 132 | 26 |
| 133 def setUp(self): | 27 def setUp(self): |
| 134 self.CleanupFlimflamDirsOnChromeOS() | 28 self.CleanupFlimflamDirsOnChromeOS() |
| 135 # Move ethernet to the end of the flimflam priority list, | 29 # Move ethernet to the end of the flimflam priority list, |
| 136 # effectively hiding any ssh connections that the | 30 # effectively hiding any ssh connections that the |
| 137 # test harness might be using and putting wifi ahead. | 31 # test harness might be using and putting wifi ahead. |
| 138 self._PushServiceOrder('wifi,ethernet') | 32 self._PushServiceOrder('wifi,ethernet') |
| 139 self._ParseDefaultRoutingTable() | 33 self._ParseDefaultRoutingTable() |
| 140 pyauto.PyUITest.setUp(self) | 34 pyauto.PyUITest.setUp(self) |
| 141 self.ForgetAllRememberedNetworks() | 35 self.ForgetAllRememberedNetworks() |
| 142 self._wifi_power_strip = None | |
| 143 | 36 |
| 144 def tearDown(self): | 37 def tearDown(self): |
| 145 self.ForgetAllRememberedNetworks() | 38 self.ForgetAllRememberedNetworks() |
| 146 pyauto.PyUITest.tearDown(self) | 39 pyauto.PyUITest.tearDown(self) |
| 147 self._PopServiceOrder() | 40 self._PopServiceOrder() |
| 148 if self._wifi_power_strip: | |
| 149 self._wifi_power_strip.TurnOffUsedRouters() | |
| 150 # Remove the route entry for the power strip. | 41 # Remove the route entry for the power strip. |
| 151 if hasattr(self, 'ps_route_entry'): | 42 if hasattr(self, 'ps_route_entry'): |
| 152 os.system('route del -net %(ipaddress)s gateway %(gateway)s netmask ' | 43 os.system('route del -net %(ipaddress)s gateway %(gateway)s netmask ' |
| 153 '%(netmask)s dev %(iface)s' % self.ps_route_entry) | 44 '%(netmask)s dev %(iface)s' % self.ps_route_entry) |
| 154 | 45 |
| 155 def _GetFlimflamManager(self): | 46 def _GetFlimflamManager(self): |
| 156 _proxy = dbus.SystemBus().get_object(self._FLIMFLAM_PATH, '/') | 47 _proxy = dbus.SystemBus().get_object(self._FLIMFLAM_PATH, '/') |
| 157 return dbus.Interface(_proxy, self._FLIMFLAM_PATH + '.Manager') | 48 return dbus.Interface(_proxy, self._FLIMFLAM_PATH + '.Manager') |
| 158 | 49 |
| 159 def _ParseDefaultRoutingTable(self): | 50 def _ParseDefaultRoutingTable(self): |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 202 old_service_order = self._old_service_order.split(',') | 93 old_service_order = self._old_service_order.split(',') |
| 203 set_service_order = self._GetFlimflamManager().GetServiceOrder().split(',') | 94 set_service_order = self._GetFlimflamManager().GetServiceOrder().split(',') |
| 204 common_service = set(old_service_order) & set(set_service_order) | 95 common_service = set(old_service_order) & set(set_service_order) |
| 205 | 96 |
| 206 old_service_order = [s for s in old_service_order if s in common_service] | 97 old_service_order = [s for s in old_service_order if s in common_service] |
| 207 set_service_order = [s for s in set_service_order if s in common_service] | 98 set_service_order = [s for s in set_service_order if s in common_service] |
| 208 | 99 |
| 209 assert old_service_order == set_service_order, \ | 100 assert old_service_order == set_service_order, \ |
| 210 'Flimflam service order not set properly. %s != %s' % \ | 101 'Flimflam service order not set properly. %s != %s' % \ |
| 211 (old_service_order, set_service_order) | 102 (old_service_order, set_service_order) |
| 212 | |
| 213 def _SetupRouteForPowerStrip(self, ipaddress, iface='eth'): | |
| 214 """Create a route table entry for the power strip.""" | |
| 215 | |
| 216 # Assume device has only one interface that is prepended with | |
| 217 # $iface and use that one. | |
| 218 try: | |
| 219 iface = [ key for key in self.default_routes.keys() if iface in key ][0] | |
| 220 except: | |
| 221 assert 'Unable to find interface of type %s.' % iface | |
| 222 | |
| 223 self.ps_route_entry = { | |
| 224 'iface' : iface, | |
| 225 'gateway' : self.default_routes[iface]['gateway'], | |
| 226 'netmask' : '255.255.255.255', | |
| 227 'ipaddress' : ipaddress | |
| 228 } | |
| 229 | |
| 230 os.system('route add -net %(ipaddress)s gateway %(gateway)s netmask ' | |
| 231 '%(netmask)s dev %(iface)s' % self.ps_route_entry) | |
| 232 | |
| 233 # Verify the route was added. | |
| 234 assert os.system('route -n | egrep "^%(ipaddress)s[[:space:]]+%(gateway)s' | |
| 235 '[[:space:]]+%(netmask)s"' % self.ps_route_entry) == 0, \ | |
| 236 'Failed to create default route for powerstrip.' | |
| 237 | |
| 238 def InitWifiPowerStrip(self): | |
| 239 """Initializes the router controller using the specified config file.""" | |
| 240 | |
| 241 assert os.path.exists(PyNetworkUITest._ROUTER_CONFIG_FILE), \ | |
| 242 'Router configuration file does not exist.' | |
| 243 | |
| 244 config = pyauto.PyUITest.EvalDataFrom(self._ROUTER_CONFIG_FILE) | |
| 245 strip_ip, routers = config['strip_ip'], config['routers'] | |
| 246 | |
| 247 self._SetupRouteForPowerStrip(strip_ip) | |
| 248 self._wifi_power_strip = WifiPowerStrip(strip_ip, routers) | |
| 249 | |
| 250 self.RouterPower = self._wifi_power_strip.RouterPower | |
| 251 self.TurnOffAllRouters = self._wifi_power_strip.TurnOffAllRouters | |
| 252 self.GetRouterConfig = self._wifi_power_strip.GetRouterConfig | |
| 253 | |
| 254 def ConnectToWifiRouter(self, router_name, shared=True): | |
| 255 """Connects to a router by name. | |
| 256 | |
| 257 Args: | |
| 258 router_name: The name of the router that is specified in the | |
| 259 configuration file. | |
| 260 """ | |
| 261 router = self._wifi_power_strip.GetRouterConfig(router_name) | |
| 262 assert router, 'Router with name %s is not defined ' \ | |
| 263 'in the router configuration.' % router_name | |
| 264 security = router.get('security', 'SECURITY_NONE') | |
| 265 passphrase = router.get('passphrase', '') | |
| 266 | |
| 267 # Branch off the connect calls depending on if the wifi network is hidden | |
| 268 # or not. | |
| 269 error_string = None | |
| 270 if router.get('hidden'): | |
| 271 error_string = self.ConnectToHiddenWifiNetwork(router['ssid'], security, | |
| 272 passphrase) | |
| 273 else: | |
| 274 service_path = self.GetServicePath(router['ssid']) | |
| 275 assert service_path, 'Service with SSID %s is not present.' % \ | |
| 276 router['ssid'] | |
| 277 | |
| 278 logging.debug('Connecting to router %s.' % router_name) | |
| 279 error_string = self.ConnectToWifiNetwork(service_path, | |
| 280 password=passphrase, | |
| 281 shared=shared) | |
| 282 return error_string | |
| OLD | NEW |