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