Home | History | Annotate | Download | only in networking
      1 #!/usr/bin/python
      2 
      3 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
      4 # Use of this source code is governed by a BSD-style license that can be
      5 # found in the LICENSE file.
      6 
      7 import dbus
      8 import logging
      9 import logging.handlers
     10 import multiprocessing
     11 
     12 import common
     13 from autotest_lib.client.common_lib import utils
     14 from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes
     15 from autotest_lib.client.cros import xmlrpc_server
     16 from autotest_lib.client.cros import constants
     17 from autotest_lib.client.cros import cros_ui
     18 from autotest_lib.client.cros import tpm_store
     19 from autotest_lib.client.cros.networking import shill_proxy
     20 from autotest_lib.client.cros.networking import wifi_proxy
     21 from autotest_lib.client.cros.power import sys_power
     22 
     23 
     24 class ShillXmlRpcDelegate(xmlrpc_server.XmlRpcDelegate):
     25     """Exposes methods called remotely during WiFi autotests.
     26 
     27     All instance methods of this object without a preceding '_' are exposed via
     28     an XMLRPC server.  This is not a stateless handler object, which means that
     29     if you store state inside the delegate, that state will remain around for
     30     future calls.
     31 
     32     """
     33 
     34     DEFAULT_TEST_PROFILE_NAME = 'test'
     35     DBUS_DEVICE = 'Device'
     36 
     37     def __init__(self):
     38         self._wifi_proxy = wifi_proxy.WifiProxy()
     39         self._tpm_store = tpm_store.TPMStore()
     40 
     41 
     42     def __enter__(self):
     43         super(ShillXmlRpcDelegate, self).__enter__()
     44         if not cros_ui.stop(allow_fail=True):
     45             logging.error('UI did not stop, there could be trouble ahead.')
     46         self._tpm_store.__enter__()
     47 
     48 
     49     def __exit__(self, exception, value, traceback):
     50         super(ShillXmlRpcDelegate, self).__exit__(exception, value, traceback)
     51         self._tpm_store.__exit__(exception, value, traceback)
     52         self.enable_ui()
     53 
     54 
     55     @xmlrpc_server.dbus_safe(False)
     56     def create_profile(self, profile_name):
     57         """Create a shill profile.
     58 
     59         @param profile_name string name of profile to create.
     60         @return True on success, False otherwise.
     61 
     62         """
     63         self._wifi_proxy.manager.CreateProfile(profile_name)
     64         return True
     65 
     66 
     67     @xmlrpc_server.dbus_safe(False)
     68     def push_profile(self, profile_name):
     69         """Push a shill profile.
     70 
     71         @param profile_name string name of profile to push.
     72         @return True on success, False otherwise.
     73 
     74         """
     75         self._wifi_proxy.manager.PushProfile(profile_name)
     76         return True
     77 
     78 
     79     @xmlrpc_server.dbus_safe(False)
     80     def pop_profile(self, profile_name):
     81         """Pop a shill profile.
     82 
     83         @param profile_name string name of profile to pop.
     84         @return True on success, False otherwise.
     85 
     86         """
     87         if profile_name is None:
     88             self._wifi_proxy.manager.PopAnyProfile()
     89         else:
     90             self._wifi_proxy.manager.PopProfile(profile_name)
     91         return True
     92 
     93 
     94     @xmlrpc_server.dbus_safe(False)
     95     def remove_profile(self, profile_name):
     96         """Remove a profile from disk.
     97 
     98         @param profile_name string name of profile to remove.
     99         @return True on success, False otherwise.
    100 
    101         """
    102         self._wifi_proxy.manager.RemoveProfile(profile_name)
    103         return True
    104 
    105 
    106     @xmlrpc_server.dbus_safe(False)
    107     def clean_profiles(self):
    108         """Pop and remove shill profiles above the default profile.
    109 
    110         @return True on success, False otherwise.
    111 
    112         """
    113         while True:
    114             active_profile = self._wifi_proxy.get_active_profile()
    115             profile_name = self._wifi_proxy.dbus2primitive(
    116                     active_profile.GetProperties(utf8_strings=True)['Name'])
    117             if profile_name == 'default':
    118                 return True
    119             self._wifi_proxy.manager.PopProfile(profile_name)
    120             self._wifi_proxy.manager.RemoveProfile(profile_name)
    121 
    122 
    123     @xmlrpc_server.dbus_safe(False)
    124     def configure_service_by_guid(self, raw_params):
    125         """Configure a service referenced by a GUID.
    126 
    127         @param raw_params serialized ConfigureServiceParameters.
    128 
    129         """
    130         params = xmlrpc_datatypes.deserialize(raw_params)
    131         shill = self._wifi_proxy
    132         properties = {}
    133         if params.autoconnect is not None:
    134             properties[shill.SERVICE_PROPERTY_AUTOCONNECT] = params.autoconnect
    135         if params.passphrase is not None:
    136             properties[shill.SERVICE_PROPERTY_PASSPHRASE] = params.passphrase
    137         if properties:
    138             self._wifi_proxy.configure_service_by_guid(params.guid, properties)
    139         return True
    140 
    141 
    142     @xmlrpc_server.dbus_safe(False)
    143     def configure_wifi_service(self, raw_params):
    144         """Configure a WiFi service
    145 
    146         @param raw_params serialized AssociationParameters.
    147         @return True on success, False otherwise.
    148 
    149         """
    150         params = xmlrpc_datatypes.deserialize(raw_params)
    151         return self._wifi_proxy.configure_wifi_service(
    152                 params.ssid,
    153                 params.security,
    154                 params.security_parameters,
    155                 save_credentials=params.save_credentials,
    156                 station_type=params.station_type,
    157                 hidden_network=params.is_hidden,
    158                 guid=params.guid,
    159                 autoconnect=params.autoconnect)
    160 
    161 
    162     def connect_wifi(self, raw_params):
    163         """Block and attempt to connect to wifi network.
    164 
    165         @param raw_params serialized AssociationParameters.
    166         @return serialized AssociationResult
    167 
    168         """
    169         logging.debug('connect_wifi()')
    170         params = xmlrpc_datatypes.deserialize(raw_params)
    171         params.security_config.install_client_credentials(self._tpm_store)
    172         wifi_if = params.bgscan_config.interface
    173         if wifi_if is None:
    174             logging.info('Using default interface for bgscan configuration')
    175             interfaces = self.list_controlled_wifi_interfaces()
    176             if not interfaces:
    177                 return xmlrpc_datatypes.AssociationResult(
    178                         failure_reason='No wifi interfaces found?')
    179 
    180             if len(interfaces) > 1:
    181                 logging.error('Defaulting to first interface of %r', interfaces)
    182             wifi_if = interfaces[0]
    183         if not self._wifi_proxy.configure_bgscan(
    184                 wifi_if,
    185                 method=params.bgscan_config.method,
    186                 short_interval=params.bgscan_config.short_interval,
    187                 long_interval=params.bgscan_config.long_interval,
    188                 signal=params.bgscan_config.signal):
    189             return xmlrpc_datatypes.AssociationResult(
    190                     failure_reason='Failed to configure bgscan')
    191 
    192         raw = self._wifi_proxy.connect_to_wifi_network(
    193                 params.ssid,
    194                 params.security,
    195                 params.security_parameters,
    196                 params.save_credentials,
    197                 station_type=params.station_type,
    198                 hidden_network=params.is_hidden,
    199                 guid=params.guid,
    200                 discovery_timeout_seconds=params.discovery_timeout,
    201                 association_timeout_seconds=params.association_timeout,
    202                 configuration_timeout_seconds=params.configuration_timeout)
    203         result = xmlrpc_datatypes.AssociationResult.from_dbus_proxy_output(raw)
    204         return result
    205 
    206 
    207     @xmlrpc_server.dbus_safe(False)
    208     def delete_entries_for_ssid(self, ssid):
    209         """Delete a profile entry.
    210 
    211         @param ssid string of WiFi service for which to delete entries.
    212         @return True on success, False otherwise.
    213 
    214         """
    215         shill = self._wifi_proxy
    216         for profile in shill.get_profiles():
    217             profile_properties = shill.dbus2primitive(
    218                     profile.GetProperties(utf8_strings=True))
    219             entry_ids = profile_properties[shill.PROFILE_PROPERTY_ENTRIES]
    220             for entry_id in entry_ids:
    221                 entry = profile.GetEntry(entry_id)
    222                 if shill.dbus2primitive(entry[shill.ENTRY_FIELD_NAME]) == ssid:
    223                     profile.DeleteEntry(entry_id)
    224         return True
    225 
    226 
    227     def init_test_network_state(self):
    228         """Create a clean slate for tests with respect to remembered networks.
    229 
    230         For shill, this means popping and removing profiles, removing all WiFi
    231         entries from the default profile, and pushing a 'test' profile.
    232 
    233         @return True iff operation succeeded, False otherwise.
    234 
    235         """
    236         self.clean_profiles()
    237         self._wifi_proxy.remove_all_wifi_entries()
    238         self.remove_profile(self.DEFAULT_TEST_PROFILE_NAME)
    239         worked = self.create_profile(self.DEFAULT_TEST_PROFILE_NAME)
    240         if worked:
    241             worked = self.push_profile(self.DEFAULT_TEST_PROFILE_NAME)
    242         return worked
    243 
    244 
    245     @xmlrpc_server.dbus_safe(None)
    246     def list_controlled_wifi_interfaces(self):
    247         """List WiFi interfaces controlled by shill.
    248 
    249         @return list of string WiFi device names (e.g. ['mlan0'])
    250 
    251         """
    252         ret = []
    253         devices = self._wifi_proxy.get_devices()
    254         for device in devices:
    255             properties = self._wifi_proxy.dbus2primitive(
    256                     device.GetProperties(utf8_strings=True))
    257             if properties[self._wifi_proxy.DEVICE_PROPERTY_TYPE] != 'wifi':
    258                 continue
    259             ret.append(properties[self._wifi_proxy.DEVICE_PROPERTY_NAME])
    260         return ret
    261 
    262 
    263     @xmlrpc_server.dbus_safe(False)
    264     def disconnect(self, ssid):
    265         """Attempt to disconnect from the given ssid.
    266 
    267         Blocks until disconnected or operation has timed out.  Returns True iff
    268         disconnect was successful.
    269 
    270         @param ssid string network to disconnect from.
    271         @return bool True on success, False otherwise.
    272 
    273         """
    274         logging.debug('disconnect()')
    275         result = self._wifi_proxy.disconnect_from_wifi_network(ssid)
    276         successful, duration, message = result
    277         if successful:
    278             level = logging.info
    279         else:
    280             level = logging.error
    281         level('Disconnect result: %r, duration: %d, reason: %s',
    282               successful, duration, message)
    283         return successful is True
    284 
    285 
    286     def wait_for_service_states(self, ssid, states, timeout_seconds):
    287         """Wait for service to achieve one state out of a list of states.
    288 
    289         @param ssid string the network to connect to (e.g. 'GoogleGuest').
    290         @param states tuple the states for which to wait
    291         @param timeout_seconds int seconds to wait for a state
    292 
    293         """
    294         return self._wifi_proxy.wait_for_service_states(
    295                 ssid, states, timeout_seconds)
    296 
    297 
    298     @xmlrpc_server.dbus_safe(None)
    299     def get_service_order(self):
    300         """Get the shill service order.
    301 
    302         @return string service order on success, None otherwise.
    303 
    304         """
    305         return str(self._wifi_proxy.manager.GetServiceOrder())
    306 
    307 
    308     @xmlrpc_server.dbus_safe(False)
    309     def set_service_order(self, order):
    310         """Set the shill service order.
    311 
    312         @param order string comma-delimited service order (eg. 'ethernet,wifi')
    313         @return bool True on success, False otherwise.
    314 
    315         """
    316         self._wifi_proxy.manager.SetServiceOrder(dbus.String(order))
    317         return True
    318 
    319 
    320     @xmlrpc_server.dbus_safe(None)
    321     def get_service_properties(self, ssid):
    322         """Get a dict of properties for a service.
    323 
    324         @param ssid string service to get properties for.
    325         @return dict of Python friendly native types or None on failures.
    326 
    327         """
    328         discovery_params = {self._wifi_proxy.SERVICE_PROPERTY_TYPE: 'wifi',
    329                             self._wifi_proxy.SERVICE_PROPERTY_NAME: ssid}
    330         service_path = self._wifi_proxy.manager.FindMatchingService(
    331                 discovery_params)
    332         service_object = self._wifi_proxy.get_dbus_object(
    333                 self._wifi_proxy.DBUS_TYPE_SERVICE, service_path)
    334         service_properties = service_object.GetProperties(
    335                 utf8_strings=True)
    336         return self._wifi_proxy.dbus2primitive(service_properties)
    337 
    338 
    339     @xmlrpc_server.dbus_safe(None)
    340     def get_manager_properties(self):
    341         manager_props = self._wifi_proxy.manager.GetProperties(utf8_strings=True)
    342         return self._wifi_proxy.dbus2primitive(manager_props)
    343 
    344 
    345     @xmlrpc_server.dbus_safe(None)
    346     def get_manager_property(self, property_name):
    347         prop_value = self._wifi_proxy.get_dbus_property(
    348                 self._wifi_proxy.manager,  property_name)
    349         return self._wifi_proxy.dbus2primitive(prop_value)
    350 
    351 
    352     @xmlrpc_server.dbus_safe(False)
    353     def set_manager_property(self, property_name, property_value):
    354         self._wifi_proxy.set_dbus_property(self._wifi_proxy.manager,
    355                                            property_name, property_value)
    356         return True
    357 
    358     @xmlrpc_server.dbus_safe(False)
    359     def set_optional_manager_property(self, property_name, property_value):
    360         """Set optional manager property.
    361 
    362         @param property_name String name of property to set
    363         @param property_value String value to set property to
    364         @return True on success, False otherwise.
    365 
    366         """
    367         self._wifi_proxy.set_optional_dbus_property(
    368                 self._wifi_proxy.manager, property_name, property_value)
    369         return True
    370 
    371     @xmlrpc_server.dbus_safe(False)
    372     def get_active_wifi_SSIDs(self):
    373         """@return list of string SSIDs with at least one BSS we've scanned."""
    374         return self._wifi_proxy.get_active_wifi_SSIDs()
    375 
    376 
    377     @xmlrpc_server.dbus_safe(False)
    378     def set_sched_scan(self, enable):
    379         """Configure scheduled scan.
    380 
    381         @param enable bool flag indicating to enable/disable scheduled scan.
    382         @return True on success, False otherwise.
    383 
    384         """
    385         self._wifi_proxy.manager.set_sched_scan(enable)
    386         return True
    387 
    388 
    389     def enable_ui(self):
    390         """@return True iff the UI was successfully started."""
    391         return cros_ui.start(allow_fail=True, wait_for_login_prompt=False) == 0
    392 
    393 
    394     def sync_time_to(self, epoch_seconds):
    395         """Sync time on the DUT to |epoch_seconds| from the epoch.
    396 
    397         @param epoch_seconds: float number of seconds from the epoch.
    398 
    399         """
    400         utils.run('date -u --set=@%f' % epoch_seconds)
    401         return True
    402 
    403 
    404     @staticmethod
    405     def do_suspend(seconds):
    406         """Suspend DUT using the power manager.
    407 
    408         @param seconds: The number of seconds to suspend the device.
    409 
    410         """
    411         return sys_power.do_suspend(seconds)
    412 
    413 
    414     @staticmethod
    415     def do_suspend_bg(seconds):
    416         """Suspend DUT using the power manager - non-blocking.
    417 
    418         @param seconds int The number of seconds to suspend the device.
    419 
    420         """
    421         process = multiprocessing.Process(target=sys_power.do_suspend,
    422                                           args=(seconds, 1))
    423         process.start()
    424         return True
    425 
    426 
    427     @xmlrpc_server.dbus_safe(None)
    428     def get_dbus_property_on_device(self, wifi_interface, prop_name):
    429         """Get a property for the given WiFi device.
    430 
    431         @param wifi_interface: string name of interface being queried.
    432         @param prop_name: the name of the property.
    433         @return the current value of the property.
    434 
    435         """
    436         dbus_object = self._wifi_proxy.find_object(
    437                 self.DBUS_DEVICE, {'Name': wifi_interface})
    438         if dbus_object is None:
    439             return None
    440 
    441         object_properties = dbus_object.GetProperties(utf8_strings=True)
    442         if prop_name not in object_properties:
    443             return None
    444 
    445         return self._wifi_proxy.dbus2primitive(
    446                 object_properties[prop_name])
    447 
    448 
    449     @xmlrpc_server.dbus_safe(False)
    450     def set_dbus_property_on_device(self, wifi_interface, prop_name, value):
    451         """Set a property on the given WiFi device.
    452 
    453         @param wifi_interface: the device to set a property for.
    454         @param prop_name: the name of the property.
    455         @param value: the desired value of the property.
    456         @return True if successful, False otherwise.
    457 
    458         """
    459         device_object = self._wifi_proxy.find_object(
    460                 self.DBUS_DEVICE, {'Name': wifi_interface})
    461         if device_object is None:
    462             return False
    463 
    464         shill_proxy.ShillProxy.set_dbus_property(device_object,
    465                                                  prop_name,
    466                                                  value)
    467         return True
    468 
    469 
    470     @xmlrpc_server.dbus_safe(False)
    471     def request_roam_dbus(self, bssid, interface):
    472         """Request that we roam to the specified BSSID.
    473 
    474         Note that this operation assumes that:
    475 
    476         1) We're connected to an SSID for which |bssid| is a member.
    477         2) There is a BSS with an appropriate ID in our scan results.
    478 
    479         @param bssid: string BSSID of BSS to roam to.
    480         @param interface: string name of interface to request roam for.
    481 
    482         """
    483 
    484         device_object = self._wifi_proxy.find_object(
    485                 self.DBUS_DEVICE, {'Name': interface})
    486         if device_object is None:
    487             return False
    488         device_object.RequestRoam(bssid)
    489         return True
    490 
    491 
    492     @xmlrpc_server.dbus_safe(False)
    493     def set_device_enabled(self, wifi_interface, enabled):
    494         """Enable or disable the WiFi device.
    495 
    496         @param wifi_interface: string name of interface being modified.
    497         @param enabled: boolean; true if this device should be enabled,
    498                 false if this device should be disabled.
    499         @return True if it worked; false, otherwise
    500 
    501         """
    502         interface = {'Name': wifi_interface}
    503         dbus_object = self._wifi_proxy.find_object(self.DBUS_DEVICE,
    504                                                    interface)
    505         if dbus_object is None:
    506             return False
    507 
    508         if enabled:
    509             dbus_object.Enable()
    510         else:
    511             dbus_object.Disable()
    512         return True
    513 
    514 
    515     def discover_tdls_link(self, wifi_interface, peer_mac_address):
    516         """Send a TDLS Discover to |peer_mac_address| on |wifi_interface|.
    517 
    518         @param wifi_interface: string name of interface to send the discover on.
    519         @param peer_mac_address: string mac address of the TDLS peer device.
    520 
    521         @return True if it the operation was initiated; False otherwise
    522 
    523         """
    524         device_object = self._wifi_proxy.find_object(
    525                 self.DBUS_DEVICE, {'Name': wifi_interface})
    526         if device_object is None:
    527             return False
    528         device_object.PerformTDLSOperation('Discover', peer_mac_address)
    529         return True
    530 
    531 
    532     def establish_tdls_link(self, wifi_interface, peer_mac_address):
    533         """Establish a TDLS link with |peer_mac_address| on |wifi_interface|.
    534 
    535         @param wifi_interface: string name of interface to establish a link on.
    536         @param peer_mac_address: string mac address of the TDLS peer device.
    537 
    538         @return True if it the operation was initiated; False otherwise
    539 
    540         """
    541         device_object = self._wifi_proxy.find_object(
    542                 self.DBUS_DEVICE, {'Name': wifi_interface})
    543         if device_object is None:
    544             return False
    545         device_object.PerformTDLSOperation('Setup', peer_mac_address)
    546         return True
    547 
    548 
    549     @xmlrpc_server.dbus_safe(False)
    550     def query_tdls_link(self, wifi_interface, peer_mac_address):
    551         """Query the TDLS link with |peer_mac_address| on |wifi_interface|.
    552 
    553         @param wifi_interface: string name of interface to establish a link on.
    554         @param peer_mac_address: string mac address of the TDLS peer device.
    555 
    556         @return string indicating the current TDLS link status.
    557 
    558         """
    559         device_object = self._wifi_proxy.find_object(
    560                 self.DBUS_DEVICE, {'Name': wifi_interface})
    561         if device_object is None:
    562             return None
    563         return self._wifi_proxy.dbus2primitive(
    564                 device_object.PerformTDLSOperation('Status', peer_mac_address))
    565 
    566 
    567     @xmlrpc_server.dbus_safe(False)
    568     def add_wake_packet_source(self, wifi_interface, source_ip):
    569         """Set up the NIC to wake on packets from the given source IP.
    570 
    571         @param wifi_interface: string name of interface to establish WoWLAN on.
    572         @param source_ip: string IP address of packet source, i.e. "127.0.0.1"
    573 
    574         @return True on success, False otherwise.
    575 
    576         """
    577         device_object = self._wifi_proxy.find_object(
    578                 self.DBUS_DEVICE, {'Name': wifi_interface})
    579         if device_object is None:
    580             return False
    581         device_object.AddWakeOnPacketConnection(source_ip)
    582         return True
    583 
    584 
    585     @xmlrpc_server.dbus_safe(False)
    586     def remove_wake_packet_source(self, wifi_interface, source_ip):
    587         """Stop waking on packets from the given source IP.
    588 
    589         @param wifi_interface: string name of interface to establish WoWLAN on.
    590         @param source_ip: string IP address of packet source, i.e. "127.0.0.1"
    591 
    592         @return True on success, False otherwise.
    593 
    594         """
    595         device_object = self._wifi_proxy.find_object(
    596                 self.DBUS_DEVICE, {'Name': wifi_interface})
    597         if device_object is None:
    598             return False
    599         device_object.RemoveWakeOnPacketConnection(source_ip)
    600         return True
    601 
    602 
    603     @xmlrpc_server.dbus_safe(False)
    604     def remove_all_wake_packet_sources(self, wifi_interface):
    605         """Stop waking on packets from any IP.
    606 
    607         @param wifi_interface: string name of interface to establish WoWLAN on.
    608 
    609         @return True on success, False otherwise.
    610 
    611         """
    612         device_object = self._wifi_proxy.find_object(
    613                 self.DBUS_DEVICE, {'Name': wifi_interface})
    614         if device_object is None:
    615             return False
    616         device_object.RemoveAllWakeOnPacketConnections()
    617         return True
    618 
    619 
    620     @xmlrpc_server.dbus_safe(False)
    621     def request_scan(self):
    622         """Request a scan from shill.
    623 
    624         @return True on success, False otherwise.
    625 
    626         """
    627         self._wifi_proxy.manager.RequestScan('wifi')
    628         return True
    629 
    630 
    631 
    632 if __name__ == '__main__':
    633     logging.basicConfig(level=logging.DEBUG)
    634     handler = logging.handlers.SysLogHandler(address = '/dev/log')
    635     formatter = logging.Formatter(
    636             'shill_xmlrpc_server: [%(levelname)s] %(message)s')
    637     handler.setFormatter(formatter)
    638     logging.getLogger().addHandler(handler)
    639     logging.debug('shill_xmlrpc_server main...')
    640     server = xmlrpc_server.XmlRpcServer('localhost',
    641                                          constants.SHILL_XMLRPC_SERVER_PORT)
    642     server.register_delegate(ShillXmlRpcDelegate())
    643     server.run()
    644