1# Copyright (c) 2013 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 5import logging 6import urlparse 7import ap_spec 8import dynamic_ap_configurator 9from selenium.common.exceptions import WebDriverException 10from selenium.common.exceptions import TimeoutException as \ 11 SeleniumTimeoutException 12 13 14class BelkinF9K1103APConfigurator( 15 dynamic_ap_configurator.DynamicAPConfigurator): 16 """Class to configure Belkin F9K1103 v1 (01c) router.""" 17 18 19 def _security_alert(self, alert): 20 text = alert.text 21 if 'Invalid character' in text: 22 alert.accept() 23 elif 'It is recommended to use WPA/WPA2 when WPS is enabled' in text: 24 alert.accept() 25 else: 26 alert.accept() 27 raise RuntimeError('Unhandeled modal dialog. %s' % text) 28 29 30 def _login(self): 31 """Opens the login page and logs in using the password. 32 We need to login before doing any other change to make sure that 33 we have access to the router. 34 """ 35 self.driver.delete_all_cookies() 36 page_url = urlparse.urljoin(self.admin_interface_url,'login.htm') 37 self.get_url(page_url) 38 self.driver.switch_to_default_content() 39 frame = self.driver.find_element_by_name('mainFrame') 40 self.driver.switch_to_frame(frame) 41 xpath = '//input[@name="ui_pws"]' 42 try: 43 self.set_content_of_text_field_by_xpath('password', xpath, 44 abort_check=True) 45 self.click_button_by_id('412') 46 except WebDriverException, e: 47 element = self.driver.find_element_by_id('60') 48 if 'Duplicate Administrator' in element.text: 49 raise RuntimeError('Cannot login. Someone has already ' 50 'logged into the router. ' + str(e)) 51 else: 52 raise WebDriverException('Cannot login. ' + str(e)) 53 54 55 def get_supported_bands(self): 56 return [{'band': ap_spec.BAND_2GHZ, 57 'channels': ['Auto', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]}, 58 {'band': ap_spec.BAND_5GHZ, 59 'channels': ['Auto', 149, 153, 157, 161, 165]}] 60 61 62 def get_supported_modes(self): 63 return [{'band': ap_spec.BAND_2GHZ, 64 'modes': [ap_spec.MODE_G, ap_spec.MODE_N, 65 ap_spec.MODE_B | ap_spec.MODE_G | ap_spec.MODE_N]}, 66 {'band': ap_spec.BAND_5GHZ, 67 'modes': [ap_spec.MODE_A, ap_spec.MODE_N, 68 ap_spec.MODE_A | ap_spec.MODE_N]}] 69 70 71 def get_number_of_pages(self): 72 return 2 73 74 75 def is_security_mode_supported(self, security_mode): 76 """ 77 Returns if a given security_type is supported. 78 79 @param security_mode: one security modes defined in the APSpec 80 81 @return True if the security mode is supported; False otherwise. 82 83 """ 84 return security_mode in (ap_spec.SECURITY_TYPE_DISABLED, 85 ap_spec.SECURITY_TYPE_WPAPSK, 86 ap_spec.SECURITY_TYPE_WEP) 87 88 89 def navigate_to_page(self, page_number): 90 """ 91 Navigates to the page corresponding to the given page number. 92 93 This method performs the translation between a page number and a url to 94 load. This is used internally by apply_settings. 95 96 @param page_number: page number of the page to load 97 98 """ 99 self._login() 100 if page_number == 1: 101 page_url = urlparse.urljoin(self.admin_interface_url, 'wifi_id.htm') 102 self.get_url(page_url, page_title='Channel and SSID') 103 self.driver.switch_to_default_content() 104 frame = self.driver.find_element_by_name('mainFrame') 105 self.driver.switch_to_frame(frame) 106 self.wait_for_object_by_xpath('//input[@name="wifi_ssid"]') 107 elif page_number == 2: 108 page_url = urlparse.urljoin(self.admin_interface_url, 'wifi_e.htm') 109 try: 110 self.driver.get(page_url) 111 self.wait.until(lambda _:'index.htm' in self.driver.title) 112 except SeleniumTimeoutException, e: 113 # The security page does not load properly, hence we 114 # refresh if we don't get the intended page 115 try: 116 self.driver.get(page_url) 117 self.wait.until(lambda _:'index.htm' in self.driver.title) 118 except SeleniumTimeoutException, e: 119 raise SeleniumTimeoutException('Page did not load. ' 120 + str(e)) 121 try: 122 self.driver.switch_to_default_content() 123 frame = self.driver.find_element_by_name('mainFrame') 124 self.driver.switch_to_frame(frame) 125 self.wait_for_object_by_xpath('//select[@name=wl_authmod]') 126 except (WebDriverException, SeleniumTimeoutException), e: 127 message = str(e) 128 if (not any(alert in message for alert in [ 129 'unexpected alert open', 'An open modal dialog blocked'])): 130 raise RuntimeError(message) 131 return 132 self._security_alert(self.driver.switch_to_alert()) 133 else: 134 raise RuntimeError('Invalid page number passed. Number of pages ' 135 '%d, page value sent was %d' % 136 (self.get_number_of_pages(), page_number)) 137 138 139 def save_page(self, page_number): 140 """Save changes and logout from the router. 141 142 @param page_number: the page number to save as an integer. 143 144 """ 145 self.click_button_by_xpath('//input[@type="button" and ' 146 '@value="Apply Changes"]', 147 alert_handler=self._security_alert) 148 self.set_wait_time(120) 149 try: 150 self.wait.until(lambda _:'Status' in self.driver.title) 151 except SeleniumTimeoutException, e: 152 try: 153 # The webpages of this router have a tendency to not load 154 # completely, hence we refresh before we raise an excpetion. 155 self.driver.refresh() 156 self.wait.until(lambda _:'Status' in self.driver.title) 157 except SeleniumTimeoutException, e: 158 raise SeleniumTimeoutException('The changes were not saved. ' 159 '%s' % str(e)) 160 finally: 161 self.restore_default_wait_time() 162 163 164 def set_ssid(self, ssid): 165 self.add_item_to_command_list(self._set_ssid, (ssid,), 1, 900) 166 167 168 def _set_ssid(self, ssid): 169 xpath = '//input[@name="wifi_ssid"]' 170 if self.current_band == ap_spec.BAND_5GHZ: 171 xpath = '//input[@name="wifi_ssid1"]' 172 self.set_content_of_text_field_by_xpath(ssid, xpath, abort_check=False) 173 self._ssid = ssid 174 175 176 def set_channel(self, channel): 177 self.add_item_to_command_list(self._set_channel, (channel,), 1, 900) 178 179 180 def _set_channel(self, channel): 181 position = self._get_channel_popup_position(channel) 182 channel_choices = ['Auto', '1', '2', '3', '4', '5', '6', '7', '8', 183 '9', '10', '11'] 184 xpath = '//select[@name="wchan"]' 185 if self.current_band == ap_spec.BAND_5GHZ: 186 xpath = '//select[@name="wchan1"]' 187 channel_choices = ['Auto', '149', '153', '157', '161', '165'] 188 self.select_item_from_popup_by_xpath(channel_choices[position], xpath) 189 190 191 def set_mode(self, mode): 192 self.add_item_to_command_list(self._set_mode, (mode,), 1, 900) 193 194 195 def _set_mode(self, mode): 196 mode_mapping = {ap_spec.MODE_G: '802.11g', ap_spec.MODE_A: '802.11a', 197 ap_spec.MODE_N: '802.11n', 198 ap_spec.MODE_A | ap_spec.MODE_N: '802.11a & 802.11n', 199 ap_spec.MODE_B | ap_spec.MODE_G | ap_spec.MODE_N: 200 '802.11b & 802.11g & 802.11n'} 201 mode_name = mode_mapping.get(mode) 202 if not mode_name: 203 raise RuntimeError('The mode %d not supported by router %s. ', 204 hex(mode), self.name) 205 xpath = '//select[@name="wbr"]' 206 if self.current_band == ap_spec.BAND_5GHZ: 207 xpath = '//select[@name="wbr1"]' 208 self.select_item_from_popup_by_xpath(mode_name, xpath, 209 wait_for_xpath=None, 210 alert_handler=self._security_alert) 211 212 213 def set_radio(self, enabled=True): 214 logging.debug('This router (%s) does not support radio', 215 self.name) 216 return None 217 218 219 def set_band(self, band): 220 if band == ap_spec.BAND_2GHZ: 221 self.current_band = ap_spec.BAND_2GHZ 222 elif band == ap_spec.BAND_5GHZ: 223 self.current_band = ap_spec.BAND_5GHZ 224 else: 225 raise RuntimeError('Invalid band sent %s' % band) 226 227 228 def _set_security(self, option, wait_for_xpath=None): 229 popup = '//select[@name="wl_authmod"]' 230 if self.current_band == ap_spec.BAND_5GHZ: 231 popup = '//select[@name="wl_authmod1"]' 232 try: 233 self.select_item_from_popup_by_xpath(option, popup, 234 wait_for_xpath=wait_for_xpath, 235 alert_handler= 236 self._security_alert) 237 except WebDriverException, e: 238 message = str(e) 239 if 'Selecting WEP Encryption will disable the WPS' in message: 240 alert = self.driver.switch_to_alert() 241 alert.accept() 242 243 244 def set_security_disabled(self): 245 self.add_item_to_command_list(self._set_security_disabled, (), 2, 1000) 246 247 248 def _set_security_disabled(self): 249 self._set_security('Disabled') 250 251 252 def set_security_wep(self, key_value, authentication): 253 self.add_item_to_command_list(self._set_security_wep, 254 (key_value, authentication), 2, 1000) 255 256 257 def _set_security_wep(self, key_value, authentication): 258 text_field = '//input[@name="wl_phrase"]' 259 generate_button = '//input[@id="811" and type="button" and \ 260 @onclick="Gen64bitkey(0)"]' 261 if self.current_band == ap_spec.BAND_5GHZ: 262 text_field = '//input[@name="wl1_phrase"]' 263 generate_button = '//input[@id="811" and type="button" and \ 264 @onclick="Gen64bitkey(1)"]' 265 self._set_security('64bit WEP', wait_for_xpath=text_field) 266 self.set_content_of_text_field_by_xpath(key_value, text_field, 267 abort_check=True) 268 self.click_button_by_id(generate_button) 269 270 271 def set_security_wpapsk(self, shared_key, update_interval=None): 272 self.add_item_to_command_list(self._set_security_wpapsk, 273 (shared_key, update_interval), 2, 900) 274 275 276 def _set_security_wpapsk(self, shared_key, update_interval=None): 277 auth_popup = '//select[@name="wl_auth"]' 278 psk_field = '//input[@name="wl_wpa_ks_txt"]' 279 if self.current_band == ap_spec.BAND_5GHZ: 280 auth_popup = '//select[@name="wl1_auth"]' 281 psk_field = '//input[@name="wl1_wpa_ks_txt"]' 282 self._set_security('WPA/WPA2-Personal (PSK)', wait_for_xpath=auth_popup) 283 self.select_item_from_popup_by_xpath('WPA-PSK', auth_popup, 284 wait_for_xpath=psk_field, 285 alert_handler= 286 self._security_alert) 287 self.set_content_of_text_field_by_xpath(shared_key, psk_field, 288 abort_check=True) 289 290 291 def is_visibility_supported(self): 292 """ 293 Returns if AP supports setting the visibility (SSID broadcast). 294 295 @return True if supported; False otherwise. 296 """ 297 return False 298 299 300 def is_update_interval_supported(self): 301 """ 302 Returns True if setting the PSK refresh interval is supported. 303 304 @return True is supported; False otherwise 305 """ 306 return False 307