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