Merge ~pieq/bugit/+git/qabro:fix-1832181-additional-info into bugit:master

Proposed by Pierre Equoy
Status: Merged
Approved by: Pierre Equoy
Approved revision: f212b3c8fc9b3050c973cac4d5ae3f5f189a93f1
Merged at revision: ee43b9e85b21ec5407f1b9f03345baff8c4e997f
Proposed branch: ~pieq/bugit/+git/qabro:fix-1832181-additional-info
Merge into: bugit:master
Diff against target: 255 lines (+115/-31)
6 files modified
qabro/__init__.py (+4/-3)
qabro/__version__.py (+1/-1)
qabro/bug_assistant.py (+34/-17)
qabro/ui.py (+1/-1)
qabro/utils.py (+67/-0)
snap/snapcraft.yaml (+8/-9)
Reviewer Review Type Date Requested Status
Jonathan Cave (community) Approve
Review via email: mp+368694@code.launchpad.net

This proposal supersedes a proposal from 2019-06-12.

Description of the change

Check related bug for more information.

Tested by building a snap and running it on:

- Desktop 18.04
- OEM Desktop 18.04
- Ubuntu Core 16

making sure:

- the manifest field is retrieved when using OEM images
- all the GPUs are shown when the device has more than one

Note: this branch includes the changes made in https://code.launchpad.net/~pieq/qabro/+git/qabro/+ref/move-to-base18

To post a comment you must log in.
Revision history for this message
Jonathan Cave (jocave) wrote :

Looks reasonable to me. If you interested in another implementation of a snapd socket class that take a look at the one in checkbox-support: https://git.launchpad.net/checkbox-support/tree/checkbox_support/snap_utils/snapd.py

review: Approve
Revision history for this message
Pierre Equoy (pieq) wrote :

Thanks joc! I'll keep an eye on your implementation that looks more robust. If I need more complex use cases in the future I may switch to using it instead.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/qabro/__init__.py b/qabro/__init__.py
index 815374c..9c72446 100644
--- a/qabro/__init__.py
+++ b/qabro/__init__.py
@@ -6,6 +6,7 @@ import sys
66
7from qabro.bug_assistant import BugAssistant, BugReport7from qabro.bug_assistant import BugAssistant, BugReport
8from qabro.ui import ReportScreen8from qabro.ui import ReportScreen
9from qabro.utils import snapdsocket
910
1011
11logger = logging.getLogger(__name__)12logger = logging.getLogger(__name__)
@@ -20,10 +21,10 @@ def main():
20 sys.exit(("Cannot determine snap name. "21 sys.exit(("Cannot determine snap name. "
21 "Please install this tool using the snap command."))22 "Please install this tool using the snap command."))
2223
23 output = subprocess.check_output(['snap', 'list', snap_name],24 ss = snapdsocket()
24 universal_newlines=True)25 qabro_snap_info = [e for e in ss.get_snaps() if e['name'] == 'qabro'][0]
2526
26 if 'devmode' not in output:27 if not qabro_snap_info['devmode']:
27 sys.exit("This tool requires --devmode option to run properly.")28 sys.exit("This tool requires --devmode option to run properly.")
2829
29 logfile = os.path.join(os.getenv('SNAP_USER_COMMON', '/tmp'), 'qabro.log')30 logfile = os.path.join(os.getenv('SNAP_USER_COMMON', '/tmp'), 'qabro.log')
diff --git a/qabro/__version__.py b/qabro/__version__.py
index 992265c..29033e1 100644
--- a/qabro/__version__.py
+++ b/qabro/__version__.py
@@ -1,5 +1,5 @@
1__title__ = 'qabro'1__title__ = 'qabro'
2__version__ = '0.10.1'2__version__ = '0.11dev'
3__description__ = 'Tool to generate a Launchpad bug report and attach useful logs (using sosreport and the like).'3__description__ = 'Tool to generate a Launchpad bug report and attach useful logs (using sosreport and the like).'
4__author__ = 'Pierre Equoy'4__author__ = 'Pierre Equoy'
5__author_email__ = 'pierre.equoy@canonical.com'5__author_email__ = 'pierre.equoy@canonical.com'
diff --git a/qabro/bug_assistant.py b/qabro/bug_assistant.py
index 209eb12..12f6df4 100644
--- a/qabro/bug_assistant.py
+++ b/qabro/bug_assistant.py
@@ -1,8 +1,6 @@
1import getpass1import getpass
2import json
3import os2import os
4import re3import re
5import shutil
6import subprocess4import subprocess
7import sys5import sys
8import tarfile6import tarfile
@@ -11,6 +9,8 @@ from collections import Counter
11from launchpadlib.launchpad import Launchpad9from launchpadlib.launchpad import Launchpad
12from launchpadlib.uris import LPNET_WEB_ROOT, STAGING_WEB_ROOT, QASTAGING_WEB_ROOT10from launchpadlib.uris import LPNET_WEB_ROOT, STAGING_WEB_ROOT, QASTAGING_WEB_ROOT
1311
12from qabro.utils import snapdsocket
13
1414
15class BugReport:15class BugReport:
16 """Bug report.16 """Bug report.
@@ -247,8 +247,21 @@ class AttachmentAssistant:
247 print("No $PATH defined. Cannot run sosreport!")247 print("No $PATH defined. Cannot run sosreport!")
248248
249 def attach_snaplist(self):249 def attach_snaplist(self):
250 output = subprocess.check_output(['snap', 'list'])250 print("Retrieving list of installed snaps...")
251 self.attachments['snap_list.log'] = output251 sckt = snapdsocket()
252 snaps = sckt.get_snaps()
253 fmt = "{:<32}{:<16}{:<16}{:<16}{:<16}{:<16}"
254 out = fmt.format("Name", "Version", "Revision",
255 "Channel", "Devmode", "Confinement") + "\n"
256 for snap in snaps:
257 if snap["devmode"]:
258 devmode = "yes"
259 else:
260 devmode = "no"
261 out += fmt.format(snap["name"], snap["version"], snap["revision"],
262 snap.get("tracking-channel", "-"), devmode,
263 snap["confinement"]) + "\n"
264 self.attachments["snap_list.log"] = out.encode()
252265
253 def attach_last_checkbox_session(self):266 def attach_last_checkbox_session(self):
254 """Archive the most recent Plainbox session available and add it to267 """Archive the most recent Plainbox session available and add it to
@@ -264,10 +277,10 @@ class AttachmentAssistant:
264 if os.path.exists(classic_path):277 if os.path.exists(classic_path):
265 rootpath = classic_path278 rootpath = classic_path
266 else:279 else:
267 out = subprocess.check_output(["snap", "list"]).decode('utf-8')280 ss = snapdsocket()
268 snap_name_list = [e for e in out.split() if 'checkbox' in e]281 snap_list = [e for e in ss.get_snaps() if 'checkbox' in e['name']]
269 if snap_name_list:282 if snap_list:
270 snap_name = snap_name_list[0]283 snap_name = snap_list[0]["name"]
271 sp = "/home/$USER/snap/{}/current/plainbox/sessions"284 sp = "/home/$USER/snap/{}/current/plainbox/sessions"
272 snap_path = os.path.expandvars(sp.format(snap_name))285 snap_path = os.path.expandvars(sp.format(snap_name))
273 if os.path.exists(snap_path):286 if os.path.exists(snap_path):
@@ -326,13 +339,16 @@ class AttachmentAssistant:
326 standard_info['Image'] = subprocess.check_output(339 standard_info['Image'] = subprocess.check_output(
327 ['tail', '-n', '1', buildstamp]).decode(sys.stdout.encoding).strip()340 ['tail', '-n', '1', buildstamp]).decode(sys.stdout.encoding).strip()
328341
329 if shutil.which("ubuntu-report"):342 # This file hosts the OEM Manifest information we need
330 print("Retrieving Manifest information...")343 # See: https://forum.snapcraft.io/t/read-access-to-a-file-in-var-lib-on-the-host/11766
331 report = subprocess.check_output(["ubuntu-report", "show"])344 udc = '/var/lib/snapd/hostfs/var/lib/ubuntu_dist_channel'
332 reportjson = json.loads(report)345 manifest = ''
333 manifest = reportjson.get("OEM").get("DCD")346 if os.path.isfile(udc):
334 if manifest:347 with open(udc) as f:
335 standard_info["Manifest"] = manifest348 dcd = f.read()
349 manifest = re.search("^([^\s#]+)$", dcd, re.MULTILINE)[0]
350 if manifest:
351 standard_info["Manifest"] = manifest
336352
337 p = os.getenv('PATH')353 p = os.getenv('PATH')
338354
@@ -351,11 +367,12 @@ class AttachmentAssistant:
351367
352 standard_info['CPU'] = AttachmentAssistant.get_cpu_info()368 standard_info['CPU'] = AttachmentAssistant.get_cpu_info()
353369
354 lspci_output = (subprocess.check_output(['lspci'])370 lspci_output = (subprocess.check_output(['lspci', '-nn'])
355 .decode(sys.stdout.encoding)371 .decode(sys.stdout.encoding)
356 .splitlines())372 .splitlines())
373 # '03' is the PCI class for display controllers
357 standard_info['GPU'] = '\n'.join([line for line in lspci_output374 standard_info['GPU'] = '\n'.join([line for line in lspci_output
358 if 'VGA' in line])375 if '[03' in line])
359376
360 standard_info["kernel-version"] = (subprocess.check_output(["uname", "-r"])377 standard_info["kernel-version"] = (subprocess.check_output(["uname", "-r"])
361 .decode(sys.stdout.encoding)378 .decode(sys.stdout.encoding)
diff --git a/qabro/ui.py b/qabro/ui.py
index 26f6bba..9303cc9 100644
--- a/qabro/ui.py
+++ b/qabro/ui.py
@@ -56,7 +56,7 @@ class ReportScreen:
56 # Adding standard info (CPU, BIOS, GPU, etc.) to the description56 # Adding standard info (CPU, BIOS, GPU, etc.) to the description
57 std_info = AttachmentAssistant.get_standard_info()57 std_info = AttachmentAssistant.get_standard_info()
58 std_info_str = '\n'.join(['{}: {}'.format(elt, std_info[elt]) for elt in std_info])58 std_info_str = '\n'.join(['{}: {}'.format(elt, std_info[elt]) for elt in std_info])
59 self.default_description += "[Additional information]\n" + std_info_str59 self.default_description += "[Additional information]\nSKU: \n" + std_info_str
6060
61 self._title = urwid.LineBox(urwid.Edit(), 'Bug Title')61 self._title = urwid.LineBox(urwid.Edit(), 'Bug Title')
62 self._description = urwid.LineBox(urwid.Edit(edit_text=self.default_description,62 self._description = urwid.LineBox(urwid.Edit(edit_text=self.default_description,
diff --git a/qabro/utils.py b/qabro/utils.py
63new file mode 10064463new file mode 100644
index 0000000..af790eb
--- /dev/null
+++ b/qabro/utils.py
@@ -0,0 +1,67 @@
1#!/usr/bin/env python3
2import socket, json, pprint
3
4class snapdsocket:
5 def __init__(self, sock=None):
6 if sock is None:
7 self.sock = socket.socket(socket.AF_UNIX)
8 else:
9 self.sock = sock
10 self.sock.settimeout(0.3)
11
12 def _connect(self):
13 self.sock.connect("/run/snapd.socket")
14
15 def _send(self, msg):
16 if type(msg) != "bytes":
17 msg = msg.encode()
18 self.sock.send(msg)
19
20 def _recv(self):
21 chunks = []
22 while True:
23 try:
24 chunk = self.sock.recv(2048)
25 chunks.append(chunk.decode())
26 except socket.timeout:
27 break
28 return ''.join(chunks)
29
30 def _close(self):
31 self.sock.close()
32
33 def _get_json_response(self, response):
34 begin = response.find('{')
35 end = response.rfind('}') + 1
36 return json.loads(response[begin:end])
37
38 def request(self, msg):
39 self.__init__()
40 self._connect()
41 self._send(msg)
42 raw_resp = self._recv()
43 self._close()
44 json_resp = self._get_json_response(raw_resp)
45 return json_resp
46
47 def get_interfaces(self):
48 req = "GET /v2/interfaces HTTP/1.1\r\nHost: localhost\r\n\r\n"
49 json_resp = self.request(req)
50 return json_resp['result']
51
52 def get_snaps(self):
53 req = "GET /v2/snaps HTTP/1.1\r\nHost: localhost\r\n\r\n"
54 json_resp = self.request(req)
55 return json_resp['result']
56
57 def get_snap_names(self):
58 req = "GET /v2/snaps HTTP/1.1\r\nHost: localhost\r\n\r\n"
59 json_resp = self.request(req)
60 snap_names = []
61 for s in json_resp["result"]:
62 snap_names.append(s["name"])
63 return snap_names
64
65if __name__ == "__main__":
66 sn = snapdsocket()
67 pprint.pprint(sn.get_snaps())
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 0a2ec6c..2c9982d 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -4,6 +4,7 @@ adopt-info: qabro
44
5grade: stable # must be 'stable' to release into candidate/stable channels5grade: stable # must be 'stable' to release into candidate/stable channels
6confinement: strict # use 'strict' once you have the right plugs and slots6confinement: strict # use 'strict' once you have the right plugs and slots
7base: core18
78
8apps:9apps:
9 qabro:10 qabro:
@@ -112,12 +113,10 @@ parts:
112 '*': patches/113 '*': patches/
113 prime: [-*]114 prime: [-*]
114115
115# https://forum.snapcraft.io/t/snap-layouts/7207116layout:
116passthrough:117 /etc/sos.conf:
117 layout:118 bind-file: $SNAP/etc/sos.conf
118 /etc/sos.conf:119 /var/lib/usbutils/usb.ids:
119 bind-file: $SNAP/etc/sos.conf120 bind-file: $SNAP/var/lib/usbutils/usb.ids
120 /var/lib/usbutils/usb.ids:121 /usr/share/alsa:
121 bind-file: $SNAP/var/lib/usbutils/usb.ids122 bind: $SNAP/usr/share/alsa
122 /usr/share/alsa:
123 bind: $SNAP/usr/share/alsa

Subscribers

People subscribed via source and target branches

to all changes: