blob: edcbc8e11ce4bd2d299e6e93974a46e9bd5cf21a [file] [log] [blame]
#!/usr/bin/python
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import dbus
import functools
import logging
import logging.handlers
import common
from autotest_lib.client.common_lib.cros import xmlrpc_server
from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes
from autotest_lib.client.cros import constants
# pylint: disable=W0611
from autotest_lib.client.cros import flimflam_test_path
# pylint: enable=W0611
import shill_proxy
def dbus_safe(default_return_value):
"""Catch all DBus exceptions and return a default value instead.
Wrap a function with a try block that catches DBus exceptions and
returns default instead. This is convenient for simple error
handling since XMLRPC doesn't understand DBus exceptions.
@param wrapped_function function to wrap.
@param default_return_value value to return on exception (usually False).
"""
def decorator(wrapped_function):
"""Call a function and catch DBus errors.
@param wrapped_function function to call in dbus safe context.
@return function return value or default_return_value on failure.
"""
@functools.wraps(wrapped_function)
def wrapper(*args, **kwargs):
"""Pass args and kwargs to a dbus safe function.
@param args formal python arguments.
@param kwargs keyword python arguments.
@return function return value or default_return_value on failure.
"""
logging.debug('%s()', wrapped_function.__name__)
try:
return wrapped_function(*args, **kwargs)
except dbus.exceptions.DBusException as e:
logging.error('Exception while performing operation %s: %s: %s',
wrapped_function.__name__,
e.get_dbus_name(),
e.get_dbus_message())
return default_return_value
return wrapper
return decorator
class ShillXmlRpcDelegate(object):
"""Exposes methods called remotely during WiFi autotests.
All instance methods of this object without a preceding '_' are exposed via
an XMLRPC server. This is not a stateless handler object, which means that
if you store state inside the delegate, that state will remain around for
future calls.
"""
def __init__(self):
self._shill_proxy = shill_proxy.ShillProxy()
@dbus_safe(False)
def create_profile(self, profile_name):
"""Create a shill profile.
@param profile_name string name of profile to create.
@return True on success, False otherwise.
"""
self._shill_proxy.manager.CreateProfile(profile_name)
return True
@dbus_safe(False)
def push_profile(self, profile_name):
"""Push a shill profile.
@param profile_name string name of profile to push.
@return True on success, False otherwise.
"""
self._shill_proxy.manager.PushProfile(profile_name)
return True
@dbus_safe(False)
def pop_profile(self, profile_name):
"""Pop a shill profile.
@param profile_name string name of profile to pop.
@return True on success, False otherwise.
"""
if profile_name is None:
self._shill_proxy.manager.PopAnyProfile()
else:
self._shill_proxy.manager.PopProfile(profile_name)
return True
@dbus_safe(False)
def remove_profile(self, profile_name):
"""Remove a profile from disk.
@param profile_name string name of profile to remove.
@return True on success, False otherwise.
"""
self._shill_proxy.manager.RemoveProfile(profile_name)
return True
@dbus_safe(False)
def clean_profiles(self):
"""Pop and remove shill profiles above the default profile.
@return True on success, False otherwise.
"""
while True:
active_profile = self._shill_proxy.get_active_profile()
profile_name = shill_proxy.dbus2primitive(
active_profile.GetProperties(utf8_strings=True)['Name'])
if profile_name == 'default':
return True
self._shill_proxy.manager.PopProfile(profile_name)
self._shill_proxy.manager.RemoveProfile(profile_name)
def connect_wifi(self, raw_params):
"""Block and attempt to connect to wifi network.
@param raw_params serialized AssociationParameters.
@return serialized AssociationResult
"""
logging.debug('connect_wifi()')
params = xmlrpc_datatypes.AssociationParameters(raw_params)
result = xmlrpc_datatypes.AssociationResult.\
from_dbus_proxy_output(
self._shill_proxy.connect_to_wifi_network(
params.ssid,
params.security,
params.psk,
params.save_credentials,
params.discovery_timeout,
params.association_timeout,
params.configuration_timeout))
return result.serialize()
def disconnect(self, ssid):
"""Attempt to disconnect from the given ssid.
Blocks until disconnected or operation has timed out. Returns True iff
disconnect was successful.
@param ssid string network to disconnect from.
@return bool True on success, False otherwise.
"""
logging.debug('disconnect()')
result = self._shill_proxy.disconnect_from_wifi_network(ssid)
successful, duration, message = result
if successful:
level = logging.info
else:
level = logging.error
level('Disconnect result: %r, duration: %d, reason: %s',
successful, duration, message)
return successful is True
def ready(self):
"""Confirm that the XMLRPC server is up and ready to serve."""
logging.debug('ready()')
return True
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
handler = logging.handlers.SysLogHandler(address = '/dev/log')
logging.getLogger().addHandler(handler)
logging.debug('shill_xmlrpc_server main...')
server = xmlrpc_server.XmlRpcServer('localhost',
constants.SHILL_XMLRPC_SERVER_PORT)
server.register_delegate(ShillXmlRpcDelegate())
server.run()