普通文本  |  199行  |  7.89 KB

# Copyright 2014 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 dbus.mainloop.glib
import time

from autotest_lib.client.common_lib.cros.network import apmanager_constants
from autotest_lib.client.cros import dbus_util


class ApmanagerProxyError(Exception):
    """Exceptions raised by ApmanagerProxy and it's children."""
    pass


class ApmanagerProxy(object):
    """A wrapper around a DBus proxy for apmanager."""

    # Core DBus error names
    DBUS_ERROR_UNKNOWN_OBJECT = 'org.freedesktop.DBus.Error.UnknownObject'
    DBUS_ERROR_SERVICE_UNKNOWN = 'org.freedesktop.DBus.Error.ServiceUnknown'
    DBUS_ERROR_UNKNOWN_METHOD = 'org.freedesktop.DBus.Error.UnknownMethod'

    # apmanager Service and Interface names.
    DBUS_SERVICE = 'org.chromium.apmanager'
    DBUS_PROPERTY_INTERFACE = 'org.freedesktop.DBus.Properties'
    DBUS_CONFIG_INTERFACE = 'org.chromium.apmanager.Config'
    DBUS_SERVICE_INTERFACE = 'org.chromium.apmanager.Service'
    DBUS_MANAGER_INTERFACE = 'org.chromium.apmanager.Manager'
    DBUS_MANAGER_PATH = '/org/chromium/apmanager/Manager'

    # AP Service property keys
    SERVICE_PROPERTY_CONFIG = 'Config'

    # Mapping for property to dbus type function.
    CONFIG_PROPERTY_DBUS_TYPE_MAPPING = {
            apmanager_constants.CONFIG_BRIDGE_INTERFACE: dbus.String,
            apmanager_constants.CONFIG_CHANNEL: dbus.UInt16,
            apmanager_constants.CONFIG_HIDDEN_NETWORK: dbus.Boolean,
            apmanager_constants.CONFIG_HW_MODE: dbus.String,
            apmanager_constants.CONFIG_INTERFACE_NAME: dbus.String,
            apmanager_constants.CONFIG_OPERATION_MODE: dbus.String,
            apmanager_constants.CONFIG_PASSPHRASE: dbus.String,
            apmanager_constants.CONFIG_SECURITY_MODE: dbus.String,
            apmanager_constants.CONFIG_SERVER_ADDRESS_INDEX: dbus.UInt16,
            apmanager_constants.CONFIG_SSID: dbus.String}

    POLLING_INTERVAL_SECONDS = 0.2


    def __init__(self, bus=None, timeout_seconds=10):
        if bus is None:
            dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
            bus = dbus.SystemBus()
        self._bus = bus
        self._manager = None
        self._connect_to_dbus(timeout_seconds)


    def _connect_to_dbus(self, timeout_seconds):
        """Connect to apmanager over DBus and initialize the DBus object for
           org.chromium.apmanager.Manager interface.

        If apmanager is not yet running, retry until it is, or until
        |timeout_seconds| expires.

        @param timeout_seconds float number of seconds to wait for connecting
               to apmanager's DBus service.

        """
        end_time = time.time() + timeout_seconds
        while self._manager is None and time.time() < end_time:
            try:
                self._manager = \
                        self._get_dbus_object(self.DBUS_MANAGER_INTERFACE,
                                              self.DBUS_MANAGER_PATH)
            except dbus.exceptions.DBusException as e:
                if (e.get_dbus_name() !=
                    ApmanagerProxy.DBUS_ERROR_SERVICE_UNKNOWN):
                    raise ApmanagerProxyError('Error connecting to apmanager')
                else:
                    # Wait a moment before retrying
                    time.sleep(ApmanagerProxy.POLLING_INTERVAL_SECONDS)
        if self._manager is None:
            raise ApmanagerProxyError('Timeout connecting to apmanager')


    def _get_dbus_object(self, interface_name, path):
        """Return the DBus object of interface |interface_name| at |path| in
           apmanager DBUS service.

        @param interface_name string (e.g. self.DBUS_SERVICE_INTERFACE).
        @param path path to object in apmanager (e.g. '/manager/services/1').
        @return DBus proxy object.

        """
        return dbus.Interface(
                self._bus.get_object(self.DBUS_SERVICE, path),
                interface_name)


    def _get_dbus_property(self, dbus_object, interface_name, property_key):
        """get property on a dbus Interface

        @param dbus_object DBus object to read property from
        @param interface_name string name of the interface
        @param property_key string name of property on interface
        @return python typed object representing property value or None

        """
        # Get the property interface for the given DBus object.
        property_interface = self._get_dbus_object(
                self.DBUS_PROPERTY_INTERFACE,
                dbus_object.object_path)
        # Invoke Get method on the property interface.
        try:
            value = dbus_util.dbus2primitive(
                    property_interface.Get(dbus.String(interface_name),
                                           dbus.String(property_key)));
        except dbus.exceptions.DBusException as e:
            raise ApmanagerProxyError(
                    'Failed to get property %s on interface %s' %
                    (property_key, interface_name))
        return value


    def _set_dbus_property(self,
                           dbus_object,
                           interface_name,
                           property_key,
                           value):
        """set property on a dbus Interface

        @param dbus_object DBus object to set property on
        @param interface_name string name of the interface
        @param property_key string name of property on interface
        @param value dbus_type value to set for property

        """
        # Get the property interface for the given DBus object.
        property_interface = self._get_dbus_object(
                self.DBUS_PROPERTY_INTERFACE,
                dbus_object.object_path)
        # Invoke Set method on the property interface.
        try:
            property_interface.Set(dbus.String(interface_name),
                                   dbus.String(property_key),
                                   value);
        except dbus.exceptions.DBusException as e:
            raise ApmanagerProxyError(
                    'Failed to set property %s on interface %s' %
                    (property_key, interface_name))


    # TODO(zqiu): add more optional parameters for setting additional
    # service configurations.
    def start_service(self, config_params):
        """Create/start an AP service with provided configurations.

        @param config_params dictionary of configuration parameters.
        @return string object path of the newly created service.

        """
        service = self._get_dbus_object(
                self.DBUS_SERVICE_INTERFACE,
                dbus_util.dbus2primitive(self._manager.CreateService()))
        # Get configuration object for the service.
        service_config = self._get_dbus_object(
                self.DBUS_CONFIG_INTERFACE,
                self._get_dbus_property(service,
                                        self.DBUS_SERVICE_INTERFACE,
                                        self.SERVICE_PROPERTY_CONFIG))
        # Set configuration properties.
        for name, value in config_params.iteritems():
            if name in self.CONFIG_PROPERTY_DBUS_TYPE_MAPPING:
                func = self.CONFIG_PROPERTY_DBUS_TYPE_MAPPING[name]
                self._set_dbus_property(service_config,
                                        self.DBUS_CONFIG_INTERFACE,
                                        name,
                                        func(value, variant_level=1))
            else:
                raise ApmanagerProxyError('Unknown configuration parameter [%s]'
                                          % name)

        # Start AP service.
        service.Start()
        return service.object_path


    def terminate_service(self, service_path):
        """ Terminate and remove the AP service |service|.

        @param service_path string object path of the service.

        """
        self._manager.RemoveService(dbus.ObjectPath(service_path))