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 3# found in the LICENSE file. 4 5"""File containing class to build all available ap_configurators.""" 6 7import logging 8import requests 9 10from autotest_lib.client.common_lib.cros.network import ap_constants 11from autotest_lib.server import site_utils 12from autotest_lib.server.cros import ap_config 13from autotest_lib.server.cros.ap_configurators import ap_cartridge 14from autotest_lib.server.cros.ap_configurators import ap_spec 15from autotest_lib.server.cros.dynamic_suite import frontend_wrappers 16 17CHAOS_URL = 'https://chaos-188802.appspot.com' 18 19 20class APConfiguratorFactory(object): 21 """Class that instantiates all available APConfigurators. 22 23 @attribute CONFIGURATOR_MAP: a dict of strings, mapping to model-specific 24 APConfigurator objects. 25 @attribute BANDS: a string, bands supported by an AP. 26 @attribute MODES: a string, 802.11 modes supported by an AP. 27 @attribute SECURITIES: a string, security methods supported by an AP. 28 @attribute HOSTNAMES: a string, AP hostname. 29 @attribute ap_list: a list of APConfigurator objects. 30 @attribute ap_config: an APConfiguratorConfig object. 31 """ 32 33 PREFIX='autotest_lib.server.cros.ap_configurators.' 34 CONFIGURATOR_MAP = { 35 'StaticAPConfigurator': 36 [PREFIX + 'static_ap_configurator', 37 'StaticAPConfigurator'], 38 } 39 40 BANDS = 'bands' 41 MODES = 'modes' 42 SECURITIES = 'securities' 43 HOSTNAMES = 'hostnames' 44 45 46 def __init__(self, ap_test_type, spec=None): 47 webdriver_ready = False 48 self.ap_list = [] 49 self.test_type = ap_test_type 50 for ap in ap_config.get_ap_list(ap_test_type): 51 module_name, configurator_class = \ 52 self.CONFIGURATOR_MAP[ap.get_class()] 53 module = __import__(module_name, fromlist=configurator_class) 54 configurator = module.__dict__[configurator_class] 55 self.ap_list.append(configurator(ap_config=ap)) 56 57 58 def _get_aps_by_visibility(self, visible=True): 59 """Returns all configurators that support setting visibility. 60 61 @param visibility = True if SSID should be visible; False otherwise. 62 63 @returns aps: a set of APConfigurators""" 64 if visible: 65 return set(self.ap_list) 66 67 return set(filter(lambda ap: ap.is_visibility_supported(), 68 self.ap_list)) 69 70 71 def _get_aps_by_mode(self, band, mode): 72 """Returns all configurators that support a given 802.11 mode. 73 74 @param band: an 802.11 band. 75 @param mode: an 802.11 modes. 76 77 @returns aps: a set of APConfigurators. 78 """ 79 if not mode: 80 return set(self.ap_list) 81 82 aps = [] 83 for ap in self.ap_list: 84 modes = ap.get_supported_modes() 85 for d in modes: 86 if d['band'] == band and mode in d['modes']: 87 aps.append(ap) 88 return set(aps) 89 90 91 def _get_aps_by_security(self, security): 92 """Returns all configurators that support a given security mode. 93 94 @param security: the security type 95 96 @returns aps: a set of APConfigurators. 97 """ 98 99 if not security: 100 return set(self.ap_list) 101 102 aps = [] 103 for ap in self.ap_list: 104 if ap.is_security_mode_supported(security): 105 aps.append(ap) 106 return set(aps) 107 108 109 def _get_aps_by_band(self, band, channel=None): 110 """Returns all APs that support a given band. 111 112 @param band: the band desired. 113 114 @returns aps: a set of APConfigurators. 115 """ 116 if not band: 117 return set(self.ap_list) 118 119 aps = [] 120 for ap in self.ap_list: 121 bands_and_channels = ap.get_supported_bands() 122 for d in bands_and_channels: 123 if channel: 124 if d['band'] == band and channel in d['channels']: 125 aps.append(ap) 126 elif d['band'] == band: 127 aps.append(ap) 128 return set(aps) 129 130 131 def get_aps_by_hostnames(self, hostnames, ap_list=None): 132 """Returns specific APs by host name. 133 134 @param hostnames: a list of strings, AP's wan_hostname defined in the AP 135 configuration file. 136 @param ap_list: a list of APConfigurator objects. 137 138 @return a list of APConfigurators. 139 """ 140 if ap_list == None: 141 ap_list = self.ap_list 142 143 aps = [] 144 for ap in ap_list: 145 if ap.host_name in hostnames: 146 logging.info('Found AP by hostname %s', ap.host_name) 147 aps.append(ap) 148 149 return aps 150 151 152 def _get_aps_by_configurator_type(self, configurator_type, ap_list): 153 """Returns APs that match the given configurator type. 154 155 @param configurator_type: the type of configurtor to return. 156 @param ap_list: a list of APConfigurator objects. 157 158 @return a list of APConfigurators. 159 """ 160 aps = [] 161 for ap in ap_list: 162 if ap.configurator_type == configurator_type: 163 aps.append(ap) 164 165 return aps 166 167 168 def _get_aps_by_lab_location(self, want_chamber_aps, ap_list): 169 """Returns APs that are inside or outside of the chaos/clique lab. 170 171 @param want_chamber_aps: True to select only APs in the chaos/clique 172 chamber. False to select APs outside of the chaos/clique chamber. 173 @param ap_list: a list of APConfigurator objects. 174 175 @return a list of APConfigurators 176 """ 177 aps = [] 178 afe = frontend_wrappers.RetryingAFE( 179 timeout_min=10, delay_sec=5, server=site_utils.get_global_afe_hostname()) 180 if self.test_type == ap_constants.AP_TEST_TYPE_CHAOS: 181 ap_label = 'chaos_ap' 182 lab_label = 'chaos_chamber' 183 elif self.test_type == ap_constants.AP_TEST_TYPE_CLIQUE: 184 ap_label = 'clique_ap' 185 lab_label = 'clique_chamber' 186 elif self.test_type == ap_constants.AP_TEST_TYPE_CASEY5: 187 ap_label = 'casey_ap5' 188 lab_label = 'casey_chamber5' 189 elif self.test_type == ap_constants.AP_TEST_TYPE_CASEY7: 190 ap_label = 'casey_ap7' 191 lab_label = 'casey_chamber7' 192 else: 193 return None 194 all_aps = set(afe.get_hostnames(label=ap_label)) 195 chamber_devices = set(afe.get_hostnames(label=lab_label)) 196 chamber_aps = all_aps.intersection(chamber_devices) 197 for ap in ap_list: 198 if want_chamber_aps and ap.host_name in chamber_aps: 199 aps.append(ap) 200 201 if not want_chamber_aps and ap.host_name not in chamber_aps: 202 aps.append(ap) 203 204 return aps 205 206 def _get_ds_aps_by_lab_location(self, want_chamber_aps, ap_list): 207 """Returns APs that are inside or outside of the chaos/clique lab. 208 209 @param want_chamber_aps: True to select only APs in the chaos/clique 210 chamber. False to select APs outside of the chaos/clique chamber. 211 @param ap_list: a list of APConfigurator objects. 212 213 @return a list of APConfigurators 214 """ 215 aps = [] 216 if self.test_type == ap_constants.AP_TEST_TYPE_CHAOS: 217 ap_label = 'chaos_ap' 218 lab_label = 'chaos_chamber' 219 elif self.test_type == ap_constants.AP_TEST_TYPE_CLIQUE: 220 ap_label = 'clique_ap' 221 lab_label = 'clique_chamber' 222 elif self.test_type == ap_constants.AP_TEST_TYPE_CASEY5: 223 ap_label = 'casey_ap5' 224 lab_label = 'casey_chamber5' 225 elif self.test_type == ap_constants.AP_TEST_TYPE_CASEY7: 226 ap_label = 'casey_ap7' 227 lab_label = 'casey_chamber7' 228 else: 229 return None 230 231 chamber_aps = [] 232 233 # Request datastore for devices with requested labels. 234 device_query = requests.put(CHAOS_URL + '/devices/location', \ 235 json={"ap_label":ap_label, "lab_label":lab_label}) 236 237 # Add hostnames to chamber_aps list 238 for device in device_query.json(): 239 chamber_aps.append(device['hostname']) 240 241 for ap in ap_list: 242 if want_chamber_aps and ap.host_name in chamber_aps: 243 aps.append(ap) 244 245 if not want_chamber_aps and ap.host_name not in chamber_aps: 246 aps.append(ap) 247 248 return aps 249 250 251 def get_ap_configurators_by_spec(self, spec=None, pre_configure=False): 252 """Returns available configurators meeting spec. 253 254 @param spec: a validated ap_spec object 255 @param pre_configure: boolean, True to set all of the configuration 256 options for the APConfigurator object using the 257 given ap_spec; False otherwise. An ap_spec must 258 be passed for this to have any effect. 259 @returns aps: a list of APConfigurator objects 260 """ 261 if not spec: 262 return self.ap_list 263 264 # APSpec matching is exact. With the exception of lab location, even 265 # if a hostname is passed the capabilities of a given configurator 266 # much match everything in the APSpec. This helps to prevent failures 267 # during the pre-scan phase. 268 aps = self._get_aps_by_band(spec.band, channel=spec.channel) 269 aps &= self._get_aps_by_mode(spec.band, spec.mode) 270 aps &= self._get_aps_by_security(spec.security) 271 aps &= self._get_aps_by_visibility(spec.visible) 272 matching_aps = list(aps) 273 # If APs hostnames are provided, assume the tester knows the location 274 # of the AP and skip AFE calls. 275 if spec.hostnames is None: 276 matching_aps = self._get_aps_by_lab_location(spec.lab_ap, 277 matching_aps) 278 # TODO(@rjahagir): Uncomment to use datastore methods. 279 # matching_aps = self._get_ds_aps_by_lab_location(spec.lab_ap, 280 # matching_aps) 281 282 if spec.configurator_type != ap_spec.CONFIGURATOR_ANY: 283 matching_aps = self._get_aps_by_configurator_type( 284 spec.configurator_type, matching_aps) 285 if spec.hostnames is not None: 286 matching_aps = self.get_aps_by_hostnames(spec.hostnames, 287 ap_list=matching_aps) 288 if pre_configure: 289 for ap in matching_aps: 290 ap.set_using_ap_spec(spec) 291 return matching_aps 292 293 294 def turn_off_all_routers(self, broken_pdus): 295 """Powers down all of the routers. 296 297 @param broken_pdus: list of bad/offline PDUs. 298 """ 299 ap_power_cartridge = ap_cartridge.APCartridge() 300 for ap in self.ap_list: 301 ap.power_down_router() 302 ap_power_cartridge.push_configurator(ap) 303 ap_power_cartridge.run_configurators(broken_pdus) 304