Home | History | Annotate | Download | only in ap_configurators
      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 
      5 from autotest_lib.client.common_lib.cros.network import iw_runner
      6 
      7 
      8 # Supported bands
      9 BAND_2GHZ = '2.4GHz'
     10 BAND_5GHZ = '5GHz'
     11 
     12 # List of valid bands.
     13 VALID_BANDS = [BAND_2GHZ, BAND_5GHZ]
     14 
     15 # List of valid 802.11 protocols (modes).
     16 MODE_A = 0x01
     17 MODE_B = 0x02
     18 MODE_G = 0x04
     19 MODE_N = 0x08
     20 MODE_AC = 0x10
     21 MODE_AUTO = 0x20
     22 MODE_M = MODE_A | MODE_B | MODE_G # Used for standard maintenance
     23 MODE_D = MODE_A | MODE_B | MODE_N # International roaming extensions
     24 
     25 # List of valid modes.
     26 VALID_MODES = [MODE_A, MODE_AC, MODE_AUTO, MODE_B, MODE_D, MODE_G, MODE_M,
     27                MODE_N]
     28 VALID_2GHZ_MODES = [MODE_B, MODE_G, MODE_N]
     29 VALID_5GHZ_MODES = [MODE_A, MODE_AC, MODE_N]
     30 
     31 # Supported security types
     32 SECURITY_TYPE_DISABLED = iw_runner.SECURITY_OPEN
     33 SECURITY_TYPE_WEP = iw_runner.SECURITY_WEP
     34 SECURITY_TYPE_WPAPSK = iw_runner.SECURITY_WPA
     35 SECURITY_TYPE_WPA2PSK = iw_runner.SECURITY_WPA2
     36 # Mixed mode security is wpa/wpa2
     37 SECURITY_TYPE_MIXED = iw_runner.SECURITY_MIXED
     38 
     39 WEP_AUTHENTICATION_OPEN = object()
     40 WEP_AUTHENTICATION_SHARED = object()
     41 
     42 # List of valid securities.
     43 # TODO (krisr) the configurators do not support WEP at this time.
     44 VALID_SECURITIES = [SECURITY_TYPE_DISABLED,
     45                     SECURITY_TYPE_WPAPSK,
     46                     SECURITY_TYPE_WPA2PSK]
     47 
     48 # List of valid channels.
     49 VALID_2GHZ_CHANNELS = range(1,15)
     50 VALID_5GHZ_CHANNELS = [36, 40, 44, 48, 149, 153, 157, 161, 165]
     51 
     52 # Frequency to channel conversion table
     53 CHANNEL_TABLE = {2412: 1, 2417: 2, 2422: 3,
     54                  2427: 4, 2432: 5, 2437: 6,
     55                  2442: 7, 2447: 8, 2452: 9,
     56                  2457: 10, 2462: 11, 2467: 12,
     57                  2472: 13, 2484: 14, 5180: 36,
     58                  5200: 40, 5220: 44, 5240: 48,
     59                  5745: 149, 5765: 153, 5785: 157,
     60                  5805: 161, 5825: 165}
     61 
     62 # This only works because the frequency table is one to one
     63 # for channels/frequencies.
     64 FREQUENCY_TABLE = dict((v,k) for k,v in CHANNEL_TABLE.iteritems())
     65 
     66 # Configurator type
     67 CONFIGURATOR_STATIC = 1
     68 CONFIGURATOR_DYNAMIC = 2
     69 CONFIGURATOR_ANY = 3
     70 
     71 # Default values
     72 DEFAULT_BAND = BAND_2GHZ
     73 
     74 DEFAULT_2GHZ_MODE = MODE_G
     75 DEFAULT_5GHZ_MODE = MODE_A
     76 
     77 DEFAULT_SECURITY_TYPE = SECURITY_TYPE_DISABLED
     78 
     79 DEFAULT_2GHZ_CHANNEL = 5
     80 DEFAULT_5GHZ_CHANNEL = 149
     81 
     82 # Convenience method to convert modes and bands to human readable strings.
     83 def band_string_for_band(band):
     84     """Returns a human readable string of the band
     85 
     86     @param band: band object
     87     @returns: string representation of the band
     88     """
     89     if band == BAND_2GHZ:
     90         return '2.4 GHz'
     91     elif band == BAND_5GHZ:
     92         return '5 GHz'
     93 
     94 
     95 def mode_string_for_mode(mode):
     96     """Returns a human readable string of the mode.
     97 
     98     @param mode: integer, the mode to convert.
     99     @returns: string representation of the mode
    100     """
    101     string_table = {MODE_A:'a', MODE_AC:'ac', MODE_B:'b', MODE_G:'g',
    102                     MODE_N:'n'}
    103 
    104     if mode == MODE_AUTO:
    105         return 'Auto'
    106     total = 0
    107     string = ''
    108     for current_mode in sorted(string_table.keys()):
    109         i = current_mode & mode
    110         total = total | i
    111         if i in string_table:
    112             string = string + string_table[i] + '/'
    113     if total == MODE_M:
    114         string = 'm'
    115     elif total == MODE_D:
    116         string = 'd'
    117     if string[-1] == '/':
    118         return string[:-1]
    119     return string
    120 
    121 
    122 class APSpec(object):
    123     """Object to specify an APs desired capabilities.
    124 
    125     The APSpec object is immutable.  All of the parameters are optional.
    126     For those not given the defaults listed above will be used.  Validation
    127     is done on the values to make sure the spec created is valid.  If
    128     validation fails a ValueError is raised.
    129     """
    130 
    131 
    132     def __init__(self, visible=True, security=SECURITY_TYPE_DISABLED,
    133                  band=None, mode=None, channel=None, hostnames=None,
    134                  configurator_type=CONFIGURATOR_ANY,
    135                  # lab_ap set to true means the AP must be in the lab;
    136                  # if it set to false the AP is outside of the lab.
    137                  lab_ap=True):
    138         super(APSpec, self).__init__()
    139         self._visible = visible
    140         self._security = security
    141         self._mode = mode
    142         self._channel = channel
    143         self._hostnames = hostnames
    144         self._configurator_type = configurator_type
    145         self._lab_ap = lab_ap
    146         self._webdriver_hostname = None
    147 
    148         if not self._channel and (self._mode == MODE_N or not self._mode):
    149             if band == BAND_2GHZ or not band:
    150                 self._channel = DEFAULT_2GHZ_CHANNEL
    151                 if not self._mode:
    152                     self._mode = DEFAULT_2GHZ_MODE
    153             elif band == BAND_5GHZ:
    154                 self._channel = DEFAULT_5GHZ_CHANNEL
    155                 if not self._mode:
    156                     self._mode = DEFAULT_5GHZ_MODE
    157             else:
    158                 raise ValueError('Invalid Band.')
    159 
    160         self._validate_channel_and_mode()
    161 
    162         if ((band == BAND_2GHZ and self._mode not in VALID_2GHZ_MODES) or
    163             (band == BAND_5GHZ and self._mode not in VALID_5GHZ_MODES)):
    164             raise ValueError('Conflicting band and modes/channels.')
    165 
    166         self._validate_security()
    167 
    168 
    169     def __str__(self):
    170         return ('AP Specification:\n'
    171                 'visible=%r\n'
    172                 'security=%s\n'
    173                 'band=%s\n'
    174                 'mode=%s\n'
    175                 'channel=%d\n'
    176                 'password=%s' % (self._visible, self._security,
    177                                  band_string_for_band(self.band),
    178                                  mode_string_for_mode(self._mode),
    179                                  self._channel, self._password))
    180 
    181 
    182     @property
    183     def password(self):
    184         """Returns the password for password supported secured networks."""
    185         return self._password
    186 
    187 
    188 
    189     @property
    190     def visible(self):
    191         """Returns if the SSID is visible or not."""
    192         return self._visible
    193 
    194 
    195     @property
    196     def security(self):
    197         """Returns the type of security."""
    198         return self._security
    199 
    200 
    201     @property
    202     def band(self):
    203         """Return the band."""
    204         if self._channel in VALID_2GHZ_CHANNELS:
    205             return BAND_2GHZ
    206         return BAND_5GHZ
    207 
    208 
    209     @property
    210     def mode(self):
    211         """Return the mode."""
    212         return self._mode
    213 
    214 
    215     @property
    216     def channel(self):
    217         """Return the channel."""
    218         return self._channel
    219 
    220 
    221     @property
    222     def frequency(self):
    223         """Return the frequency equivalent of the channel."""
    224         return FREQUENCY_TABLE[self._channel]
    225 
    226 
    227     @property
    228     def hostnames(self):
    229         """Return the hostnames; this may be None."""
    230         return self._hostnames
    231 
    232 
    233     @property
    234     def configurator_type(self):
    235         """Returns the configurator type."""
    236         return self._configurator_type
    237 
    238 
    239     @property
    240     def lab_ap(self):
    241         """Returns if the AP should be in the lab or not."""
    242         return self._lab_ap
    243 
    244 
    245     @property
    246     def webdriver_hostname(self):
    247         """Returns locked webdriver hostname."""
    248         return self._webdriver_hostname
    249 
    250 
    251     @webdriver_hostname.setter
    252     def webdriver_hostname(self, value):
    253         """Sets webdriver_hostname to locked instance.
    254 
    255         @param value: locked webdriver hostname
    256 
    257         """
    258         self._webdriver_hostname = value
    259 
    260 
    261     def _validate_channel_and_mode(self):
    262         """Validates the channel and mode selected are correct.
    263 
    264         raises ValueError: if the channel or mode selected is invalid
    265         """
    266         if self._channel and self._mode:
    267             if ((self._channel in VALID_2GHZ_CHANNELS and
    268                  self._mode not in VALID_2GHZ_MODES) or
    269                 (self._channel in VALID_5GHZ_CHANNELS and
    270                  self._mode not in VALID_5GHZ_MODES)):
    271                 raise ValueError('Conflicting mode/channel has been selected.')
    272         elif self._channel:
    273             if self._channel in VALID_2GHZ_CHANNELS:
    274                 self._mode = DEFAULT_2GHZ_MODE
    275             elif self._channel in VALID_5GHZ_CHANNELS:
    276                 self._mode = DEFAULT_5GHZ_MODE
    277             else:
    278                 raise ValueError('Invalid channel passed.')
    279         else:
    280             if self._mode in VALID_2GHZ_MODES:
    281                 self._channel = DEFAULT_2GHZ_CHANNEL
    282             elif self._mode in VALID_5GHZ_MODES:
    283                 self._channel = DEFAULT_5GHZ_CHANNEL
    284             else:
    285                 raise ValueError('Invalid mode passed.')
    286 
    287 
    288     def _validate_security(self):
    289         """Sets a password for security settings that need it.
    290 
    291         raises ValueError: if the security setting passed is invalid.
    292         """
    293         if self._security == SECURITY_TYPE_DISABLED:
    294             self._password = None
    295         elif (self._security == SECURITY_TYPE_WPAPSK or
    296              self._security == SECURITY_TYPE_WPA2PSK):
    297              self._password = 'chromeos'
    298         else:
    299             raise ValueError('Invalid security passed.')
    300