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 7import dbus 8import logging 9import logging.handlers 10import multiprocessing 11 12import common 13from autotest_lib.client.common_lib import utils 14from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes 15from autotest_lib.client.cros import xmlrpc_server 16from autotest_lib.client.cros import constants 17from autotest_lib.client.cros import cros_ui 18from autotest_lib.client.cros import sys_power 19from autotest_lib.client.cros import tpm_store 20from autotest_lib.client.cros.networking import shill_proxy 21from autotest_lib.client.cros.networking import wifi_proxy 22 23 24class 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(False) 340 def get_active_wifi_SSIDs(self): 341 """@return list of string SSIDs with at least one BSS we've scanned.""" 342 return self._wifi_proxy.get_active_wifi_SSIDs() 343 344 345 @xmlrpc_server.dbus_safe(False) 346 def set_sched_scan(self, enable): 347 """Configure scheduled scan. 348 349 @param enable bool flag indicating to enable/disable scheduled scan. 350 @return True on success, False otherwise. 351 352 """ 353 self._wifi_proxy.manager.set_sched_scan(enable) 354 return True 355 356 357 def enable_ui(self): 358 """@return True iff the UI was successfully started.""" 359 return cros_ui.start(allow_fail=True, wait_for_login_prompt=False) == 0 360 361 362 def sync_time_to(self, epoch_seconds): 363 """Sync time on the DUT to |epoch_seconds| from the epoch. 364 365 @param epoch_seconds: float number of seconds from the epoch. 366 367 """ 368 utils.run('date -u --set=@%f' % epoch_seconds) 369 return True 370 371 372 @staticmethod 373 def do_suspend(seconds): 374 """Suspend DUT using the power manager. 375 376 @param seconds: The number of seconds to suspend the device. 377 378 """ 379 return sys_power.do_suspend(seconds) 380 381 382 @staticmethod 383 def do_suspend_bg(seconds): 384 """Suspend DUT using the power manager - non-blocking. 385 386 @param seconds int The number of seconds to suspend the device. 387 388 """ 389 process = multiprocessing.Process(target=sys_power.do_suspend, 390 args=(seconds, 1)) 391 process.start() 392 return True 393 394 395 @xmlrpc_server.dbus_safe(None) 396 def get_dbus_property_on_device(self, wifi_interface, prop_name): 397 """Get a property for the given WiFi device. 398 399 @param wifi_interface: string name of interface being queried. 400 @param prop_name: the name of the property. 401 @return the current value of the property. 402 403 """ 404 dbus_object = self._wifi_proxy.find_object( 405 self.DBUS_DEVICE, {'Name': wifi_interface}) 406 if dbus_object is None: 407 return None 408 409 object_properties = dbus_object.GetProperties(utf8_strings=True) 410 if prop_name not in object_properties: 411 return None 412 413 return self._wifi_proxy.dbus2primitive( 414 object_properties[prop_name]) 415 416 417 @xmlrpc_server.dbus_safe(False) 418 def set_dbus_property_on_device(self, wifi_interface, prop_name, value): 419 """Set a property on the given WiFi device. 420 421 @param wifi_interface: the device to set a property for. 422 @param prop_name: the name of the property. 423 @param value: the desired value of the property. 424 @return True if successful, False otherwise. 425 426 """ 427 device_object = self._wifi_proxy.find_object( 428 self.DBUS_DEVICE, {'Name': wifi_interface}) 429 if device_object is None: 430 return False 431 432 shill_proxy.ShillProxy.set_dbus_property(device_object, 433 prop_name, 434 value) 435 return True 436 437 438 @xmlrpc_server.dbus_safe(False) 439 def request_roam_dbus(self, bssid, interface): 440 """Request that we roam to the specified BSSID. 441 442 Note that this operation assumes that: 443 444 1) We're connected to an SSID for which |bssid| is a member. 445 2) There is a BSS with an appropriate ID in our scan results. 446 447 @param bssid: string BSSID of BSS to roam to. 448 449 """ 450 451 device_object = self._wifi_proxy.find_object( 452 self.DBUS_DEVICE, {'Name': interface}) 453 if device_object is None: 454 return False 455 device_object.RequestRoam(bssid) 456 return True 457 458 459 @xmlrpc_server.dbus_safe(False) 460 def set_device_enabled(self, wifi_interface, enabled): 461 """Enable or disable the WiFi device. 462 463 @param wifi_interface: string name of interface being modified. 464 @param enabled: boolean; true if this device should be enabled, 465 false if this device should be disabled. 466 @return True if it worked; false, otherwise 467 468 """ 469 interface = {'Name': wifi_interface} 470 dbus_object = self._wifi_proxy.find_object(self.DBUS_DEVICE, 471 interface) 472 if dbus_object is None: 473 return False 474 475 if enabled: 476 dbus_object.Enable() 477 else: 478 dbus_object.Disable() 479 return True 480 481 482 def discover_tdls_link(self, wifi_interface, peer_mac_address): 483 """Send a TDLS Discover to |peer_mac_address| on |wifi_interface|. 484 485 @param wifi_interface: string name of interface to send the discover on. 486 @param peer_mac_address: string mac address of the TDLS peer device. 487 488 @return True if it the operation was initiated; False otherwise 489 490 """ 491 device_object = self._wifi_proxy.find_object( 492 self.DBUS_DEVICE, {'Name': wifi_interface}) 493 if device_object is None: 494 return False 495 device_object.PerformTDLSOperation('Discover', peer_mac_address) 496 return True 497 498 499 def establish_tdls_link(self, wifi_interface, peer_mac_address): 500 """Establish a TDLS link with |peer_mac_address| on |wifi_interface|. 501 502 @param wifi_interface: string name of interface to establish a link on. 503 @param peer_mac_address: string mac address of the TDLS peer device. 504 505 @return True if it the operation was initiated; False otherwise 506 507 """ 508 device_object = self._wifi_proxy.find_object( 509 self.DBUS_DEVICE, {'Name': wifi_interface}) 510 if device_object is None: 511 return False 512 device_object.PerformTDLSOperation('Setup', peer_mac_address) 513 return True 514 515 516 @xmlrpc_server.dbus_safe(False) 517 def query_tdls_link(self, wifi_interface, peer_mac_address): 518 """Query the TDLS link with |peer_mac_address| on |wifi_interface|. 519 520 @param wifi_interface: string name of interface to establish a link on. 521 @param peer_mac_address: string mac address of the TDLS peer device. 522 523 @return string indicating the current TDLS link status. 524 525 """ 526 device_object = self._wifi_proxy.find_object( 527 self.DBUS_DEVICE, {'Name': wifi_interface}) 528 if device_object is None: 529 return None 530 return self._wifi_proxy.dbus2primitive( 531 device_object.PerformTDLSOperation('Status', peer_mac_address)) 532 533 534 @xmlrpc_server.dbus_safe(False) 535 def add_wake_packet_source(self, wifi_interface, source_ip): 536 """Set up the NIC to wake on packets from the given source IP. 537 538 @param wifi_interface: string name of interface to establish WoWLAN on. 539 @param source_ip: string IP address of packet source, i.e. "127.0.0.1" 540 541 @return True on success, False otherwise. 542 543 """ 544 device_object = self._wifi_proxy.find_object( 545 self.DBUS_DEVICE, {'Name': wifi_interface}) 546 if device_object is None: 547 return False 548 device_object.AddWakeOnPacketConnection(source_ip) 549 return True 550 551 552 @xmlrpc_server.dbus_safe(False) 553 def remove_wake_packet_source(self, wifi_interface, source_ip): 554 """Stop waking on packets from the given source IP. 555 556 @param wifi_interface: string name of interface to establish WoWLAN on. 557 @param source_ip: string IP address of packet source, i.e. "127.0.0.1" 558 559 @return True on success, False otherwise. 560 561 """ 562 device_object = self._wifi_proxy.find_object( 563 self.DBUS_DEVICE, {'Name': wifi_interface}) 564 if device_object is None: 565 return False 566 device_object.RemoveWakeOnPacketConnection(source_ip) 567 return True 568 569 570 @xmlrpc_server.dbus_safe(False) 571 def remove_all_wake_packet_sources(self, wifi_interface): 572 """Stop waking on packets from any IP. 573 574 @param wifi_interface: string name of interface to establish WoWLAN on. 575 576 @return True on success, False otherwise. 577 578 """ 579 device_object = self._wifi_proxy.find_object( 580 self.DBUS_DEVICE, {'Name': wifi_interface}) 581 if device_object is None: 582 return False 583 device_object.RemoveAllWakeOnPacketConnections() 584 return True 585 586 587 588if __name__ == '__main__': 589 logging.basicConfig(level=logging.DEBUG) 590 handler = logging.handlers.SysLogHandler(address = '/dev/log') 591 formatter = logging.Formatter( 592 'shill_xmlrpc_server: [%(levelname)s] %(message)s') 593 handler.setFormatter(formatter) 594 logging.getLogger().addHandler(handler) 595 logging.debug('shill_xmlrpc_server main...') 596 server = xmlrpc_server.XmlRpcServer('localhost', 597 constants.SHILL_XMLRPC_SERVER_PORT) 598 server.register_delegate(ShillXmlRpcDelegate()) 599 server.run() 600