1# Copyright (c) 2013 The Chromium OS 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 5import collections 6import dbus 7import dbus.mainloop.glib 8import gobject 9import time 10 11from autotest_lib.client.cros import dbus_util 12 13 14class ShillProxyError(Exception): 15 """Exceptions raised by ShillProxy and its children.""" 16 pass 17 18 19class ShillProxyTimeoutError(ShillProxyError): 20 """Timeout exception raised by ShillProxy and its children.""" 21 def __init__(self, desc): 22 super(ShillProxyTimeoutError, self).__init__( 23 'Timed out waiting for condition %s.' % desc) 24 25 26class ShillProxy(object): 27 """A wrapper around a DBus proxy for shill.""" 28 29 # Core DBus error names 30 DBUS_ERROR_UNKNOWN_OBJECT = 'org.freedesktop.DBus.Error.UnknownObject' 31 # Shill error names 32 ERROR_ALREADY_CONNECTED = 'org.chromium.flimflam.Error.AlreadyConnected' 33 ERROR_FAILURE = 'org.chromium.flimflam.Error.Failure' 34 ERROR_INCORRECT_PIN = 'org.chromium.flimflam.Error.IncorrectPin' 35 ERROR_IN_PROGRESS = 'org.chromium.flimflam.Error.InProgress' 36 ERROR_NOT_CONNECTED = 'org.chromium.flimflam.Error.NotConnected' 37 ERROR_NOT_SUPPORTED = 'org.chromium.flimflam.Error.NotSupported' 38 ERROR_PIN_BLOCKED = 'org.chromium.flimflam.Error.PinBlocked' 39 40 41 DBUS_INTERFACE = 'org.chromium.flimflam' 42 DBUS_SERVICE_UNKNOWN = 'org.freedesktop.DBus.Error.ServiceUnknown' 43 DBUS_TYPE_DEVICE = 'org.chromium.flimflam.Device' 44 DBUS_TYPE_IPCONFIG = 'org.chromium.flimflam.IPConfig' 45 DBUS_TYPE_MANAGER = 'org.chromium.flimflam.Manager' 46 DBUS_TYPE_PROFILE = 'org.chromium.flimflam.Profile' 47 DBUS_TYPE_SERVICE = 'org.chromium.flimflam.Service' 48 49 ENTRY_FIELD_NAME = 'Name' 50 ENTRY_FIELD_TYPE = 'Type' 51 52 MANAGER_PROPERTY_ACTIVE_PROFILE = 'ActiveProfile' 53 MANAGER_PROPERTY_DEVICES = 'Devices' 54 MANAGER_PROPERTY_NO_AUTOCONNECT_TECHNOLOGIES = 'NoAutoConnectTechnologies' 55 MANAGER_PROPERTY_ENABLED_TECHNOLOGIES = 'EnabledTechnologies' 56 MANAGER_PROPERTY_PROHIBITED_TECHNOLOGIES = 'ProhibitedTechnologies' 57 MANAGER_PROPERTY_UNINITIALIZED_TECHNOLOGIES = 'UninitializedTechnologies' 58 MANAGER_PROPERTY_PROFILES = 'Profiles' 59 MANAGER_PROPERTY_SERVICES = 'Services' 60 MANAGER_PROPERTY_ALL_SERVICES = 'ServiceCompleteList' 61 MANAGER_PROPERTY_DHCPPROPERTY_HOSTNAME = 'DHCPProperty.Hostname' 62 MANAGER_PROPERTY_DHCPPROPERTY_VENDORCLASS = 'DHCPProperty.VendorClass' 63 64 MANAGER_OPTIONAL_PROPERTY_MAP = { 65 MANAGER_PROPERTY_DHCPPROPERTY_HOSTNAME: dbus.String, 66 MANAGER_PROPERTY_DHCPPROPERTY_VENDORCLASS: dbus.String 67 } 68 69 PROFILE_PROPERTY_ENTRIES = 'Entries' 70 PROFILE_PROPERTY_NAME = 'Name' 71 72 OBJECT_TYPE_PROPERTY_MAP = { 73 'Device': ( DBUS_TYPE_DEVICE, MANAGER_PROPERTY_DEVICES ), 74 'Profile': ( DBUS_TYPE_PROFILE, MANAGER_PROPERTY_PROFILES ), 75 'Service': ( DBUS_TYPE_SERVICE, MANAGER_PROPERTY_SERVICES ), 76 'AnyService': ( DBUS_TYPE_SERVICE, MANAGER_PROPERTY_ALL_SERVICES ) 77 } 78 79 DEVICE_ENUMERATION_TIMEOUT = 30 80 DEVICE_ENABLE_DISABLE_TIMEOUT = 60 81 SERVICE_DISCONNECT_TIMEOUT = 5 82 83 SERVICE_PROPERTY_AUTOCONNECT = 'AutoConnect' 84 SERVICE_PROPERTY_DEVICE = 'Device' 85 SERVICE_PROPERTY_GUID = 'GUID' 86 SERVICE_PROPERTY_HEX_SSID = 'WiFi.HexSSID' 87 SERVICE_PROPERTY_HIDDEN = 'WiFi.HiddenSSID' 88 SERVICE_PROPERTY_MODE = 'Mode' 89 SERVICE_PROPERTY_NAME = 'Name' 90 SERVICE_PROPERTY_PASSPHRASE = 'Passphrase' 91 SERVICE_PROPERTY_PROFILE = 'Profile' 92 SERVICE_PROPERTY_SAVE_CREDENTIALS = 'SaveCredentials' 93 # Unless you really care whether a network is WPA (TSN) vs. WPA-2 94 # (RSN), you should use SERVICE_PROPERTY_SECURITY_CLASS. 95 SERVICE_PROPERTY_SECURITY_RAW = 'Security' 96 SERVICE_PROPERTY_SECURITY_CLASS = 'SecurityClass' 97 SERVICE_PROPERTY_SSID = 'SSID' 98 SERVICE_PROPERTY_STRENGTH = 'Strength' 99 SERVICE_PROPERTY_STATE = 'State' 100 SERVICE_PROPERTY_TYPE = 'Type' 101 102 # EAP related properties. 103 SERVICE_PROPERTY_EAP_EAP = 'EAP.EAP' 104 SERVICE_PROPERTY_EAP_INNER_EAP = 'EAP.InnerEAP' 105 SERVICE_PROPERTY_EAP_IDENTITY = 'EAP.Identity' 106 SERVICE_PROPERTY_EAP_PASSWORD = 'EAP.Password' 107 SERVICE_PROPERTY_EAP_CA_CERT_PEM = 'EAP.CACertPEM' 108 109 # OpenVPN related properties. 110 SERVICE_PROPERTY_OPENVPN_CA_CERT_PEM = 'OpenVPN.CACertPEM' 111 SERVICE_PROPERTY_OPENVPN_PASSWORD = 'OpenVPN.Password' 112 SERVICE_PROPERTY_OPENVPN_PKCS11_ID = 'OpenVPN.Pkcs11.ID' 113 SERVICE_PROPERTY_OPENVPN_PKCS11_PIN = 'OpenVPN.Pkcs11.PIN' 114 SERVICE_PROPERTY_OPENVPN_PROVIDER_HOST = 'Provider.Host' 115 SERVICE_PROPERTY_OPENVPN_PROVIDER_TYPE = 'Provider.Type' 116 SERVICE_PROPERTY_OPENVPN_REMOTE_CERT_EKU = 'OpenVPN.RemoteCertEKU' 117 SERVICE_PROPERTY_OPENVPN_USER = 'OpenVPN.User' 118 SERVICE_PROPERTY_OPENVPN_VERB = 'OpenVPN.Verb' 119 SERVICE_PROPERTY_OPENVPN_VERIFY_HASH = 'OpenVPN.VerifyHash' 120 SERVICE_PROPERTY_OPENVPN_VERIFY_X509_NAME = 'OpenVPN.VerifyX509Name' 121 SERVICE_PROPERTY_OPENVPN_VERIFY_X509_TYPE = 'OpenVPN.VerifyX509Type' 122 SERVICE_PROPERTY_OPENVPN_VPN_DOMAIN = 'VPN.Domain' 123 124 # L2TP VPN related properties. 125 SERVICE_PROPERTY_L2TP_CA_CERT_PEM = 'L2TPIPsec.CACertPEM' 126 SERVICE_PROPERTY_L2TP_CLIENT_CERT_ID = 'L2TPIPsec.ClientCertID' 127 SERVICE_PROPERTY_L2TP_CLIENT_CERT_SLOT = 'L2TPIPsec.ClientCertSlot' 128 SERVICE_PROPERTY_L2TP_PASSWORD = 'L2TPIPsec.Password' 129 SERVICE_PROPERTY_L2TP_PIN = 'L2TPIPsec.PIN' 130 SERVICE_PROPERTY_L2TP_PSK = 'L2TPIPsec.PSK' 131 SERVICE_PROPERTY_L2TP_USER = 'L2TPIPsec.User' 132 SERVICE_PROPERTY_L2TP_XAUTH_PASSWORD = 'L2TPIPsec.XauthPassword' 133 SERVICE_PROPERTY_L2TP_XAUTH_USER = 'L2TPIPsec.XauthUser' 134 135 # Mapping of service property to its dbus type. 136 SERVICE_PROPERTY_MAP = { 137 SERVICE_PROPERTY_AUTOCONNECT: dbus.Boolean, 138 SERVICE_PROPERTY_DEVICE: dbus.ObjectPath, 139 SERVICE_PROPERTY_GUID: dbus.String, 140 SERVICE_PROPERTY_HEX_SSID: dbus.String, 141 SERVICE_PROPERTY_HIDDEN: dbus.Boolean, 142 SERVICE_PROPERTY_MODE: dbus.String, 143 SERVICE_PROPERTY_NAME: dbus.String, 144 SERVICE_PROPERTY_PASSPHRASE: dbus.String, 145 SERVICE_PROPERTY_PROFILE: dbus.ObjectPath, 146 SERVICE_PROPERTY_SAVE_CREDENTIALS: dbus.Boolean, 147 SERVICE_PROPERTY_SECURITY_RAW: dbus.String, 148 SERVICE_PROPERTY_SECURITY_CLASS: dbus.String, 149 SERVICE_PROPERTY_SSID: dbus.String, 150 SERVICE_PROPERTY_STRENGTH: dbus.Byte, 151 SERVICE_PROPERTY_STATE: dbus.String, 152 SERVICE_PROPERTY_TYPE: dbus.String, 153 154 SERVICE_PROPERTY_EAP_EAP: dbus.String, 155 SERVICE_PROPERTY_EAP_INNER_EAP: dbus.String, 156 SERVICE_PROPERTY_EAP_IDENTITY: dbus.String, 157 SERVICE_PROPERTY_EAP_PASSWORD: dbus.String, 158 SERVICE_PROPERTY_EAP_CA_CERT_PEM: dbus.Array, 159 160 SERVICE_PROPERTY_OPENVPN_CA_CERT_PEM: dbus.Array, 161 SERVICE_PROPERTY_OPENVPN_PASSWORD: dbus.String, 162 SERVICE_PROPERTY_OPENVPN_PKCS11_ID: dbus.String, 163 SERVICE_PROPERTY_OPENVPN_PKCS11_PIN: dbus.String, 164 SERVICE_PROPERTY_OPENVPN_PROVIDER_HOST: dbus.String, 165 SERVICE_PROPERTY_OPENVPN_PROVIDER_TYPE: dbus.String, 166 SERVICE_PROPERTY_OPENVPN_REMOTE_CERT_EKU: dbus.String, 167 SERVICE_PROPERTY_OPENVPN_USER: dbus.String, 168 SERVICE_PROPERTY_OPENVPN_VERB: dbus.String, 169 SERVICE_PROPERTY_OPENVPN_VERIFY_HASH: dbus.String, 170 SERVICE_PROPERTY_OPENVPN_VERIFY_X509_NAME: dbus.String, 171 SERVICE_PROPERTY_OPENVPN_VERIFY_X509_TYPE: dbus.String, 172 SERVICE_PROPERTY_OPENVPN_VPN_DOMAIN: dbus.String, 173 174 SERVICE_PROPERTY_L2TP_CA_CERT_PEM: dbus.Array, 175 SERVICE_PROPERTY_L2TP_CLIENT_CERT_ID: dbus.String, 176 SERVICE_PROPERTY_L2TP_CLIENT_CERT_SLOT: dbus.String, 177 SERVICE_PROPERTY_L2TP_PASSWORD: dbus.String, 178 SERVICE_PROPERTY_L2TP_PIN: dbus.String, 179 SERVICE_PROPERTY_L2TP_PSK: dbus.String, 180 SERVICE_PROPERTY_L2TP_USER: dbus.String, 181 SERVICE_PROPERTY_L2TP_XAUTH_PASSWORD: dbus.String, 182 SERVICE_PROPERTY_L2TP_XAUTH_USER: dbus.String 183 } 184 185 SERVICE_CONNECTED_STATES = ['portal', 'online'] 186 187 SUPPORTED_WIFI_STATION_TYPES = {'managed': 'managed', 188 'ibss': 'adhoc', 189 None: 'managed'} 190 191 DEVICE_PROPERTY_ADDRESS = 'Address' 192 DEVICE_PROPERTY_EAP_AUTHENTICATION_COMPLETED = 'EapAuthenticationCompleted' 193 DEVICE_PROPERTY_EAP_AUTHENTICATOR_DETECTED = 'EapAuthenticatorDetected' 194 DEVICE_PROPERTY_IP_CONFIG = 'IpConfig' 195 DEVICE_PROPERTY_INTERFACE = 'Interface' 196 DEVICE_PROPERTY_NAME = 'Name' 197 DEVICE_PROPERTY_POWERED = 'Powered' 198 DEVICE_PROPERTY_RECEIVE_BYTE_COUNT = 'ReceiveByteCount' 199 DEVICE_PROPERTY_SCANNING = 'Scanning' 200 DEVICE_PROPERTY_TRANSMIT_BYTE_COUNT = 'TransmitByteCount' 201 DEVICE_PROPERTY_TYPE = 'Type' 202 203 TECHNOLOGY_CELLULAR = 'cellular' 204 TECHNOLOGY_ETHERNET = 'ethernet' 205 TECHNOLOGY_VPN = 'vpn' 206 TECHNOLOGY_WIFI = 'wifi' 207 TECHNOLOGY_WIMAX = 'wimax' 208 209 VALUE_POWERED_ON = True 210 VALUE_POWERED_OFF = False 211 212 POLLING_INTERVAL_SECONDS = 0.2 213 214 # Default log level used in connectivity tests. 215 LOG_LEVEL_FOR_TEST = -4 216 217 # Default log scopes used in connectivity tests. 218 LOG_SCOPES_FOR_TEST_COMMON = [ 219 'connection', 220 'dbus', 221 'device', 222 'link', 223 'manager', 224 'portal', 225 'service' 226 ] 227 228 # Default log scopes used in connectivity tests for specific technologies. 229 LOG_SCOPES_FOR_TEST = { 230 TECHNOLOGY_CELLULAR: LOG_SCOPES_FOR_TEST_COMMON + ['cellular'], 231 TECHNOLOGY_ETHERNET: LOG_SCOPES_FOR_TEST_COMMON + ['ethernet'], 232 TECHNOLOGY_VPN: LOG_SCOPES_FOR_TEST_COMMON + ['vpn'], 233 TECHNOLOGY_WIFI: LOG_SCOPES_FOR_TEST_COMMON + ['wifi'], 234 TECHNOLOGY_WIMAX: LOG_SCOPES_FOR_TEST_COMMON + ['wimax'] 235 } 236 237 UNKNOWN_METHOD = 'org.freedesktop.DBus.Error.UnknownMethod' 238 239 240 @staticmethod 241 def str2dbus(dbus_class, value): 242 """Typecast string property values to dbus types. 243 244 This mostly makes it easy to special case Boolean constructors 245 to interpret strings like 'false' and '0' as False. 246 247 @param dbus_class: DBus class object. 248 @param value: value to pass to constructor. 249 250 """ 251 if isinstance(dbus_class, dbus.Boolean): 252 return dbus_class(value.lower() in ('true','1')) 253 else: 254 return dbus_class(value) 255 256 257 @staticmethod 258 def service_properties_to_dbus_types(in_dict): 259 """Convert service properties to dbus types. 260 261 @param in_dict: Dictionary containing service properties. 262 @return DBus variant dictionary containing service properties. 263 264 """ 265 dbus_dict = {} 266 for key, value in in_dict.iteritems(): 267 if key not in ShillProxy.SERVICE_PROPERTY_MAP: 268 raise ShillProxyError('Unsupported property %s' % (key)) 269 dbus_dict[key] = ShillProxy.SERVICE_PROPERTY_MAP[key]( 270 value, variant_level=1) 271 return dbus_dict 272 273 274 @classmethod 275 def dbus2primitive(cls, value): 276 """Typecast values from dbus types to python types. 277 278 @param value: dbus object to convert to a primitive. 279 280 """ 281 return dbus_util.dbus2primitive(value) 282 283 284 @staticmethod 285 def get_dbus_property(interface, property_key): 286 """get property on a dbus Interface 287 288 @param interface dbus Interface to receive new setting 289 @param property_key string name of property on interface 290 @return python typed object representing property value or None 291 292 """ 293 properties = interface.GetProperties(utf8_strings=True) 294 if property_key in properties: 295 return ShillProxy.dbus2primitive(properties[property_key]) 296 else: 297 return None 298 299 300 @staticmethod 301 def set_dbus_property(interface, property_key, value): 302 """set property on a dbus Interface 303 304 @param interface dbus Interface to receive new setting 305 @param property_key string name of property on interface 306 @param value string value to set for property on interface from string 307 308 """ 309 properties = interface.GetProperties(utf8_strings=True) 310 if property_key not in properties: 311 raise ShillProxyError('No property %s found in %s' % 312 (property_key, interface.object_path)) 313 else: 314 dbus_class = properties[property_key].__class__ 315 interface.SetProperty(property_key, 316 ShillProxy.str2dbus(dbus_class, value)) 317 318 319 @staticmethod 320 def set_optional_dbus_property(interface, property_key, value): 321 """set an optional property on a dbus Interface. 322 323 This method can be used for properties that are optionally listed 324 in the profile. It skips the initial check of the property 325 being in the interface.GetProperties list. 326 327 @param interface dbus Interface to receive new setting 328 @param property_key string name of property on interface 329 @param value string value to set for property on interface from string 330 331 """ 332 if property_key not in ShillProxy.MANAGER_OPTIONAL_PROPERTY_MAP: 333 raise ShillProxyError('Unsupported property %s' % 334 (property_key)) 335 else: 336 dbus_class = ShillProxy.MANAGER_OPTIONAL_PROPERTY_MAP[property_key] 337 interface.SetProperty(property_key, 338 ShillProxy.str2dbus(dbus_class, value)) 339 340 341 @classmethod 342 def get_proxy(cls, bus=None, timeout_seconds=10): 343 """Create a Proxy, retrying if necessary. 344 345 This method creates a proxy object of the required subclass of 346 ShillProxy. A call to SomeSubclassOfShillProxy.get_proxy() will return 347 an object of type SomeSubclassOfShillProxy. 348 349 Connects to shill over D-Bus. If shill is not yet running, 350 retry until it is, or until |timeout_seconds| expires. 351 352 After connecting to shill, this method will verify that shill 353 is answering RPCs. No timeout is applied to the test RPC, so 354 this method _may_ block indefinitely. 355 356 @param bus D-Bus bus to use, or specify None and this object will 357 create a mainloop and bus. 358 @param timeout_seconds float number of seconds to try connecting 359 A value <= 0 will cause the method to return immediately, 360 without trying to connect. 361 @return a ShillProxy instance if we connected, or None otherwise 362 363 """ 364 end_time = time.time() + timeout_seconds 365 connection = None 366 while connection is None and time.time() < end_time: 367 try: 368 # We create instance of class on which this classmethod was 369 # called. This way, calling SubclassOfShillProxy.get_proxy() 370 # will get a proxy of the right type. 371 connection = cls(bus=bus) 372 except dbus.exceptions.DBusException as e: 373 if e.get_dbus_name() != ShillProxy.DBUS_SERVICE_UNKNOWN: 374 raise ShillProxyError('Error connecting to shill') 375 else: 376 # Wait a moment before retrying 377 time.sleep(ShillProxy.POLLING_INTERVAL_SECONDS) 378 379 if connection is None: 380 return None 381 382 # Although shill is connected to D-Bus at this point, it may 383 # not have completed initialization just yet. Call into shill, 384 # and wait for the response, to make sure that it is truly up 385 # and running. (Shill will not service D-Bus requests until 386 # initialization is complete.) 387 connection.get_profiles() 388 return connection 389 390 391 def __init__(self, bus=None): 392 if bus is None: 393 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 394 bus = dbus.SystemBus() 395 self._bus = bus 396 self._manager = self.get_dbus_object(self.DBUS_TYPE_MANAGER, '/') 397 398 399 def configure_service_by_guid(self, guid, properties={}): 400 """Configure a service identified by its GUID. 401 402 @param guid string unique identifier of service. 403 @param properties dictionary of service property:value pairs. 404 405 """ 406 config = properties.copy() 407 config[self.SERVICE_PROPERTY_GUID] = guid 408 self.configure_service(config) 409 410 411 def configure_service(self, config): 412 """Configure a service with given properties. 413 414 @param config dictionary of service property:value pairs. 415 416 """ 417 # Convert configuration values to dbus variant typed values. 418 dbus_config = ShillProxy.service_properties_to_dbus_types(config) 419 self.manager.ConfigureService(dbus_config) 420 421 422 def set_logging(self, level, scopes): 423 """Set the logging in shill to the specified |level| and |scopes|. 424 425 @param level int log level to set to in shill. 426 @param scopes list of strings of log scopes to set to in shill. 427 428 """ 429 self.manager.SetDebugLevel(level) 430 self.manager.SetDebugTags('+'.join(scopes)) 431 432 433 def set_logging_for_test(self, technology): 434 """Set the logging in shill for a test of the specified |technology|. 435 436 Set the log level to |LOG_LEVEL_FOR_TEST| and the log scopes to the 437 ones defined in |LOG_SCOPES_FOR_TEST| for |technology|. If |technology| 438 is not found in |LOG_SCOPES_FOR_TEST|, the log scopes are set to 439 |LOG_SCOPES_FOR_TEST_COMMON|. 440 441 @param technology string representing the technology type of a test 442 that the logging in shill is to be customized for. 443 444 """ 445 scopes = self.LOG_SCOPES_FOR_TEST.get(technology, 446 self.LOG_SCOPES_FOR_TEST_COMMON) 447 self.set_logging(self.LOG_LEVEL_FOR_TEST, scopes) 448 449 450 def wait_for_property_in(self, dbus_object, property_name, 451 expected_values, timeout_seconds): 452 """Wait till a property is in a list of expected values. 453 454 Block until the property |property_name| in |dbus_object| is in 455 |expected_values|, or |timeout_seconds|. 456 457 @param dbus_object DBus proxy object as returned by 458 self.get_dbus_object. 459 @param property_name string property key in dbus_object. 460 @param expected_values iterable set of values to return successfully 461 upon seeing. 462 @param timeout_seconds float number of seconds to return if we haven't 463 seen the appropriate property value in time. 464 @return tuple(successful, final_value, duration) 465 where successful is True iff we saw one of |expected_values| for 466 |property_name|, final_value is the member of |expected_values| we 467 saw, and duration is how long we waited to see that value. 468 469 """ 470 start_time = time.time() 471 duration = lambda: time.time() - start_time 472 473 update_queue = collections.deque() 474 signal_receiver = lambda key, value: update_queue.append((key, value)) 475 receiver_ref = self._bus.add_signal_receiver( 476 signal_receiver, 477 signal_name='PropertyChanged', 478 dbus_interface=dbus_object.dbus_interface, 479 path=dbus_object.object_path) 480 try: 481 # Check to make sure we're not already in a target state. 482 try: 483 properties = self.dbus2primitive( 484 dbus_object.GetProperties(utf8_strings=True)) 485 last_value = properties.get(property_name, '(no value found)') 486 if last_value in expected_values: 487 return True, last_value, duration() 488 489 except dbus.exceptions.DBusException: 490 return False, '(object reference became invalid)', duration() 491 492 context = gobject.MainLoop().get_context() 493 while duration() < timeout_seconds: 494 # Dispatch all pending events. 495 while context.iteration(False): 496 pass 497 498 while update_queue: 499 updated_property, value = map(self.dbus2primitive, 500 update_queue.popleft()) 501 if property_name != updated_property: 502 continue 503 504 last_value = value 505 if not last_value in expected_values: 506 continue 507 508 return True, last_value, duration() 509 510 time.sleep(0.2) # Give that CPU a break. CPUs love breaks. 511 finally: 512 receiver_ref.remove() 513 514 return False, last_value, duration() 515 516 517 @property 518 def manager(self): 519 """ @return DBus proxy object representing the shill Manager. """ 520 return self._manager 521 522 523 def get_active_profile(self): 524 """Get the active profile in shill. 525 526 @return dbus object representing the active profile. 527 528 """ 529 properties = self.manager.GetProperties(utf8_strings=True) 530 return self.get_dbus_object( 531 self.DBUS_TYPE_PROFILE, 532 properties[self.MANAGER_PROPERTY_ACTIVE_PROFILE]) 533 534 535 def get_dbus_object(self, type_str, path): 536 """Return the DBus object of type |type_str| at |path| in shill. 537 538 @param type_str string (e.g. self.DBUS_TYPE_SERVICE). 539 @param path path to object in shill (e.g. '/service/12'). 540 @return DBus proxy object. 541 542 """ 543 return dbus.Interface( 544 self._bus.get_object(self.DBUS_INTERFACE, path), 545 type_str) 546 547 548 def get_devices(self): 549 """Return the list of devices as dbus Interface objects""" 550 properties = self.manager.GetProperties(utf8_strings=True) 551 return [self.get_dbus_object(self.DBUS_TYPE_DEVICE, path) 552 for path in properties[self.MANAGER_PROPERTY_DEVICES]] 553 554 555 def get_profiles(self): 556 """Return the list of profiles as dbus Interface objects""" 557 properties = self.manager.GetProperties(utf8_strings=True) 558 return [self.get_dbus_object(self.DBUS_TYPE_PROFILE, path) 559 for path in properties[self.MANAGER_PROPERTY_PROFILES]] 560 561 562 def get_service(self, params): 563 """ 564 Get the shill service that matches |params|. 565 566 @param params dict of strings understood by shill to describe 567 a service. 568 @return DBus object interface representing a service. 569 570 """ 571 dbus_params = self.service_properties_to_dbus_types(params) 572 path = self.manager.GetService(dbus_params) 573 return self.get_dbus_object(self.DBUS_TYPE_SERVICE, path) 574 575 576 def get_service_for_device(self, device): 577 """Attempt to find a service that manages |device|. 578 579 @param device a dbus object interface representing a device. 580 @return Dbus object interface representing a service if found. None 581 otherwise. 582 583 """ 584 properties = self.manager.GetProperties(utf8_strings=True) 585 all_services = properties.get(self.MANAGER_PROPERTY_ALL_SERVICES, 586 None) 587 if not all_services: 588 return None 589 590 for service_path in all_services: 591 service = self.get_dbus_object(self.DBUS_TYPE_SERVICE, 592 service_path) 593 properties = service.GetProperties(utf8_strings=True) 594 device_path = properties.get(self.SERVICE_PROPERTY_DEVICE, None) 595 if device_path == device.object_path: 596 return service 597 598 return None 599 600 601 def find_object(self, object_type, properties): 602 """Find a shill object with the specified type and properties. 603 604 Return the first shill object of |object_type| whose properties match 605 all that of |properties|. 606 607 @param object_type string representing the type of object to be 608 returned. Valid values are those object types defined in 609 |OBJECT_TYPE_PROPERTY_MAP|. 610 @param properties dict of strings understood by shill to describe 611 a service. 612 @return DBus object interface representing the object found or None 613 if no matching object is found. 614 615 """ 616 if object_type not in self.OBJECT_TYPE_PROPERTY_MAP: 617 return None 618 619 dbus_type, manager_property = self.OBJECT_TYPE_PROPERTY_MAP[object_type] 620 manager_properties = self.manager.GetProperties(utf8_strings=True) 621 for path in manager_properties[manager_property]: 622 try: 623 test_object = self.get_dbus_object(dbus_type, path) 624 object_properties = test_object.GetProperties(utf8_strings=True) 625 for name, value in properties.iteritems(): 626 if (name not in object_properties or 627 self.dbus2primitive(object_properties[name]) != value): 628 break 629 else: 630 return test_object 631 632 except dbus.exceptions.DBusException, e: 633 # This could happen if for instance, you're enumerating services 634 # and test_object was removed in shill between the call to get 635 # the manager properties and the call to get the service 636 # properties. This causes failed method invocations. 637 continue 638 return None 639 640 641 def find_matching_service(self, properties): 642 """Find a service object that matches the given properties. 643 644 This re-implements the manager DBus method FindMatchingService. 645 The advantage of doing this here is that FindMatchingServices does 646 not exist on older images, which will cause tests to fail. 647 648 @param properties dict of strings understood by shill to describe 649 a service. 650 651 """ 652 return self.find_object('Service', properties) 653 654 655 def connect_service_synchronous(self, service, timeout_seconds): 656 """Connect a service and wait for its state to become connected. 657 658 @param service DBus service object to connect. 659 @param timeout_seconds number of seconds to wait for service to go 660 enter a connected state. 661 @return True if the service connected successfully. 662 663 """ 664 try: 665 service.Connect() 666 except dbus.exceptions.DBusException as e: 667 if e.get_dbus_name() != self.ERROR_ALREADY_CONNECTED: 668 raise e 669 success, _, _ = self.wait_for_property_in( 670 service, self.SERVICE_PROPERTY_STATE, 671 self.SERVICE_CONNECTED_STATES, 672 timeout_seconds=timeout_seconds) 673 return success 674 675 676 def disconnect_service_synchronous(self, service, timeout_seconds): 677 """Disconnect a service and wait for its state to go idle. 678 679 @param service DBus service object to disconnect. 680 @param timeout_seconds number of seconds to wait for service to go idle. 681 @return True if the service disconnected successfully. 682 683 """ 684 try: 685 service.Disconnect() 686 except dbus.exceptions.DBusException as e: 687 if e.get_dbus_name() not in [self.ERROR_IN_PROGRESS, 688 self.ERROR_NOT_CONNECTED]: 689 raise e 690 success, _, _ = self.wait_for_property_in( 691 service, self.SERVICE_PROPERTY_STATE, ['idle'], 692 timeout_seconds=timeout_seconds) 693 return success 694