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
5import logging
6import urlparse
7import time
8
9import dynamic_ap_configurator
10import ap_spec
11
12from selenium.common.exceptions import TimeoutException
13from selenium.common.exceptions import WebDriverException
14from selenium.webdriver.support import expected_conditions as ec
15from selenium.webdriver.common.by import By
16
17class WesternDigitalN600APConfigurator(
18        dynamic_ap_configurator.DynamicAPConfigurator):
19    """Base class for objects to configure Western Digital N600 access point
20       using webdriver."""
21
22
23    def _sec_alert(self, alert):
24        text = alert.text
25        if 'Your wireless security mode is not compatible with' in text:
26            alert.accept()
27        elif 'WARNING: Your Wireless-N devices will only operate' in text:
28            alert.accept()
29        elif 'Your new setting will disable Wi-Fi Protected Setup.' in text:
30            alert.accept()
31        elif 'To use WEP security, WPS must be disabled. Proceed ?' in text:
32             alert.accept()
33        elif 'Warning ! Selecting None in Security Mode will make' in text:
34             alert.accept()
35        else:
36           raise RuntimeError('Invalid handler')
37
38
39    def get_number_of_pages(self):
40        return 1
41
42
43    def is_update_interval_supported(self):
44        """
45        Returns True if setting the PSK refresh interval is supported.
46
47        @return True is supported; False otherwise
48        """
49        return False
50
51
52    def is_visibility_supported(self):
53        """
54        Returns if AP supports setting the visibility (SSID broadcast).
55
56        @return True if supported; False otherwise.
57
58        """
59        return False
60
61
62    def get_supported_modes(self):
63        return [{'band': ap_spec.BAND_2GHZ,
64                 'modes': [ap_spec.MODE_B, ap_spec.MODE_G, ap_spec.MODE_N,
65                           ap_spec.MODE_B | ap_spec.MODE_G,
66                           ap_spec.MODE_G | ap_spec.MODE_N,
67                           ap_spec.MODE_B | ap_spec.MODE_G | ap_spec.MODE_N]},
68                {'band': ap_spec.BAND_5GHZ,
69                 'modes': [ap_spec.MODE_A, ap_spec.MODE_N,
70                           ap_spec.MODE_A | ap_spec.MODE_N]}]
71
72
73    def get_supported_bands(self):
74        return [{'band': ap_spec.BAND_2GHZ,
75                 'channels': ['Auto', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]},
76                {'band': ap_spec.BAND_5GHZ,
77                 'channels': ['Auto', 36, 40, 44, 48, 149, 153, 157, 161, 165]}]
78
79
80    def is_security_mode_supported(self, security_mode):
81        """Check if the AP supports this mode of security.
82
83        @param security_mode: Type of security.
84
85        """
86        return security_mode in (ap_spec.SECURITY_TYPE_DISABLED,
87                                 ap_spec.SECURITY_TYPE_WPAPSK,
88                                 ap_spec.SECURITY_TYPE_WPA2PSK,
89                                 ap_spec.SECURITY_TYPE_WEP)
90
91
92    def navigate_to_page(self, page_number):
93        """Navigate to the required page.
94
95        @param page_number: The page number to navigate to.
96
97        """
98        page_url = urlparse.urljoin(self.admin_interface_url, 'wlan.php')
99        self.get_url(page_url, page_title='WESTERN DIGITAL')
100        xpath_found = self.wait_for_objects_by_id(['loginusr', 'ssid'])
101        if 'loginusr' in xpath_found:
102            self._login_to_router()
103        elif 'ssid' not in xpath_found:
104            raise RuntimeError('The page %s did not load or Radio is switched'
105                               'off' % page_url)
106        self.set_radio(enabled=True)
107
108
109    def _login_to_router(self):
110        self.wait_for_object_by_id('loginusr')
111        self.set_content_of_text_field_by_id('admin', 'loginusr',
112                                             abort_check=True)
113        self.wait_for_object_by_id('loginpwd')
114        self.set_content_of_text_field_by_id('password', 'loginpwd',
115                                             abort_check=True)
116        self.click_button_by_xpath('//input[@value="Submit"]')
117        self.wait_for_object_by_id('ssid')
118
119
120    def save_page(self, page_number):
121        """ Save page after applying settings.
122
123        @param page_number: The page number to be saved.
124
125        """
126        self.wait_for_object_by_id('onsumit')
127        self.click_button_by_id('onsumit', alert_handler=self._sec_alert)
128        warning = '//h1[text()="Warning"]'
129        settings_changed = True
130        try:
131            self.wait_for_object_by_xpath(warning)
132            xpath = '//input[@id="onsumit"]'
133            button = self.driver.find_elements_by_xpath(xpath)[1]
134            button.click()
135            self._handle_alert(xpath, self._sec_alert)
136            self.wait_for_object_by_xpath('//input[@value="Ok"]', wait_time=5)
137        except WebDriverException, e:
138            logging.debug('There is a webdriver exception: "%s".', str(e))
139            settings_changed = False
140        if not settings_changed:
141            try:
142                # if settings are not changed, hit 'continue' button.
143                self.driver.find_element_by_id('nochg')
144                self.click_button_by_id('nochg')
145            except WebDriverException, e:
146                logging.debug('There is a webdriver exception: "%s".', str(e))
147
148
149    def set_mode(self, mode, band=None):
150        self.add_item_to_command_list(self._set_mode, (mode,), 1, 900)
151
152
153    def _set_mode(self, mode, band=None):
154        # This is a dummy wait to give enough time for the popup to
155        # load all options.
156        self._wait_for_page_reload()
157        mode_mapping = {ap_spec.MODE_B | ap_spec.MODE_G:'Mixed 802.11 b+g',
158                        ap_spec.MODE_G:'802.11g only',
159                        ap_spec.MODE_B:'802.11b only',
160                        ap_spec.MODE_N:'802.11n only',
161                        ap_spec.MODE_A:'802.11a only',
162                        ap_spec.MODE_G | ap_spec.MODE_N:'Mixed 802.11 g+n',
163                        ap_spec.MODE_B | ap_spec.MODE_G | ap_spec.MODE_N:
164                        'Mixed 802.11 b+g+n',
165                        ap_spec.MODE_A | ap_spec.MODE_N: 'Mixed 802.11 a+n'}
166        mode_id = 'wlan_mode'
167        if self.current_band == ap_spec.BAND_5GHZ:
168            mode_id = 'wlan_mode_Aband'
169        mode_name = ''
170        if mode in mode_mapping.keys():
171            mode_name = mode_mapping[mode]
172        else:
173            raise RuntimeError('The mode selected \'%d\' is not supported by '
174                               ' \'%s\'.', hex(mode), self.name)
175        popup = self.wait_for_object_by_id(mode_id)
176        while popup and not(self.object_by_id_exist(mode_id)):
177            logging.debug('The object %s does not exist', mode_id)
178        # Click is needed so that we can focus and don't get an empty popup.
179        self.driver.find_element_by_id(mode_id).click()
180        self.select_item_from_popup_by_id(mode_name, mode_id,
181                                          alert_handler=self._sec_alert)
182
183
184    def set_ssid(self, ssid):
185        self.add_item_to_command_list(self._set_ssid, (ssid,), 1, 900)
186
187
188    def _set_ssid(self, ssid):
189        self._set_radio(True)
190        ssid_id = 'ssid'
191        if self.current_band == ap_spec.BAND_5GHZ:
192            ssid_id = 'ssid_Aband'
193        self.wait_for_object_by_id(ssid_id)
194        self.set_content_of_text_field_by_id(ssid, ssid_id, abort_check=True)
195        self._ssid = ssid
196
197
198    def _wait_for_page_reload(self):
199        """
200        This router has a tendency to reload the webpage right after we load
201        it. To avoid any exceptions because of this we wait for the page to
202        reload by default.
203        """
204        elements = self.driver.find_elements_by_css_selector('span.checkbox')
205        checkbox = elements[0]
206        ssid_id = 'ssid'
207        if self.current_band == ap_spec.BAND_5GHZ:
208            checkbox = elements[3]
209            ssid_id = 'ssid_Aband'
210        for timer in range(5):   # Waiting for the page to reload
211            try:
212                if ('checkbox.png' in
213                    checkbox.value_of_css_property('background-image')):
214                    break
215            except:
216                pass
217            time.sleep(1)
218        try:
219            if(self.wait_for_object_by_id(ssid_id).is_enabled()):
220                logging.info('The page reload succeeded.')
221        except:
222            raise RuntimeError('The page reload after login failed.')
223
224
225    def set_radio(self, enabled=True):
226        self.add_item_to_command_list(self._set_radio, (enabled, ), 1, 1000)
227
228
229    def _set_radio(self, enabled=True):
230        self._wait_for_page_reload()
231        elements = self.driver.find_elements_by_css_selector('span.checkbox')
232        checkbox = elements[0]
233        ssid = 'ssid'
234        if self.current_band == ap_spec.BAND_5GHZ:
235            checkbox = elements[3]
236            ssid = 'ssid_Aband'
237        image = 'checkbox_off.png'
238        if enabled:
239            image = 'checkbox.png'
240            try:
241                self.wait.until(ec.element_to_be_clickable((By.ID, ssid)))
242            except TimeoutException, e:
243                if not (image in
244                        checkbox.value_of_css_property('background-image')):
245                    checkbox.click()
246                else:
247                    message = 'Radio is not enabled. ' + str(e)
248                    raise TimeoutException(message)
249        elif not (image in checkbox.value_of_css_property('background-image')):
250            checkbox.click()
251
252
253    def set_channel(self, channel):
254        self.add_item_to_command_list(self._set_channel, (channel,), 1, 900)
255
256
257    def _set_channel(self, channel):
258        self._wait_for_page_reload()
259        position = self._get_channel_popup_position(channel)
260        channel_id = 'channel'
261        channel_choices = ['Auto', '2.412 GHz - CH 1', '2.417 GHz - CH 2',
262                           '2.422 GHz - CH 3', '2.427 GHz - CH 4',
263                           '2.432 GHz - CH 5', '2.437 GHz - CH 6',
264                           '2.442 GHz - CH 7', '2.447 GHz - CH 8',
265                           '2.452 GHz - CH 9', '2.457 GHz - CH 10',
266                           '2.462 GHz - CH 11']
267        if self.current_band == ap_spec.BAND_5GHZ:
268            channel_id = 'channel_Aband'
269            channel_choices = ['Auto', '5.180 GHz - CH 36', '5.200 GHz - CH 40',
270                               '5.220 GHz - CH 44', '5.240 GHz - CH 48',
271                               '5.745 GHz - CH 149', '5.765 GHz - CH 153',
272                               '5.785 GHz - CH 157', '5.805 GHz - CH 161',
273                               '5.825 GHz - CH 165']
274        self.wait_for_object_by_id(channel_id)
275        self.select_item_from_popup_by_id(channel_choices[position], channel_id)
276
277
278    def set_channel_width(self, channel_wid):
279        self.add_item_to_command_list(self._set_channel_width, (channel_wid,),
280                                      1, 900)
281
282
283    def _set_channel_width(self, channel_wid):
284        channel_width_choice = ['20 MHz', '20/40 MHz(Auto)']
285        width_id = 'bw'
286        if self.current_band == ap_spec.BAND_5GHZ:
287            width_id = 'bw_Aband'
288        self.select_item_from_popup_by_id(channel_width_choice[channel_wid],
289                                          width_id)
290
291
292    def set_band(self, band):
293        if band == ap_spec.BAND_5GHZ:
294            self.current_band = ap_spec.BAND_5GHZ
295        elif band == ap_spec.BAND_2GHZ:
296            self.current_band = ap_spec.BAND_2GHZ
297        else:
298            raise RuntimeError('Invalid band sent %s' % band)
299        self.set_radio(True)
300
301
302    def _set_security(self, security_type, wait_path=None):
303        self._wait_for_page_reload()
304        sec_id = 'security_type'
305        if self.current_band == ap_spec.BAND_5GHZ:
306            sec_id = 'security_type_Aband'
307            text = '//input[@name="wpapsk_Aband" and @type="text"]'
308        self.wait_for_object_by_id(sec_id, wait_time=5)
309        if self.item_in_popup_by_id_exist(security_type, sec_id):
310            self.select_item_from_popup_by_id(security_type, sec_id,
311                                              wait_for_xpath=wait_path,
312                                              alert_handler=self._sec_alert)
313        elif security_type == 'WEP':
314            raise RuntimeError('Could not find WEP security_type in dropdown. '
315                               'Please check the network mode. '
316                               'Some of the modes do not support WEP.')
317        else:
318            raise RuntimeError('The dropdown %s does not have item %s' %
319                               (sec_id, security_type))
320
321
322    def set_security_disabled(self):
323        self.add_item_to_command_list(self._set_security_disabled, (), 1, 1000)
324
325
326    def _set_security_disabled(self):
327        self._set_security('None')
328
329
330    def set_security_wep(self, key_value, authentication):
331        self.add_item_to_command_list(self._set_security_wep,
332                                      (key_value, authentication), 1, 1000)
333
334
335    def _set_security_wep(self, key_value, authentication):
336        # WEP is not supported for Wireless-N only and Mixed (g+n, b+g+n) mode.
337        # WEP does not show up in the list, no alert is thrown.
338        text = '//input[@name="wepkey_64"]'
339        if self.current_band == ap_spec.BAND_5GHZ:
340            text = '//input[@name="wepkey_64_Aband"]'
341        self._set_security('WEP', text)
342        self.set_content_of_text_field_by_xpath(key_value, text,
343                                                abort_check=True)
344
345
346    def set_security_wpapsk(self, security, shared_key, update_interval=None):
347        # WEP and WPA-Personal are not supported for Wireless-N only mode,
348        self.add_item_to_command_list(self._set_security_wpapsk,
349                                      (security, shared_key,), 1, 1000)
350
351
352    def _set_security_wpapsk(self, security, shared_key):
353        text = 'wpapsk'
354        if self.current_band == ap_spec.BAND_5GHZ:
355            text = 'wpapsk_Aband'
356        if security == ap_spec.SECURITY_TYPE_WPAPSK:
357            self._set_security('WPA - Personal', '//input[@id="%s"]' % text)
358        else:
359            self._set_security('WPA2 - Personal', '//input[@id="%s"]' % text)
360        self.set_content_of_text_field_by_id(shared_key, text,
361                                             abort_check=False)
362
363
364    def set_visibility(self, visible=True):
365        # The SSID Broadcast can't be reliable set on this AP beacuse the CSS
366        # property for backgroung-image always returns OFF.
367        return None
368