blob: 575d24235b167dba6804cf4a9234914bb1151782 [file] [log] [blame]
Christopher Wiley1f89fd22013-12-19 15:02:45 -08001# 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
Christopher Wiley1f89fd22013-12-19 15:02:45 -08009import time
10
Christopher Wiley10fe2772014-10-13 09:21:23 -070011from autotest_lib.client.cros import dbus_util
12
Christopher Wiley1f89fd22013-12-19 15:02:45 -080013
14class ShillProxyError(Exception):
Ben Chandd492752018-01-30 15:53:18 -080015 """Exceptions raised by ShillProxy and its children."""
Christopher Wiley1f89fd22013-12-19 15:02:45 -080016 pass
17
18
Ben Chandd492752018-01-30 15:53:18 -080019class 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
Christopher Wiley1f89fd22013-12-19 15:02:45 -080026class 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
Thieu Le98327a42014-08-21 18:11:41 -070032 ERROR_ALREADY_CONNECTED = 'org.chromium.flimflam.Error.AlreadyConnected'
Christopher Wiley1f89fd22013-12-19 15:02:45 -080033 ERROR_FAILURE = 'org.chromium.flimflam.Error.Failure'
Thieu Le98327a42014-08-21 18:11:41 -070034 ERROR_INCORRECT_PIN = 'org.chromium.flimflam.Error.IncorrectPin'
Thieu Lee3b3fcf2014-09-08 13:56:15 -070035 ERROR_IN_PROGRESS = 'org.chromium.flimflam.Error.InProgress'
Thieu Le98327a42014-08-21 18:11:41 -070036 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
Christopher Wiley1f89fd22013-12-19 15:02:45 -080040
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'
Christopher Wiley71957022014-01-08 09:43:38 -080050 ENTRY_FIELD_TYPE = 'Type'
Christopher Wiley1f89fd22013-12-19 15:02:45 -080051
52 MANAGER_PROPERTY_ACTIVE_PROFILE = 'ActiveProfile'
53 MANAGER_PROPERTY_DEVICES = 'Devices'
Thieu Le996e0a02014-09-15 12:36:08 -070054 MANAGER_PROPERTY_NO_AUTOCONNECT_TECHNOLOGIES = 'NoAutoConnectTechnologies'
Ben Chanb24e46a2018-01-18 15:49:53 -080055 MANAGER_PROPERTY_ENABLED_TECHNOLOGIES = 'EnabledTechnologies'
56 MANAGER_PROPERTY_PROHIBITED_TECHNOLOGIES = 'ProhibitedTechnologies'
Brian Norrise93b4ba2018-01-26 17:10:19 -080057 MANAGER_PROPERTY_UNINITIALIZED_TECHNOLOGIES = 'UninitializedTechnologies'
Christopher Wiley1f89fd22013-12-19 15:02:45 -080058 MANAGER_PROPERTY_PROFILES = 'Profiles'
59 MANAGER_PROPERTY_SERVICES = 'Services'
Brian Norris3a1cb2a2019-08-08 13:35:28 -070060 MANAGER_PROPERTY_DEFAULT_SERVICE = 'DefaultService'
Christopher Wiley1f89fd22013-12-19 15:02:45 -080061 MANAGER_PROPERTY_ALL_SERVICES = 'ServiceCompleteList'
Rebecca Silbersteineea69662016-03-03 21:39:21 -080062 MANAGER_PROPERTY_DHCPPROPERTY_HOSTNAME = 'DHCPProperty.Hostname'
63 MANAGER_PROPERTY_DHCPPROPERTY_VENDORCLASS = 'DHCPProperty.VendorClass'
Matthew Wang371e0f52018-11-15 14:45:07 -080064 MANAGER_PROPERTY_WIFI_GLOBAL_FT_ENABLED = 'WiFi.GlobalFTEnabled'
Rebecca Silbersteineea69662016-03-03 21:39:21 -080065
66 MANAGER_OPTIONAL_PROPERTY_MAP = {
67 MANAGER_PROPERTY_DHCPPROPERTY_HOSTNAME: dbus.String,
Matthew Wang371e0f52018-11-15 14:45:07 -080068 MANAGER_PROPERTY_DHCPPROPERTY_VENDORCLASS: dbus.String,
69 MANAGER_PROPERTY_WIFI_GLOBAL_FT_ENABLED: dbus.Boolean
Rebecca Silbersteineea69662016-03-03 21:39:21 -080070 }
Christopher Wiley1f89fd22013-12-19 15:02:45 -080071
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
Ben Chan927c9b02018-01-30 14:47:28 -080082 DEVICE_ENUMERATION_TIMEOUT = 30
Thieu Lee3b3fcf2014-09-08 13:56:15 -070083 DEVICE_ENABLE_DISABLE_TIMEOUT = 60
Christopher Wiley1f89fd22013-12-19 15:02:45 -080084 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'
Matthew Wang1667e5c2018-02-27 17:32:59 -080096 SERVICE_PROPERTY_FT_ENABLED = 'WiFi.FTEnabled'
mukesh agrawal9fe0e252014-12-12 17:45:38 -080097 # 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'
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800101 SERVICE_PROPERTY_SSID = 'SSID'
102 SERVICE_PROPERTY_STRENGTH = 'Strength'
103 SERVICE_PROPERTY_STATE = 'State'
Alex Khouderchahc7424102019-08-06 14:34:57 -0700104 SERVICE_PROPERTY_STATIC_IP_CONFIG = 'StaticIPConfig'
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800105 SERVICE_PROPERTY_TYPE = 'Type'
106
Peter Qiuaf683232015-08-11 16:47:57 -0700107 # 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'
Matthew Wang1667e5c2018-02-27 17:32:59 -0800113 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'
Peter Qiuaf683232015-08-11 16:47:57 -0700118
Ningyuan Wangc326ddf2015-09-22 16:03:40 -0700119 # 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'
Ningyuan Wangc326ddf2015-09-22 16:03:40 -0700132
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
Alex Khouderchahc7424102019-08-06 14:34:57 -0700144 # Mapping of service property to (dbus-type, additional kwargs).
Peter Qiuaf683232015-08-11 16:47:57 -0700145 SERVICE_PROPERTY_MAP = {
Alex Khouderchahc7424102019-08-06 14:34:57 -0700146 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'}),
Peter Qiuaf683232015-08-11 16:47:57 -0700165
Alex Khouderchahc7424102019-08-06 14:34:57 -0700166 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, {}),
Ningyuan Wangc326ddf2015-09-22 16:03:40 -0700176
Alex Khouderchahc7424102019-08-06 14:34:57 -0700177 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, {}),
Ningyuan Wangc326ddf2015-09-22 16:03:40 -0700189
Alex Khouderchahc7424102019-08-06 14:34:57 -0700190 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, {})
Peter Qiuaf683232015-08-11 16:47:57 -0700199 }
200
Brian Norris3380b222019-05-23 14:04:12 -0700201 SERVICE_CONNECTED_STATES = ['portal', 'no-connectivity', 'redirect-found',
202 'portal-suspected', 'online', 'ready']
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800203 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'
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800223
Thieu Le75f83682014-09-23 18:03:14 -0700224 VALUE_POWERED_ON = True
225 VALUE_POWERED_OFF = False
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800226
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'],
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800249 }
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
Peter Qiuaf683232015-08-11 16:47:57 -0700271 @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))
Alex Khouderchahc7424102019-08-06 14:34:57 -0700283 (dbus_type, kwargs) = ShillProxy.SERVICE_PROPERTY_MAP[key]
284 dbus_dict[key] = dbus_type(value, variant_level=1, **kwargs)
Peter Qiuaf683232015-08-11 16:47:57 -0700285 return dbus_dict
286
287
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800288 @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 """
Christopher Wiley10fe2772014-10-13 09:21:23 -0700295 return dbus_util.dbus2primitive(value)
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800296
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
Rebecca Silbersteineea69662016-03-03 21:39:21 -0800333 @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
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800355 @classmethod
Thieu Le16f7eeb2014-08-26 18:31:17 -0700356 def get_proxy(cls, bus=None, timeout_seconds=10):
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800357 """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
Thieu Le16f7eeb2014-08-26 18:31:17 -0700370 @param bus D-Bus bus to use, or specify None and this object will
371 create a mainloop and bus.
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800372 @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.
Thieu Le16f7eeb2014-08-26 18:31:17 -0700385 connection = cls(bus=bus)
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800386 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
Peter Qiuaf683232015-08-11 16:47:57 -0700422 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.
Alex Khouderchahe385f2a2020-02-04 12:13:59 -0800429 @return DBus object interface representing configured Service.
Peter Qiuaf683232015-08-11 16:47:57 -0700430
431 """
432 # Convert configuration values to dbus variant typed values.
433 dbus_config = ShillProxy.service_properties_to_dbus_types(config)
Alex Khouderchahe385f2a2020-02-04 12:13:59 -0800434 path = self.manager.ConfigureService(dbus_config)
435 return self.get_dbus_object(self.DBUS_TYPE_SERVICE, path)
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800436
437
Matthew Wangac6bbe92019-02-13 18:25:49 -0800438 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
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800452 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
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800502
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)
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800510 try:
Thieu Lec28759c2014-03-13 13:16:02 -0700511 # 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()
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800523 while duration() < timeout_seconds:
Thieu Le2da53e22014-09-24 12:51:19 -0700524 # Dispatch all pending events.
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800525 while context.iteration(False):
Thieu Le2da53e22014-09-24 12:51:19 -0700526 pass
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800527
528 while update_queue:
Thieu Le37c4d7d2014-09-08 18:00:01 -0700529 updated_property, value = map(self.dbus2primitive,
530 update_queue.popleft())
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800531 if property_name != updated_property:
532 continue
533
Thieu Le37c4d7d2014-09-08 18:00:01 -0700534 last_value = value
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800535 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(
Eric Caruso3e493712019-09-06 15:58:54 -0700574 self._bus.get_object(self.DBUS_INTERFACE, path,
575 introspect=False),
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800576 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 """
Ningyuan Wangc326ddf2015-09-22 16:03:40 -0700602 dbus_params = self.service_properties_to_dbus_types(params)
603 path = self.manager.GetService(dbus_params)
Christopher Wiley1f89fd22013-12-19 15:02:45 -0800604 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)
Thieu Le88038722014-09-18 18:18:39 -0700684
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:
Thieu Le5e6acf22014-10-14 17:34:23 -0700698 if e.get_dbus_name() != self.ERROR_ALREADY_CONNECTED:
Thieu Le88038722014-09-18 18:18:39 -0700699 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:
Thieu Le5e6acf22014-10-14 17:34:23 -0700718 if e.get_dbus_name() not in [self.ERROR_IN_PROGRESS,
Thieu Le88038722014-09-18 18:18:39 -0700719 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
Brian Norris3a1cb2a2019-08-08 13:35:28 -0700725
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)