Merge lp:~brendan-donegan/checkbox/bug1066094 into lp:checkbox

Proposed by Brendan Donegan
Status: Work in progress
Proposed branch: lp:~brendan-donegan/checkbox/bug1066094
Merge into: lp:checkbox
Diff against target: 171 lines (+65/-23)
2 files modified
debian/changelog (+2/-0)
scripts/network_restart (+63/-23)
To merge this branch: bzr merge lp:~brendan-donegan/checkbox/bug1066094
Reviewer Review Type Date Requested Status
Marc Tardif (community) Needs Fixing
Review via email: mp+129880@code.launchpad.net

Commit message

Fix bug 1066094 by dealing with each case where superuser is needed in network_restart individually

Description of the change

As pointed out in the linked bug, the network_restart script took a very brute-force approach to informing the user of the script that superuser permissions may be required for specific operations - i.e. it assumes that they are and doesn't allow the script to be run without them. This has been changed so that for each command that may require superuser permissions, if an error occurs then a check is done to see if the script is not being run as the superuser and the user of the script is informed if that's the case.

To post a comment you must log in.
1778. By Brendan Donegan

Tweaks to the code.

Revision history for this message
Marc Tardif (cr3) wrote :

> - output = check_output(['/sbin/ifconfig', '-s', '-a'])
> + output = check_output(['ifconfig', '-s', '-a'])

Thank you for changing the path to ifconfig and the other commands from
absolute to just their names!

> - for interface in self.interfaces:
> + for iface in self.interfaces:

What was wrong with the name "interface"?

> try:
> - check_output(['/sbin/ifconfig', interface, 'up'])
> + check_output(['ifconfig', iface, 'up'])
> except CalledProcessError:

This will still output the stderr of ifconfig to the terminal. The
exception raised by the check_output function only has one output
attribute, so you need to pipe stderr into stdout:

    check_output(
        ['/sbin/ifconfig', interface, 'up'],
        stderr=subprocess.STDOUT)

> - logging.error('Unable to bring up interface {0!r}'
> - .format(interface))
> + message = 'Unable to bring up interface {0!r}'.format(iface)
> +
> + if os.getuid():
> + message += self.SUPERUSER_MSG
> +
> + message += '.'
> +
> + print(message, file=sys.stderr)
> + logging.error(message)
> raise

How do you know that just because check_output raised a CalledProcessError
that it was because of missing superuser privileges? Here's another error
that has nothing to do with privileges:

    $ ifconfig eth8 up
    eth8: ERROR while getting interface flags: No such device

So, once you piped stderr into stdout, you should use the output variable
in the exception to provide a useful and accurate error message to the
user:

    message = "Unable to bring up interface {0!r}:\n".format(interface)
    message += exception.output.decode("utf-8")
    print(message, file=sys.stderr)
    logging.error(message)
    raise

the same applies for the other calls that assumer that any error is because
of missing superuser privileges.

> - default='/var/log',
> + default='.',
> help='The path to the log directory. \
> Default is /var/log')

The problem here is that the default kwarg was changed but the help wasn't
changed accordingly. The solution would be to use %(default)s in the help
instead of hard coding a duplicate value.

Revision history for this message
Marc Tardif (cr3) wrote :

See above comment, I wonder how I can set the review to "needs fixing" by email...

review: Needs Fixing
Revision history for this message
Brendan Donegan (brendan-donegan) wrote :

On 16/10/12 14:52, Marc Tardif wrote:
>> - output = check_output(['/sbin/ifconfig', '-s', '-a'])
>> + output = check_output(['ifconfig', '-s', '-a'])
> Thank you for changing the path to ifconfig and the other commands from
> absolute to just their names!
You're welcome :)
>
>> - for interface in self.interfaces:
>> + for iface in self.interfaces:
> What was wrong with the name "interface"?
Not a lot, just that it avoids having to split certain statements across
multiple lines. I reckoned iface is a fairly common contraction of
interface so it shouldn't be confusing
>
>> try:
>> - check_output(['/sbin/ifconfig', interface, 'up'])
>> + check_output(['ifconfig', iface, 'up'])
>> except CalledProcessError:
> This will still output the stderr of ifconfig to the terminal. The
> exception raised by the check_output function only has one output
> attribute, so you need to pipe stderr into stdout:
This is an existing problem with the script as far as I know, but it
would be ok to fix it now.
>
> check_output(
> ['/sbin/ifconfig', interface, 'up'],
> stderr=subprocess.STDOUT)
>
>> - logging.error('Unable to bring up interface {0!r}'
>> - .format(interface))
>> + message = 'Unable to bring up interface {0!r}'.format(iface)
>> +
>> + if os.getuid():
>> + message += self.SUPERUSER_MSG
>> +
>> + message += '.'
>> +
>> + print(message, file=sys.stderr)
>> + logging.error(message)
>> raise
> How do you know that just because check_output raised a CalledProcessError
> that it was because of missing superuser privileges? Here's another error
> that has nothing to do with privileges:
>
> $ ifconfig eth8 up
> eth8: ERROR while getting interface flags: No such device
>
> So, once you piped stderr into stdout, you should use the output variable
> in the exception to provide a useful and accurate error message to the
> user:
>
> message = "Unable to bring up interface {0!r}:\n".format(interface)
> message += exception.output.decode("utf-8")
> print(message, file=sys.stderr)
> logging.error(message)
> raise
Sounds like a fine method, I'll give it a try
>
> the same applies for the other calls that assumer that any error is because
> of missing superuser privileges.
>
>> - default='/var/log',
>> + default='.',
>> help='The path to the log directory. \
>> Default is /var/log')
> The problem here is that the default kwarg was changed but the help wasn't
> changed accordingly. The solution would be to use %(default)s in the help
> instead of hard coding a duplicate value.
I fixed this specific issue but in principle where defaults are
mentioned in help text that convention should be used, so I'll change it.
>

Revision history for this message
Brendan Donegan (brendan-donegan) wrote :

Rather disappointingly, the contents of exception.output in the case of 'start network-manager' failing are:

'Unable to stop network-manager, stop: Rejected send message, 1 matched rules; type="method_call", sender=":1.159" (uid=1000 pid=20099 comm="stop network-manager ") interface="com.ubuntu.Upstart0_6.Job" member="Stop" error name="(unset)" requested_reply="0" destination="com.ubuntu.Upstart" (uid=0 pid=1 comm="/sbin/init")\n'

Not exactly helpful. I appreciate the observation that we shouldn't assume that lack of superuser privileges caused a command to fail, but if this is the alternative then it's not really helpful.

Revision history for this message
Marc Tardif (cr3) wrote :

As mentionned in #ubuntu-server, I would suggest we special case upstart errors:

  when the output string contains "Rejected send message", change the message to invalid permission
  otherwise, use the original message

Revision history for this message
Brendan Donegan (brendan-donegan) wrote :

I don't have time to work on this right now and it's a very low priority fix

Unmerged revisions

1778. By Brendan Donegan

Tweaks to the code.

1777. By Brendan Donegan

scripts/network_restart - be specific about where superuser permissions
are required (LP: #1066094)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2012-10-15 20:03:32 +0000
3+++ debian/changelog 2012-10-16 13:20:27 +0000
4@@ -7,6 +7,8 @@
5 don't get tampered with (LP: #1065932)
6 * plugins/launchpad_exchange.py - Remove call to string_to_type on
7 self.timeout, which is an int (LP: #1066967)
8+ * scripts/network_restart - be specific about where superuser permissions
9+ are required (LP: #1066094)
10
11 [Daniel Manrique]
12 * [FEATURE] checkbox/job.py: Fixed intltool warning about unnamed
13
14=== modified file 'scripts/network_restart'
15--- scripts/network_restart 2012-10-12 20:16:20 +0000
16+++ scripts/network_restart 2012-10-16 13:20:27 +0000
17@@ -21,6 +21,7 @@
18 except (ImportError, RuntimeError):
19 gtk_found = False
20
21+SUPERUSER_MSG = ", superuser permissions required"
22
23 class PingError(Exception):
24 def __init__(self, address, reason):
25@@ -31,12 +32,6 @@
26 def main():
27 args = parse_args()
28
29- # Verify that script is run as root
30- if os.getuid():
31- sys.stderr.write(
32- 'This script needs superuser permissions to run correctly\n')
33- return 1
34-
35 configure_logging(args.log_level, args.output)
36
37 # Select interface based on graphich capabilities available
38@@ -196,6 +191,8 @@
39 """
40 Networking abstraction to start/stop all interfaces
41 """
42+
43+
44 def __init__(self, address):
45 self.address = address
46 self.interfaces = self._get_interfaces()
47@@ -204,7 +201,7 @@
48 """
49 Get all network interfaces
50 """
51- output = check_output(['/sbin/ifconfig', '-s', '-a'])
52+ output = check_output(['ifconfig', '-s', '-a'])
53 lines = output.splitlines()[1:]
54 interfaces = ([interface for interface in
55 [line.split()[0] for line in lines] if interface != 'lo'])
56@@ -222,19 +219,34 @@
57 Start networking
58 """
59 logging.info('Bringing all interfaces up...')
60- for interface in self.interfaces:
61+ for iface in self.interfaces:
62 try:
63- check_output(['/sbin/ifconfig', interface, 'up'])
64+ check_output(['ifconfig', iface, 'up'])
65 except CalledProcessError:
66- logging.error('Unable to bring up interface {0!r}'
67- .format(interface))
68+ message = 'Unable to bring up interface {0!r}'.format(iface)
69+
70+ if os.getuid():
71+ message += SUPERUSER_MSG
72+
73+ message += '.'
74+
75+ print(message, file=sys.stderr)
76+ logging.error(message)
77 raise
78
79 logging.info('Starting network manager...')
80 try:
81- check_output(['/sbin/start', 'network-manager'])
82+ check_output(['start', 'network-manager'])
83 except CalledProcessError:
84- logging.error('Unable to start network manager')
85+ message = 'Unable to stop network-manager'
86+
87+ if os.getuid():
88+ message += SUPERUSER_MSG
89+
90+ message += "."
91+
92+ print(message)
93+ logging.error(message)
94 raise
95
96 # Verify that network interface is up
97@@ -245,8 +257,7 @@
98 if success:
99 break
100 else:
101- raise PingError(self.address,
102- 'Some interface is still down')
103+ raise PingError(self.address, 'Some interface is still down')
104
105 def _stop(self):
106 """
107@@ -254,18 +265,33 @@
108 """
109 logging.info('Stopping network manager...')
110 try:
111- check_output(['/sbin/stop', 'network-manager'])
112+ check_output(['stop', 'network-manager'])
113 except CalledProcessError:
114- logging.error('Unable to stop network manager')
115+ message = 'Unable to stop network-manager'
116+
117+ if os.getuid():
118+ message += SUPERUSER_MSG
119+
120+ message += "."
121+
122+ print(message, file=sys.stderr)
123+ logging.error(message)
124 raise
125
126 logging.info('Bringing all interfaces down...')
127- for interface in self.interfaces:
128+ for iface in self.interfaces:
129 try:
130- check_output(['/sbin/ifconfig', interface, 'down'])
131+ check_output(['ifconfig', iface, 'down'])
132 except CalledProcessError:
133- logging.error('Unable to bring down interface {0!r}'
134- .format(interface))
135+ message = "Unable to bring down interface {0!r}".format(iface)
136+
137+ if os.getuid():
138+ message += SUPERUSER_MSG
139+
140+ message += "."
141+
142+ print(message, file=sys.stderr)
143+ logging.error(message)
144 raise
145
146 # Verify that network interface is down
147@@ -327,8 +353,22 @@
148 log_filename = os.path.join(output,
149 '{0}.log'.format(os.path.basename(__file__)))
150 rollover = os.path.exists(log_filename)
151- log_handler = logging.handlers.RotatingFileHandler(log_filename, mode='a+',
152- backupCount=3)
153+
154+ try:
155+ log_handler = logging.handlers.RotatingFileHandler(log_filename,
156+ mode='a+',
157+ backupCount=3)
158+ except IOError:
159+ message = 'Unable to log to {0}'.format(log_filename)
160+
161+ if os.getuid():
162+ message += SUPERUSER_MSG
163+
164+ message += '.'
165+
166+ print(message)
167+ return
168+
169 formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s')
170 log_handler.setFormatter(formatter)
171 log_handler.setLevel(logging.DEBUG)

Subscribers

People subscribed via source and target branches