Merge lp:~alecu/ubuntu-sso-client/split-nm into lp:ubuntu-sso-client

Proposed by Alejandro J. Cura on 2010-07-22
Status: Merged
Approved by: Rodrigo Moya on 2010-07-27
Approved revision: 553
Merged at revision: 549
Proposed branch: lp:~alecu/ubuntu-sso-client/split-nm
Merge into: lp:ubuntu-sso-client
Diff against target: 314 lines (+300/-0)
3 files modified
bin/show_nm_state.py (+36/-0)
ubuntu_sso/networkstate.py (+105/-0)
ubuntu_sso/tests/test_networkstate.py (+159/-0)
To merge this branch: bzr merge lp:~alecu/ubuntu-sso-client/split-nm
Reviewer Review Type Date Requested Status
Rodrigo Moya (community) Approve on 2010-07-27
Natalia Bidart Approve on 2010-07-27
John Lenton 2010-07-22 Approve on 2010-07-23
Review via email: mp+30727@code.launchpad.net

Commit Message

A module for online/offline detection, querying NetworkManager thru DBus.

Description of the Change

A module for online/offline detection, querying NetworkManager thru DBus.

To post a comment you must log in.
John Lenton (chipaca) wrote :
Download full text (12.7 KiB)

wrap a try/finally around the print in got_state, and maybe docstrings to
got_state and got_error in show_nm_state. And, lovely.

 review approve

On Thu, Jul 22, 2010 at 10:55:56PM -0000, Alejandro J. Cura wrote:
> Alejandro J. Cura has proposed merging lp:~alecu/ubuntu-sso-client/split-nm into lp:ubuntu-sso-client.
>
> Requested reviews:
> Ubuntu One hackers (ubuntuone-hackers)
>
>
> A module for online/offline detection, querying NetworkManager thru DBus.

> === added file 'bin/show_nm_state.py'
> --- bin/show_nm_state.py 1970-01-01 00:00:00 +0000
> +++ bin/show_nm_state.py 2010-07-22 22:55:54 +0000
> @@ -0,0 +1,36 @@
> +# show_nm_state - Show the state of the NetworkManager daemon
> +#
> +# Author: Alejandro J. Cura <email address hidden>
> +#
> +# 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/>.
> +
> +from dbus.mainloop.glib import DBusGMainLoop
> +DBusGMainLoop(set_as_default=True)
> +import gobject
> +loop = gobject.MainLoop()
> +
> +from ubuntu_sso.networkstate import NetworkManagerState, nm_state_names
> +
> +def got_state(state):
> + print nm_state_names[state]
> + loop.quit()
> +
> +def got_error(error):
> + loop.quit()
> + raise error
> +
> +nms = NetworkManagerState(got_state, got_error)
> +nms.find_online_state()
> +loop.run()
>
> === modified file 'ubuntu_sso/auth.py'
> --- ubuntu_sso/auth.py 2010-06-10 14:34:40 +0000
> +++ ubuntu_sso/auth.py 2010-07-22 22:55:54 +0000
> @@ -94,7 +94,7 @@
> fp.close()
> # In case the server sent a relative URL, join with original:
> newurl = urllib.basejoin(self.type + ":" + url, newurl)
> -
> +
> # pass data if present when we redirect
> if data:
> return self.open(newurl, data)
>
> === added file 'ubuntu_sso/networkstate.py'
> --- ubuntu_sso/networkstate.py 1970-01-01 00:00:00 +0000
> +++ ubuntu_sso/networkstate.py 2010-07-22 22:55:54 +0000
> @@ -0,0 +1,114 @@
> +# networkstate - detect the current state of the network
> +#
> +# Author: Alejandro J. Cura <email address hidden>
> +#
> +# 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 shou...

review: Approve
Rodrigo Moya (rodrigo-moya) wrote :

Shouldn't this include the changes in auth.py to use the new networkstate object?

review: Needs Information
Alejandro J. Cura (alecu) wrote :

> Shouldn't this include the changes in auth.py to use the new networkstate
> object?

This just adds the code and tests to detect NM state.
Changes to the SSO login process are coming in a following branch.

lp:~alecu/ubuntu-sso-client/split-nm updated on 2010-07-23
552. By Alejandro J. Cura on 2010-07-23

docstring fixes

Natalia Bidart (nataliabidart) wrote :

If network manager is not installed, I get:

nessita@miro:~/canonical/ubuntu-sso-client/review_split-nm$ PYTHONPATH=. python bin/show_nm_state.py
ERROR:dbus.proxies:Introspect error on org.freedesktop.NetworkManager:/org/freedesktop/NetworkManager: dbus.exceptions.DBusException: org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.NetworkManager was not provided by any .service files
Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.6/dbus/connection.py", line 579, in msg_reply_handler
    *message.get_args_list()))
  File "/home/nessita/canonical/ubuntu-sso-client/review_split-nm/ubuntu_sso/networkstate.py", line 80, in got_error
    self.call_error_cb(error)
  File "/home/nessita/canonical/ubuntu-sso-client/review_split-nm/ubuntu_sso/networkstate.py", line 65, in call_error_cb
    self.error_cb(error)
  File "bin/show_nm_state.py", line 37, in got_error
    raise error
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.NetworkManager was not provided by any .service files
nessita@miro:~/canonical/ubuntu-sso-client/review_split-nm$

Is that the expected behavior?

review: Needs Information
lp:~alecu/ubuntu-sso-client/split-nm updated on 2010-07-26
553. By Alejandro J. Cura on 2010-07-26

state will be reported as UNKNOWN when NM is not installed or there is a dbus problem

Alejandro J. Cura (alecu) wrote :

Simplified the interface of this module following Naty's comment. Tested on Maverick, with NM installed and uninstalled too. With connman installed it currently reports "unknown" as the state; this should be fixed in a later branch.

Natalia Bidart (nataliabidart) wrote :

> Simplified the interface of this module following Naty's comment. Tested on
> Maverick, with NM installed and uninstalled too. With connman installed it
> currently reports "unknown" as the state; this should be fixed in a later
> branch.

Much better!

review: Approve
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'bin/show_nm_state.py'
2--- bin/show_nm_state.py 1970-01-01 00:00:00 +0000
3+++ bin/show_nm_state.py 2010-07-26 18:49:44 +0000
4@@ -0,0 +1,36 @@
5+# show_nm_state - Show the state of the NetworkManager daemon
6+#
7+# Author: Alejandro J. Cura <alecu@canonical.com>
8+#
9+# Copyright 2010 Canonical Ltd.
10+#
11+# This program is free software: you can redistribute it and/or modify it
12+# under the terms of the GNU General Public License version 3, as published
13+# by the Free Software Foundation.
14+#
15+# This program is distributed in the hope that it will be useful, but
16+# WITHOUT ANY WARRANTY; without even the implied warranties of
17+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
18+# PURPOSE. See the GNU General Public License for more details.
19+#
20+# You should have received a copy of the GNU General Public License along
21+# with this program. If not, see <http://www.gnu.org/licenses/>.
22+"""A test program that just shows the network state."""
23+
24+from dbus.mainloop.glib import DBusGMainLoop
25+DBusGMainLoop(set_as_default=True)
26+import gobject
27+loop = gobject.MainLoop()
28+
29+from ubuntu_sso.networkstate import NetworkManagerState, nm_state_names
30+
31+def got_state(state):
32+ """Called when the network state is found."""
33+ try:
34+ print nm_state_names[state]
35+ finally:
36+ loop.quit()
37+
38+nms = NetworkManagerState(got_state)
39+nms.find_online_state()
40+loop.run()
41
42=== added file 'ubuntu_sso/networkstate.py'
43--- ubuntu_sso/networkstate.py 1970-01-01 00:00:00 +0000
44+++ ubuntu_sso/networkstate.py 2010-07-26 18:49:44 +0000
45@@ -0,0 +1,105 @@
46+# networkstate - detect the current state of the network
47+#
48+# Author: Alejandro J. Cura <alecu@canonical.com>
49+#
50+# Copyright 2010 Canonical Ltd.
51+#
52+# This program is free software: you can redistribute it and/or modify it
53+# under the terms of the GNU General Public License version 3, as published
54+# by the Free Software Foundation.
55+#
56+# This program is distributed in the hope that it will be useful, but
57+# WITHOUT ANY WARRANTY; without even the implied warranties of
58+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
59+# PURPOSE. See the GNU General Public License for more details.
60+#
61+# You should have received a copy of the GNU General Public License along
62+# with this program. If not, see <http://www.gnu.org/licenses/>.
63+"""Implementation of network state detection."""
64+
65+import dbus
66+
67+from ubuntu_sso.logger import setupLogging
68+logger = setupLogging("ubuntu_sso.networkstate")
69+
70+# Values returned by the callback
71+ONLINE, OFFLINE, UNKNOWN = object(), object(), object()
72+
73+nm_state_names = {
74+ ONLINE: "online",
75+ OFFLINE: "offline",
76+ UNKNOWN: "unknown",
77+}
78+
79+# Internal NetworkManager State constants
80+NM_STATE_UNKNOWN = 0
81+NM_STATE_ASLEEP = 1
82+NM_STATE_CONNECTING = 2
83+NM_STATE_CONNECTED = 3
84+NM_STATE_DISCONNECTED = 4
85+
86+NM_DBUS_INTERFACE = "org.freedesktop.NetworkManager"
87+NM_DBUS_OBJECTPATH = "/org/freedesktop/NetworkManager"
88+DBUS_UNKNOWN_SERVICE = "org.freedesktop.DBus.Error.ServiceUnknown"
89+
90+class NetworkManagerState(object):
91+ """Checks the state of NetworkManager thru DBus."""
92+
93+ def __init__(self, result_cb, dbus=dbus):
94+ """Initialize this instance with a result and error callbacks."""
95+ self.result_cb = result_cb
96+ self.dbus = dbus
97+ self.state_signal = None
98+
99+ def call_result_cb(self, state):
100+ """Return the state thru the result callback."""
101+ if self.state_signal:
102+ self.state_signal.remove()
103+ self.result_cb(state)
104+
105+ def got_state(self, state):
106+ """Called by DBus when the state is retrieved from NM."""
107+ if state == NM_STATE_CONNECTED:
108+ self.call_result_cb(ONLINE)
109+ elif state == NM_STATE_CONNECTING:
110+ logger.debug("Currently connecting, waiting for signal")
111+ else:
112+ self.call_result_cb(OFFLINE)
113+
114+ def got_error(self, error):
115+ """Called by DBus when the state is retrieved from NM."""
116+ if isinstance(error, self.dbus.exceptions.DBusException) and \
117+ error.get_dbus_name() == DBUS_UNKNOWN_SERVICE:
118+ logger.debug("Network Manager not present")
119+ self.call_result_cb(UNKNOWN)
120+ else:
121+ logger.error("Error contacting NetworkManager: %s" % \
122+ str(error))
123+ self.call_result_cb(UNKNOWN)
124+
125+ def state_changed(self, state):
126+ """Called when a signal is emmited by Network Manager."""
127+ if int(state) == NM_STATE_CONNECTED:
128+ self.call_result_cb(ONLINE)
129+ elif int(state) == NM_STATE_DISCONNECTED:
130+ self.call_result_cb(OFFLINE)
131+ else:
132+ logger.debug("Not yet connected: continuing to wait")
133+
134+ def find_online_state(self):
135+ """Get the network state and return it thru the set callback."""
136+ try:
137+ sysbus = self.dbus.SystemBus()
138+ nm_proxy = sysbus.get_object(NM_DBUS_INTERFACE,
139+ NM_DBUS_OBJECTPATH,
140+ follow_name_owner_changes=True)
141+ nm_if = self.dbus.Interface(nm_proxy, NM_DBUS_INTERFACE)
142+ self.state_signal = nm_if.connect_to_signal(
143+ signal_name="StateChanged",
144+ handler_function=self.state_changed,
145+ dbus_interface=NM_DBUS_INTERFACE)
146+ nm_proxy.Get(NM_DBUS_INTERFACE, "State",
147+ reply_handler=self.got_state,
148+ error_handler=self.got_error)
149+ except Exception, e:
150+ self.got_error(e)
151
152=== added file 'ubuntu_sso/tests/test_networkstate.py'
153--- ubuntu_sso/tests/test_networkstate.py 1970-01-01 00:00:00 +0000
154+++ ubuntu_sso/tests/test_networkstate.py 2010-07-26 18:49:44 +0000
155@@ -0,0 +1,159 @@
156+# test_networkstate - tests for ubuntu_sso.networkstate
157+#
158+# Author: Alejandro J. Cura <alecu@canonical.com>
159+#
160+# Copyright 2010 Canonical Ltd.
161+#
162+# This program is free software: you can redistribute it and/or modify it
163+# under the terms of the GNU General Public License version 3, as published
164+# by the Free Software Foundation.
165+#
166+# This program is distributed in the hope that it will be useful, but
167+# WITHOUT ANY WARRANTY; without even the implied warranties of
168+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
169+# PURPOSE. See the GNU General Public License for more details.
170+#
171+# You should have received a copy of the GNU General Public License along
172+# with this program. If not, see <http://www.gnu.org/licenses/>.
173+"""Tests for the network state detection code."""
174+
175+from ubuntu_sso.networkstate import (NetworkManagerState,
176+ DBUS_UNKNOWN_SERVICE,
177+ ONLINE, OFFLINE, UNKNOWN,
178+ NM_STATE_DISCONNECTED,
179+ NM_STATE_CONNECTING,
180+ NM_STATE_CONNECTED)
181+
182+from mocker import ARGS, KWARGS, ANY, MockerTestCase
183+
184+class TestException(Exception):
185+ """An exception to test error conditions."""
186+ def get_dbus_name(self):
187+ """A fake dbus name for this exception."""
188+ return "Test Exception Message"
189+
190+class TestNmNotAvailableException(Exception):
191+ """An exception to test unavailability conditions."""
192+ def get_dbus_name(self):
193+ """The real name of the dbus error when NM is not running."""
194+ return DBUS_UNKNOWN_SERVICE
195+
196+class NetworkManagerStateTestCase(MockerTestCase):
197+ """Test NetworkManager state retrieval code."""
198+
199+ def setUp(self):
200+ """Setup the mocker dbus object tree."""
201+ self.dbusmock = self.mocker.mock()
202+ self.dbusmock.SystemBus()
203+ sysbusmock = self.mocker.mock()
204+ self.mocker.result(sysbusmock)
205+
206+ sysbusmock.get_object(ARGS, KWARGS)
207+ proxymock = self.mocker.mock()
208+ self.mocker.result(proxymock)
209+
210+ self.dbusmock.Interface(proxymock, ANY)
211+ ifmock = self.mocker.mock()
212+ self.mocker.result(ifmock)
213+
214+ ifmock.connect_to_signal(ARGS, KWARGS)
215+ signalmock = self.mocker.mock()
216+ self.mocker.result(signalmock)
217+
218+ proxymock.Get(ARGS, KWARGS)
219+ signalmock.remove()
220+
221+ self.mocker.replay()
222+
223+ def test_nm_online(self):
224+ """Check the connected case."""
225+ def got_state_cb(state):
226+ self.assertEquals(state, ONLINE)
227+
228+ ns = NetworkManagerState(got_state_cb, self.dbusmock)
229+ ns.find_online_state()
230+ ns.got_state(NM_STATE_CONNECTED)
231+
232+ def test_nm_offline(self):
233+ """Check the disconnected case."""
234+ def got_state_cb(state):
235+ self.assertEquals(state, OFFLINE)
236+
237+ ns = NetworkManagerState(got_state_cb, self.dbusmock)
238+ ns.find_online_state()
239+ ns.got_state(NM_STATE_DISCONNECTED)
240+
241+ def test_nm_connecting_then_online(self):
242+ """Check the waiting for connection case."""
243+ def got_state_cb(state):
244+ self.assertEquals(state, ONLINE)
245+
246+ ns = NetworkManagerState(got_state_cb, self.dbusmock)
247+ ns.find_online_state()
248+ ns.got_state(NM_STATE_CONNECTING)
249+ ns.state_changed(NM_STATE_CONNECTED)
250+
251+ def test_nm_connecting_then_offline(self):
252+ """Check the waiting but fail case."""
253+ def got_state_cb(state):
254+ self.assertEquals(state, OFFLINE)
255+
256+ ns = NetworkManagerState(got_state_cb, self.dbusmock)
257+ ns.find_online_state()
258+ ns.got_state(NM_STATE_CONNECTING)
259+ ns.state_changed(NM_STATE_DISCONNECTED)
260+
261+class NetworkManagerStateErrorsTestCase(MockerTestCase):
262+ """Test NetworkManager state retrieval code."""
263+ def setUp(self):
264+ """Setup the mocker dbus object tree."""
265+ self.dbusmock = self.mocker.mock()
266+ self.dbusmock.SystemBus()
267+ self.sysbusmock = self.mocker.mock()
268+ self.mocker.result(self.sysbusmock)
269+ self.sysbusmock.get_object(ARGS, KWARGS)
270+
271+ def mock_except_while_getting_proxy(self, e):
272+ """Simulate an exception while getting the DBus proxy object."""
273+ self.mocker.throw(e)
274+ self.dbusmock.exceptions.DBusException
275+ self.mocker.result(e)
276+ self.mocker.replay()
277+
278+ def mock_dbus_error_while_getting_state(self, e):
279+ """Simulate an exception while getting the State."""
280+ proxymock = self.mocker.mock()
281+ self.mocker.result(proxymock)
282+
283+ self.dbusmock.Interface(proxymock, ANY)
284+ ifmock = self.mocker.mock()
285+ self.mocker.result(ifmock)
286+
287+ ifmock.connect_to_signal(ARGS, KWARGS)
288+ signalmock = self.mocker.mock()
289+ self.mocker.result(signalmock)
290+
291+ proxymock.Get(ARGS, KWARGS)
292+ self.dbusmock.exceptions.DBusException
293+ self.mocker.result(e)
294+ signalmock.remove()
295+ self.mocker.replay()
296+
297+ def test_nm_not_running(self):
298+ """Check the case when NM is not running."""
299+ def got_state_cb(state):
300+ self.assertEquals(state, UNKNOWN)
301+
302+ self.mock_dbus_error_while_getting_state(TestNmNotAvailableException)
303+ ns = NetworkManagerState(got_state_cb, self.dbusmock)
304+ ns.find_online_state()
305+ ns.got_error(TestNmNotAvailableException())
306+
307+ def test_dbus_problem(self):
308+ """Check the case when DBus throws some other exception."""
309+ def got_state_cb(state):
310+ self.assertEquals(state, UNKNOWN)
311+
312+ self.mock_except_while_getting_proxy(TestException)
313+ ns = NetworkManagerState(got_state_cb, self.dbusmock)
314+ ns.find_online_state()

Subscribers

People subscribed via source and target branches