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