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