~mvo/ubuntu-sso-client/strawman-lp711413

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# -*- coding: utf-8 -*-
#
# networkstate - detect the current state of the network
#
# Author: Alejandro J. Cura <alecu@canonical.com>
#
# Copyright 2010 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.
"""Implementation of network state detection."""

import dbus

from ubuntu_sso.logger import setup_logging
logger = setup_logging("ubuntu_sso.networkstate")

# Values returned by the callback
ONLINE, OFFLINE, UNKNOWN = object(), object(), object()

NM_STATE_NAMES = {
    ONLINE: "online",
    OFFLINE: "offline",
    UNKNOWN: "unknown",
}

# Internal NetworkManager State constants
NM_STATE_UNKNOWN = 0
NM_STATE_UNKNOWN_LIST = [NM_STATE_UNKNOWN]
NM_STATE_ASLEEP_OLD = 1
NM_STATE_ASLEEP = 10
NM_STATE_ASLEEP_LIST = [NM_STATE_ASLEEP_OLD,
                        NM_STATE_ASLEEP]
NM_STATE_CONNECTING_OLD = 2
NM_STATE_CONNECTING = 40
NM_STATE_CONNECTING_LIST = [NM_STATE_CONNECTING_OLD,
                            NM_STATE_CONNECTING]
NM_STATE_CONNECTED_OLD = 3
NM_STATE_CONNECTED_LOCAL = 50
NM_STATE_CONNECTED_SITE = 60
NM_STATE_CONNECTED_GLOBAL = 70
# Specifically don't include local and site, as they won't let us get to server
NM_STATE_CONNECTED_LIST = [NM_STATE_CONNECTED_OLD,
                           NM_STATE_CONNECTED_GLOBAL]
NM_STATE_DISCONNECTED_OLD = 4
NM_STATE_DISCONNECTED = 20
# For us, local and site connections are the same as diconnected
NM_STATE_DISCONNECTED_LIST = [NM_STATE_DISCONNECTED_OLD,
                              NM_STATE_DISCONNECTED,
                              NM_STATE_CONNECTED_LOCAL,
                              NM_STATE_CONNECTED_SITE]

NM_DBUS_INTERFACE = "org.freedesktop.NetworkManager"
NM_DBUS_OBJECTPATH = "/org/freedesktop/NetworkManager"
DBUS_UNKNOWN_SERVICE = "org.freedesktop.DBus.Error.ServiceUnknown"


class NetworkManagerState(object):
    """Checks the state of NetworkManager thru DBus."""

    def __init__(self, result_cb, dbus_module=dbus):
        """Initialize this instance with a result and error callbacks."""
        self.result_cb = result_cb
        self.dbus = dbus_module
        self.state_signal = None

    def call_result_cb(self, state):
        """Return the state thru the result callback."""
        if self.state_signal:
            self.state_signal.remove()
        self.result_cb(state)

    def got_state(self, state):
        """Called by DBus when the state is retrieved from NM."""
        if state in NM_STATE_CONNECTED_LIST:
            self.call_result_cb(ONLINE)
        elif state in NM_STATE_CONNECTING_LIST:
            logger.debug("Currently connecting, waiting for signal")
        else:
            self.call_result_cb(OFFLINE)

    def got_error(self, error):
        """Called by DBus when the state is retrieved from NM."""
        if isinstance(error, self.dbus.exceptions.DBusException) and \
                error.get_dbus_name() == DBUS_UNKNOWN_SERVICE:
            logger.debug("Network Manager not present")
            self.call_result_cb(UNKNOWN)
        else:
            logger.error("Error contacting NetworkManager: %s" % \
                             str(error))
            self.call_result_cb(UNKNOWN)

    def state_changed(self, state):
        """Called when a signal is emmited by Network Manager."""
        if int(state) in NM_STATE_CONNECTED_LIST:
            self.call_result_cb(ONLINE)
        elif int(state) in NM_STATE_DISCONNECTED_LIST:
            self.call_result_cb(OFFLINE)
        else:
            logger.debug("Not yet connected: continuing to wait")

    def find_online_state(self):
        """Get the network state and return it thru the set callback."""
        try:
            sysbus = self.dbus.SystemBus()
            nm_proxy = sysbus.get_object(NM_DBUS_INTERFACE,
                                         NM_DBUS_OBJECTPATH,
                                         follow_name_owner_changes=True)
            nm_if = self.dbus.Interface(nm_proxy, NM_DBUS_INTERFACE)
            self.state_signal = nm_if.connect_to_signal(
                        signal_name="StateChanged",
                        handler_function=self.state_changed,
                        dbus_interface=NM_DBUS_INTERFACE)
            nm_proxy.Get(NM_DBUS_INTERFACE, "State",
                         reply_handler=self.got_state,
                         error_handler=self.got_error)
        except Exception, e:  # pylint: disable=W0703
            self.got_error(e)