Merge lp:~rodsmith/hwcert-tools/show-hw into lp:~hardware-certification/hwcert-tools/reporting-tools

Proposed by Rod Smith
Status: Merged
Approved by: Jeff Lane 
Approved revision: 157
Merged at revision: 170
Proposed branch: lp:~rodsmith/hwcert-tools/show-hw
Merge into: lp:~hardware-certification/hwcert-tools/reporting-tools
Diff against target: 183 lines (+179/-0)
1 file modified
certification_reports/show_hw.py (+179/-0)
To merge this branch: bzr merge lp:~rodsmith/hwcert-tools/show-hw
Reviewer Review Type Date Requested Status
Jeff Lane  Approve
Review via email: mp+242385@code.launchpad.net

This proposal supersedes a proposal from 2014-10-28.

Description of the change

Added new show_hw script, which pulls down dmidecode data from C3. This script is a bit hacky, but is adequate for my immediate needs. Requesting merge in case it might be useful to others in the future, either as-is or as a base for further refinement.

Resubmission: Addressed Jeff's comment.

To post a comment you must log in.
Revision history for this message
Jeff Lane  (bladernr) wrote : Posted in a previous version of this proposal

Other than one minor nitpick that really is a semantics issue, I'm fine iwth this. If you don't want to change the shebang line, that's fine too, just say so and I'll approve.

review: Needs Information
Revision history for this message
Jeff Lane  (bladernr) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'certification_reports/show_hw.py'
2--- certification_reports/show_hw.py 1970-01-01 00:00:00 +0000
3+++ certification_reports/show_hw.py 2014-11-20 17:04:44 +0000
4@@ -0,0 +1,179 @@
5+#!/usr/bin/python2
6+#
7+# Script to extract dmidecode data from C3. Unfortunately, security issues
8+# require using both a C3 API key AND SSO credentials. The former are entered
9+# on the command line and the script prompts for the latter. Results are
10+# stored in a sequence of files, called dmidecode.#.{CID}.txt, where "#"
11+# is the sequence number in which the dmidecode data was retrieved and
12+# "{CID}" is the machine's CID number. Note that multiple dmidecode files
13+# may be retrieved for some computers; I believe this depends on how many
14+# were stored in C3.
15+#
16+# This script uses code provided to me by Daniel Manrique and written by
17+# somebody else, whose name I don't recall.
18+#
19+# This script cannot retrieve dmidecode data by CID value, and some machines
20+# which have stored data are NOT being retrieved. This is clearly a bug.
21+#
22+# The "maximum number of files to be retrieved" value is approximate; the
23+# script may return one or two more or less than this.
24+#
25+# Required parameters:
26+# - C3 username
27+# - C3 API key (obtained from https://certification.canonical.com/me/)
28+# - Maximum number of files to be retrieved (a run during development
29+# produced 7759 files, so specifying at least 10000 is advisable,
30+# unless you want just a few)
31+#
32+# By Rod Smith, 2014-10-23
33+
34+from api_utils import APIQuery, QueryError
35+from argparse import ArgumentParser
36+
37+import os
38+import sys
39+import getpass
40+import mechanize
41+
42+C3_URL = "https://certification.canonical.com"
43+
44+api = APIQuery(C3_URL)
45+
46+
47+def get_browser():
48+ """
49+ Return a mechanize.Browser configured appropriately.
50+ """
51+ browser = mechanize.Browser()
52+ browser.set_handle_robots(False)
53+ return browser
54+
55+
56+def prompt_for_credentials():
57+ """
58+ Return username and password collected from stdin.
59+ """
60+ print >> sys.stderr, "\rUbuntu SSO username (Canonical e-mail): ",
61+ username = raw_input()
62+ password = getpass.getpass()
63+ return username, password
64+
65+
66+def prompt_for_code():
67+ """
68+ Return code collected from stdin.
69+ """
70+ print >> sys.stderr, "\r2-factor authentication code: ",
71+ return raw_input()
72+
73+
74+def get_session_cookie(browser=get_browser()):
75+ """
76+ Fetch initial url, login with the supplied credentials, and return the
77+ session id for the authenticated session.
78+ """
79+ bad_creds = False
80+ bad_code = False
81+
82+ print >> sys.stderr, "Authenticating with Ubuntu SSO..."
83+ browser.open("https://certification.canonical.com/openid/login")
84+ while True:
85+ # There are four possibilities
86+ # 1- I'm already authenticated, so bapireak out and fetch the
87+ # delicious cookie.
88+ if "currently logged in " in browser.response().read():
89+ break
90+ # 2- I'm in an openid "Continue" page. I can tell because
91+ # I have a "Continue" button.
92+ browser.select_form(nr=0)
93+ try:
94+ browser.form.find_control(predicate=lambda c: 'value' in
95+ getattr(c, 'attrs', '') and
96+ c.attrs['value'] == 'Continue')
97+ browser.submit()
98+ except mechanize.ControlNotFoundError:
99+ #3- I'm in a page asking for login/password *or* auth token.
100+ try:
101+ browser.form.find_control("oath_token")
102+ except mechanize.ControlNotFoundError:
103+ if bad_creds:
104+ print >> sys.stderr, "\rBad credentials"
105+ bad_creds = True
106+ username, password = prompt_for_credentials()
107+ browser["email"] = username
108+ browser["password"] = password
109+ browser.submit()
110+ else:
111+ if bad_code:
112+ print >> sys.stderr, "\rBad code"
113+ bad_code = True
114+ code = prompt_for_code()
115+ browser["oath_token"] = code
116+ browser.submit()
117+ # Get session id from cookiejar
118+ cookiejar = browser._ua_handlers["_cookies"].cookiejar
119+ oemshare_cookies = cookiejar._cookies["certification.canonical.com"]
120+ return oemshare_cookies["/"]["sessionid"].value, browser
121+
122+
123+def extract_cid(path):
124+ head = path
125+ for i in range(6):
126+ head, tail = os.path.split(head)
127+ return tail
128+
129+
130+def get_dmi_data(username, api_key, max_records):
131+ cookie, browser = get_session_cookie()
132+ request_params = {"username": username,
133+ "api_key": api_key,
134+ "limit": 20,
135+ "offset": 0}
136+ keep_going = True
137+ i = 1
138+
139+ while keep_going:
140+ try:
141+ results = api.single_query(C3_URL + '/api/v1/attachment/',
142+ params=request_params)
143+ except QueryError:
144+ print("Failed to retrieve metadata!")
145+ sys.exit(1)
146+
147+ for k, v in results.iteritems():
148+ if k == 'meta':
149+ total_count = v['total_count']
150+ if k == 'objects':
151+ for att in v:
152+ if att['name'] == 'dmidecode_attachment':
153+ try:
154+ cid = extract_cid(att['url'])
155+ dmidec = browser.open(C3_URL + att['url'])
156+ filename = "dmidecode." + str(i) + "." + str(cid) + ".txt"
157+ print("Saving to " + filename)
158+ f = open(filename, "w")
159+ f.write(dmidec.read())
160+ f.close()
161+ i = i + 1
162+ except:
163+ print("Could not retrieve data")
164+
165+ if i >= max_records or request_params['offset'] > total_count:
166+ keep_going = False
167+ request_params['offset'] = request_params['offset'] + request_params['limit']
168+
169+
170+def main():
171+ parser = ArgumentParser("Extract dmidecode data from C3.")
172+ parser.add_argument('username',
173+ help="Launchpad username used to access C3.")
174+ parser.add_argument('api_key',
175+ help="API key used to access C3.")
176+ parser.add_argument('max_records',
177+ help="Limit on number of attachments to save")
178+ args = parser.parse_args()
179+
180+ get_dmi_data(args.username, args.api_key, int(args.max_records))
181+
182+if __name__ == "__main__":
183+ sys.exit(main())

Subscribers

People subscribed via source and target branches