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