• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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