Merge ~pieq/bugit/+git/qabro:more-logs-in-reports into bugit:master

Proposed by Pierre Equoy
Status: Merged
Approved by: Pierre Equoy
Approved revision: 9f3de5b81b1726d0499c90db55b7c30fe80bf81f
Merged at revision: 8718393b29a183ceb4edbc6046866b08a468b28d
Proposed branch: ~pieq/bugit/+git/qabro:more-logs-in-reports
Merge into: bugit:master
Diff against target: 517 lines (+161/-109)
6 files modified
dev/null (+0/-9)
patches/sosreport-env-path-snap.patch (+22/-0)
qabro/__init__.py (+14/-7)
qabro/bug_assistant.py (+39/-36)
qabro/ui.py (+33/-37)
snap/snapcraft.yaml (+53/-20)
Reviewer Review Type Date Requested Status
Sylvain Pineau (community) Approve
Maciej Kisielewski Pending
OEM Services QA Pending
Review via email: mp+358000@code.launchpad.net

Description of the change

The major change of this is made by commit e09dc48 and allows sosreport to run commands like lspci, gather the output and store it in the archive sent to Launchpad.

It was a massive headache and it's still far from being the best solution, but it's already a big improvement over current version of qabro snap.

To test:

1. Generate a snap:

    $ snapcraft clean && snapcraft

2. If using core < 2.36, activate snap layouts "experimental" feature:

    $ sudo snap set core experimental.layouts=true

(snap layouts are experimental until snapd 2.36)

3. Install qabro:

    $ sudo snap install qabro_0.7dev_amd64.snap --devmode

4. Launch qabro and use the staging Launchpad instance for testing:

    $ APPORT_LAUNCHPAD_INSTANCE=staging qabro

The generated report should include things like the correct output for pci and usb plugins (i.e. stuff in sos_commands/pci/ and sos_commands/usb/ should not be empty!)

I tested it on:

    - 18.04
    - UC16
    - UC18

To post a comment you must log in.
Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

Lots of good stuff, the patch for sosreport is an excellent workaround to get lspci data.*
and 2.36 is in stable now.

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/config/sos.conf b/config/sos.conf
2deleted file mode 100644
3index d5b5377..0000000
4--- a/config/sos.conf
5+++ /dev/null
6@@ -1,9 +0,0 @@
7-[plugins]
8-
9-#disable = rpm, selinux, dovecot
10-
11-[tunables]
12-
13-#rpm.rpmva = off
14-#general.syslogsize = 15
15-
16diff --git a/patches/sosreport-env-path-snap.patch b/patches/sosreport-env-path-snap.patch
17new file mode 100644
18index 0000000..b4f95c1
19--- /dev/null
20+++ b/patches/sosreport-env-path-snap.patch
21@@ -0,0 +1,22 @@
22+diff -ru /tmp/sos-3.5/sos/policies/ubuntu.py usr/share/sosreport/sos/policies/ubuntu.py
23+--- /tmp/sos-3.5/sos/policies/ubuntu.py 2017-11-02 20:54:47.000000000 +0800
24++++ usr/share/sosreport/sos/policies/ubuntu.py 2018-10-24 15:28:46.439206314 +0800
25+@@ -3,11 +3,18 @@
26+ from sos.plugins import UbuntuPlugin, DebianPlugin
27+ from sos.policies.debian import DebianPolicy
28+
29++import os
30+
31+ class UbuntuPolicy(DebianPolicy):
32+ distro = "Ubuntu"
33+ vendor = "Ubuntu"
34+ vendor_url = "http://www.ubuntu.com/"
35++ PATH = ("/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"
36++ ":/usr/local/sbin:/usr/local/bin"
37++ ":$SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$SNAP/usr/games"
38++ ":$SNAP/usr/local/games:$SNAP/usr/local/sbin:$SNAP/usr/local/bin"
39++ )
40++ PATH = os.path.expandvars(PATH)
41+
42+ def __init__(self, sysroot=None):
43+ super(UbuntuPolicy, self).__init__(sysroot=sysroot)
44diff --git a/qabro/__init__.py b/qabro/__init__.py
45index 358ed45..ace1c53 100644
46--- a/qabro/__init__.py
47+++ b/qabro/__init__.py
48@@ -1,9 +1,12 @@
49-import logging, os, subprocess, sys
50-from time import sleep
51+import logging
52+import os
53+import subprocess
54+import sys
55
56 from qabro.bug_assistant import BugAssistant
57 from qabro.ui import ReportScreen
58
59+
60 def main():
61 if os.geteuid() == 0:
62 sys.exit("This tool cannot be run as super user.")
63@@ -21,18 +24,22 @@ def main():
64 sys.exit("This tool requires --devmode option to run properly.")
65
66 logfile = os.path.join(os.getenv('SNAP_USER_COMMON', '/tmp'), 'qabro.log')
67- logging.basicConfig(filename=logfile,
68- format='%(asctime)s - %(levelname)s - %(message)s',
69- level=logging.ERROR
70- )
71+ loglevel = os.getenv('LOGLEVEL', 'ERROR')
72+ logformat = ("%(asctime)s - %(levelname)s - "
73+ "%(module)s:%(funcName)s:%(lineno)d - %(message)s")
74+ numeric_level = getattr(logging, loglevel.upper(), None)
75+ if not isinstance(numeric_level, int):
76+ raise ValueError('Invalid log level: %s' % loglevel)
77+ logging.basicConfig(filename=logfile, format=logformat, level=numeric_level)
78 start_ui()
79
80+
81 def start_ui():
82 ui = ReportScreen()
83 ui.run()
84 ba = BugAssistant(ui.report())
85 while True:
86- try:
87+ try:
88 result = ba.file_bug()
89 except Exception as exc:
90 ui.message.set_text([('error', "Error: "), str(exc)])
91diff --git a/qabro/bug_assistant.py b/qabro/bug_assistant.py
92index 87cbb62..adce592 100644
93--- a/qabro/bug_assistant.py
94+++ b/qabro/bug_assistant.py
95@@ -1,24 +1,29 @@
96-import getpass, os, re, subprocess, sys, tarfile
97+import getpass
98+import os
99+import re
100+import subprocess
101+import sys
102+import tarfile
103 from collections import Counter
104-from httplib2 import ServerNotFoundError
105
106-from launchpadlib.launchpad import (
107- Launchpad, EDGE_SERVICE_ROOT, STAGING_SERVICE_ROOT
108- )
109-from launchpadlib.uris import LPNET_WEB_ROOT, STAGING_WEB_ROOT
110+from launchpadlib.launchpad import Launchpad
111+from launchpadlib.uris import LPNET_WEB_ROOT, STAGING_WEB_ROOT, QASTAGING_WEB_ROOT
112+
113
114 class BugReport:
115 """Bug report.
116-
117+
118 A bug report contains all the textual information that can be transmitted
119 to Launchpad, such as the bug title, description, related project, etc. It
120 also contains a dictionary of options.
121 """
122- def __init__(self, title, description, project, series, assignee, tags, status, importance, options):
123+ def __init__(self, title, description, project, series, assignee, tags,
124+ status, importance, options):
125 self.update(title, description, project, series, assignee, tags,
126 status, importance, options)
127
128- def update(self, title, description, project, series, assignee, tags, status, importance, options):
129+ def update(self, title, description, project, series, assignee, tags,
130+ status, importance, options):
131 self.title = title
132 self.description = description
133 self.project = project
134@@ -28,7 +33,7 @@ class BugReport:
135 self.status = status
136 self.importance = importance
137 self.options = options
138-
139+
140
141 class BugAssistant:
142 """Handle a bug report, its attachments and their transmission to Launchpad."""
143@@ -51,13 +56,9 @@ class BugAssistant:
144
145 def launchpad_login(self):
146 """Log in Launchpad and create credentials file if needed."""
147- if ('APPORT_STAGING' in os.environ
148- or os.environ.get('APPORT_LAUNCHPAD_INSTANCE', None) == 'staging'):
149- print('Using staging service root')
150- service_root = STAGING_SERVICE_ROOT
151- else:
152- print('Using edge service root')
153- service_root = EDGE_SERVICE_ROOT
154+ # can be 'production', 'staging' or 'qastaging'
155+ service_root = os.environ.get('APPORT_LAUNCHPAD_INSTANCE', 'production')
156+ print('Using {} service root'.format(service_root))
157
158 directory = os.path.expanduser('~/.launchpadlib')
159 if not os.path.isdir(directory):
160@@ -91,7 +92,6 @@ class BugAssistant:
161 try:
162 print('Checking project name...')
163 project = launchpad.projects[self.lp_project]
164- #logging.debug('Project found: {0}'.format(project))
165 except Exception as ex:
166 error_message = ('{} launchpad project not found'
167 .format(self.lp_project))
168@@ -105,7 +105,6 @@ class BugAssistant:
169 if series is None:
170 error_message = ('{} series not found'
171 .format(series_name))
172- #logging.error(error_message)
173 raise BugAssistantError(error_message)
174
175 assignee_name = self.lp_assignee
176@@ -119,6 +118,7 @@ class BugAssistant:
177 .format(assignee_name))
178 raise BugAssistantError(error_message)
179
180+ print('Getting standard bug information...')
181 std_info = AttachmentAssistant.get_standard_info()
182 std_info_str = '\n'.join(['{}: {}'.format(elt, std_info[elt])
183 for elt in std_info])
184@@ -126,9 +126,9 @@ class BugAssistant:
185 if not self.bug:
186 print('Creating Launchpad bug report...')
187 self.bug = launchpad.bugs.createBug(title=self.lp_title,
188- description=description,
189- tags=self.lp_tags.split(),
190- target=project)
191+ description=description,
192+ tags=self.lp_tags.split(),
193+ target=project)
194 print('Bug report #{} created.'.format(self.bug.id))
195 else:
196 print('Updating Launchpad bug report...')
197@@ -158,8 +158,10 @@ class BugAssistant:
198 nomination = self.bug.addNomination(target=series)
199 nomination.approve()
200
201- if ('APPORT_STAGING' in os.environ
202- or os.environ.get('APPORT_LAUNCHPAD_INSTANCE', None) == 'staging'):
203+ service_root = os.environ.get('APPORT_LAUNCHPAD_INSTANCE', 'production')
204+ if service_root == 'qastaging':
205+ bug_url = QASTAGING_WEB_ROOT+'bugs/{}'.format(self.bug.id)
206+ elif service_root == 'staging':
207 bug_url = STAGING_WEB_ROOT+'bugs/{}'.format(self.bug.id)
208 else:
209 bug_url = LPNET_WEB_ROOT+'bugs/{}'.format(self.bug.id)
210@@ -171,7 +173,8 @@ class BugAssistant:
211 aa.run_attachment_methods()
212 aa.upload_attachments(launchpad, self.bug.id)
213
214- return 'Bug report #{} and it attachments are available at <{}>'.format(self.bug.id, bug_url)
215+ result = "Bug report #{} and its attachments are available at <{}>"
216+ return result.format(self.bug.id, bug_url)
217
218
219 class BugAssistantError(Exception):
220@@ -181,7 +184,7 @@ class BugAssistantError(Exception):
221
222 class AttachmentAssistant:
223 """Handle attachments and related tasks.
224-
225+
226 This class provides a dictionary of attachments that is populated by the
227 different `attach_*` methods. These attachments can then be uploaded to
228 the given launchpad bug using `upload_attachments`.
229@@ -215,16 +218,16 @@ class AttachmentAssistant:
230 home_dir = os.getenv('HOME')
231 user = getpass.getuser()
232 p = os.getenv('PATH')
233- conf_file = os.path.expandvars('$SNAP/etc/sos.conf')
234+ s = os.getenv('SNAP')
235 if p:
236 print("Running sosreport...")
237 try:
238 # Call sosreport while preserving the user $PATH when using sudo
239 # to have access to the sosreport version from the snap
240- command = ["sudo", "env", "PATH={}".format(p), "sosreport",
241- "--no-report", "--batch", "--tmp-dir", home_dir,
242- "--config-file", conf_file]
243- output = subprocess.check_output(command).decode(sys.stdout.encoding)
244+ command = ["sudo", "env", "SNAP={}".format(s),
245+ "PATH={}".format(p), "sosreport", "--no-report",
246+ "--batch", "--tmp-dir", home_dir]
247+ output = subprocess.check_output(command).decode('utf-8')
248 archive_path = output.splitlines()[-6].strip()
249 print("Report generated: {}".format(archive_path))
250 command = ["sudo", "chown", "{}:{}".format(user, user), archive_path]
251@@ -259,11 +262,12 @@ class AttachmentAssistant:
252 if os.path.exists(classic_path):
253 rootpath = classic_path
254 else:
255- out = subprocess.check_output(["snap", "list"]).decode(sys.stdout.encoding)
256+ out = subprocess.check_output(["snap", "list"]).decode('utf-8')
257 snap_name_list = [e for e in out.split() if 'checkbox' in e]
258 if snap_name_list:
259 snap_name = snap_name_list[0]
260- snap_path = os.path.expandvars("/home/$USER/snap/{}/current/plainbox/sessions".format(snap_name))
261+ sp = "/home/$USER/snap/{}/current/plainbox/sessions"
262+ snap_path = os.path.expandvars(sp.format(snap_name))
263 if os.path.exists(snap_path):
264 rootpath = snap_path
265
266@@ -330,7 +334,7 @@ class AttachmentAssistant:
267 else:
268 # Otherwise, try to use the first version found on the system
269 command = ["sudo", "dmidecode", "-s"]
270-
271+
272 for dmi_value in ['system-manufacturer', 'system-product-name', 'bios-version']:
273 standard_info[dmi_value] = subprocess.check_output(
274 command + [dmi_value]).decode(sys.stdout.encoding).strip()
275@@ -365,7 +369,7 @@ class AttachmentAssistant:
276 cpu_names = Counter()
277 for cpu_number, cpu in enumerate(cpus):
278 if 'model name' in cpu:
279- cpu_name = ' '.join(cpu["model name"].split())
280+ cpu_name = ' '.join(cpu["model name"].split())
281 cpu_names[cpu_name] += 1
282 elif 'Processor' in cpu:
283 cpu_name = ' '.join(cpu["Processor"].split())
284@@ -375,4 +379,3 @@ class AttachmentAssistant:
285 for cpu_name, count in cpu_names.items()]
286
287 return '\n'.join(cpu_names_str)
288-
289diff --git a/qabro/ui.py b/qabro/ui.py
290index 806f78e..3767d1b 100644
291--- a/qabro/ui.py
292+++ b/qabro/ui.py
293@@ -1,15 +1,12 @@
294-from time import sleep
295 import sys
296 import urwid
297
298 from qabro.bug_assistant import BugReport
299 from qabro.__version__ import __version__ as qversion, __title__ as qtitle
300
301+
302 class ReportScreen:
303 palette = [
304- #('body', 'light gray', 'black'),
305- #('focus', 'black', 'light gray', 'standout'),
306- #('head', 'black', 'light gray', 'standout'),
307 ('foot', 'light gray', 'black'),
308 ('title', 'white', 'black', 'bold'),
309 ('key', 'yellow,bold', ''),
310@@ -29,38 +26,37 @@ class ReportScreen:
311 'Medium',
312 'High',
313 'Critical')
314- bug_types = { "Audio": "hwe-audio",
315- "Bluetooth": "hwe-bluetooth",
316- "Camera": "oem-camera",
317- "External storage": "oem-storage",
318- "Touchpad": "hwe-touchpad",
319- "Touchscreen": "oem-touchscreen",
320- "Install": "hwe-installer",
321- "Media Card": "hwe-media",
322- "Networking (ethernet)": "hwe-networking-ethernet",
323- "Networking (wifi)": "hwe-networking-wifi",
324- "Networking (modem)": "hwe-networking-modem",
325- "Power management": "hwe-suspend-resume",
326- "Checkbox": "checkbox test-case",
327- "Video": "hwe-graphics",
328- "Hotkeys": "hwe-hotkeys",
329- "Firmware": "hwe-firmware",
330- "Missing driver": "hwe-needs-driver",
331- "Performance": "oem-performance",
332- "Other problem": "oem-other"}
333+ bug_types = {"Audio": "hwe-audio",
334+ "Bluetooth": "hwe-bluetooth",
335+ "Camera": "oem-camera",
336+ "External storage": "oem-storage",
337+ "Touchpad": "hwe-touchpad",
338+ "Touchscreen": "oem-touchscreen",
339+ "Install": "hwe-installer",
340+ "Media Card": "hwe-media",
341+ "Networking (ethernet)": "hwe-networking-ethernet",
342+ "Networking (wifi)": "hwe-networking-wifi",
343+ "Networking (modem)": "hwe-networking-modem",
344+ "Power management": "hwe-suspend-resume",
345+ "Checkbox": "checkbox test-case",
346+ "Video": "hwe-graphics",
347+ "Hotkeys": "hwe-hotkeys",
348+ "Firmware": "hwe-firmware",
349+ "Missing driver": "hwe-needs-driver",
350+ "Performance": "oem-performance",
351+ "Other problem": "oem-other"}
352
353 default_description = ("Summary:\n\n"
354 "Steps to reproduce:\n\n"
355 "Expected result:\n\n"
356 "Actual result:\n\n"
357- "Failure rate:"
358- )
359+ "Failure rate:")
360
361 def __init__(self):
362 self._title = urwid.LineBox(urwid.Edit(), 'Bug Title')
363 self._description = urwid.LineBox(urwid.Edit(edit_text=self.default_description,
364- multiline=True),
365- 'Description (including steps to reproduce)')
366+ multiline=True),
367+ "Description (including steps to reproduce)")
368 self._project = urwid.LineBox(urwid.Edit(), 'Project')
369 self._series = urwid.LineBox(urwid.Edit(), 'Series')
370 self._status = []
371@@ -71,7 +67,8 @@ class ReportScreen:
372 rb = urwid.RadioButton(self._importance, i, 'first True', None)
373 self._bug_type = []
374 for i in sorted(self.bug_types):
375- rb = urwid.RadioButton(self._bug_type, i, state=False, on_state_change=self.add_tag)
376+ rb = urwid.RadioButton(self._bug_type, i, state=False,
377+ on_state_change=self.add_tag)
378 self._assignee = urwid.LineBox(urwid.Edit(), 'Assigned To (Launchpad ID)')
379 self._tags = urwid.LineBox(urwid.Edit(), 'Tags')
380 status_list = urwid.LineBox(urwid.Pile(self._status), 'Status')
381@@ -80,13 +77,13 @@ class ReportScreen:
382 self.message = urwid.Text('')
383 self.footer = urwid.AttrMap(urwid.Text(self.footer_text), 'foot')
384 self.pile = urwid.Pile([self.message,
385- self._title,
386- self._description,
387- self._project,
388- self._series,
389- status_list,
390- importance_list,
391- self._assignee,
392+ self._title,
393+ self._description,
394+ self._project,
395+ self._series,
396+ status_list,
397+ importance_list,
398+ self._assignee,
399 self._tags,
400 bug_types_list])
401 self.view = urwid.Frame(urwid.Filler(self.pile, 'top'), footer=self.footer)
402@@ -108,8 +105,7 @@ class ReportScreen:
403 self.loop = urwid.MainLoop(self.view,
404 self.palette,
405 handle_mouse=False,
406- unhandled_input=self.unhandled_input
407- )
408+ unhandled_input=self.unhandled_input)
409 self.loop.run()
410
411 def unhandled_input(self, key):
412diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
413index 94d79e9..415c1e9 100644
414--- a/snap/snapcraft.yaml
415+++ b/snap/snapcraft.yaml
416@@ -22,8 +22,14 @@ apps:
417 environment:
418 LD_LIBRARY_PATH: $LD_LIBRARY_PATH:$SNAP/lib/fwts
419
420- acpidump:
421- command: acpidump-acpica
422+ lspci:
423+ command: lspci
424+
425+ lsusb:
426+ command: lsusb
427+
428+ amixer:
429+ command: amixer
430
431 parts:
432 qabro:
433@@ -42,30 +48,40 @@ parts:
434 - pciutils
435
436 sosreport:
437+ after: [patches]
438 plugin: nil
439 stage-packages:
440 - sosreport
441 - libc6
442 - libdb5.3
443+ override-build: |
444+ snapcraftctl build
445+ # Patch to add a bunch of $SNAP-related paths in the $PATH environment
446+ # variable used by sosreport to call other commands.
447+ patch $SNAPCRAFT_PART_INSTALL/usr/share/sosreport/sos/policies/ubuntu.py $SNAPCRAFT_STAGE/patches/sosreport-env-path-snap.patch
448
449 fwts:
450- plugin: autotools
451- source: git://kernel.ubuntu.com/hwe/fwts
452 source-tag: V18.06.02
453+
454+ lspci:
455+ plugin: nil
456 stage-packages:
457- - libfdt1
458- build-packages:
459- - gcc
460- - make
461- - autoconf
462- - automake
463- - libtool
464- - libjson-c-dev
465- - flex
466- - bison
467- - dh-autoreconf
468- - libglib2.0-dev
469- - libfdt-dev
470+ - pciutils
471+ - libc6
472+ - libkmod2
473+ - libpci3
474+
475+ lsusb:
476+ plugin: nil
477+ stage-packages:
478+ - usbutils
479+ - libc6
480+
481+ alsa:
482+ plugin: nil
483+ stage-packages:
484+ - alsa-utils
485+ - libc6
486
487 acpidump:
488 plugin: nil
489@@ -73,8 +89,25 @@ parts:
490 - acpica-tools
491 - libc6
492
493- config:
494+ dmsetup:
495+ plugin: nil
496+ stage-packages:
497+ - dmsetup
498+ - libc6
499+
500+ patches:
501+ source: patches
502 plugin: dump
503- source: config/
504 organize:
505- '*.conf': etc/
506+ '*': patches/
507+ prime: [-*]
508+
509+# https://forum.snapcraft.io/t/snap-layouts/7207
510+passthrough:
511+ layout:
512+ /etc/sos.conf:
513+ bind-file: $SNAP/etc/sos.conf
514+ /var/lib/usbutils/usb.ids:
515+ bind-file: $SNAP/var/lib/usbutils/usb.ids
516+ /usr/share/alsa:
517+ bind: $SNAP/usr/share/alsa

Subscribers

People subscribed via source and target branches

to all changes: