1#!/usr/bin/python2 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 tpm_store 19from autotest_lib.client.cros.networking import shill_proxy 20from autotest_lib.client.cros.networking import wifi_proxy 21from autotest_lib.client.cros.power import sys_power 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(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 @xmlrpc_server.dbus_safe(False) 515 def add_wake_packet_source(self, wifi_interface, source_ip): 516 """Set up the NIC to wake on packets from the given source IP. 517 518 @param wifi_interface: string name of interface to establish WoWLAN on. 519 @param source_ip: string IP address of packet source, i.e. "127.0.0.1" 520 521 @return True on success, 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.AddWakeOnPacketConnection(source_ip) 529 return True 530 531 532 @xmlrpc_server.dbus_safe(False) 533 def remove_wake_packet_source(self, wifi_interface, source_ip): 534 """Stop waking on packets from the given source IP. 535 536 @param wifi_interface: string name of interface to establish WoWLAN on. 537 @param source_ip: string IP address of packet source, i.e. "127.0.0.1" 538 539 @return True on success, False otherwise. 540 541 """ 542 device_object = self._wifi_proxy.find_object( 543 self.DBUS_DEVICE, {'Name': wifi_interface}) 544 if device_object is None: 545 return False 546 device_object.RemoveWakeOnPacketConnection(source_ip) 547 return True 548 549 550 @xmlrpc_server.dbus_safe(False) 551 def remove_all_wake_packet_sources(self, wifi_interface): 552 """Stop waking on packets from any IP. 553 554 @param wifi_interface: string name of interface to establish WoWLAN on. 555 556 @return True on success, False otherwise. 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 False 563 device_object.RemoveAllWakeOnPacketConnections() 564 return True 565 566 567 @xmlrpc_server.dbus_safe(False) 568 def request_scan(self): 569 """Request a scan from shill. 570 571 @return True on success, False otherwise. 572 573 """ 574 self._wifi_proxy.manager.RequestScan('wifi') 575 return True 576 577 578 579if __name__ == '__main__': 580 logging.basicConfig(level=logging.DEBUG) 581 handler = logging.handlers.SysLogHandler(address = '/dev/log') 582 formatter = logging.Formatter( 583 'shill_xmlrpc_server: [%(levelname)s] %(message)s') 584 handler.setFormatter(formatter) 585 logging.getLogger().addHandler(handler) 586 logging.debug('shill_xmlrpc_server main...') 587 server = xmlrpc_server.XmlRpcServer('localhost', 588 constants.SHILL_XMLRPC_SERVER_PORT) 589 server.register_delegate(ShillXmlRpcDelegate()) 590 server.run() 591