Merge lp:~rodsmith/hwcert-tools/get-ytd into lp:~hardware-certification/hwcert-tools/reporting-tools
- get-ytd
- Merge into reporting-tools
Status: | Superseded |
---|---|
Proposed branch: | lp:~rodsmith/hwcert-tools/get-ytd |
Merge into: | lp:~hardware-certification/hwcert-tools/reporting-tools |
Diff against target: |
141 lines (+137/-0) 1 file modified
certification_reports/get_ytd_makes.py (+137/-0) |
To merge this branch: | bzr merge lp:~rodsmith/hwcert-tools/get-ytd |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Zygmunt Krynicki (community) | Needs Fixing | ||
Jeff Lane | Pending | ||
Review via email:
|
This proposal has been superseded by a proposal from 2015-03-18.
Commit message
Description of the change
New script to generate YTD reports on server certificates issued.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Zygmunt Krynicki (zyga) wrote : | # |
Also, flake8 the script. I'm sure I've missed something. Also feel
free to pip install flake8-docstrings and re-flake the script.
On Wed, Mar 18, 2015 at 5:09 PM, Zygmunt Krynicki
<email address hidden> wrote:
> Review: Needs Fixing
>
> Hey.
>
> Have a look at my comments below
>
> Diff comments:
>
>> === added file 'certification_
>> --- certification_
>> +++ certification_
>> @@ -0,0 +1,131 @@
>> +#!/usr/bin/python2
>
> for all python2 code please do at a bare minimum:
>
> from __future__ import print_function, absolute_import
>
>> +'''
>> +Script to summarize make and model certification for the year to date
>
> Please move this to the description= keyword argument of the argparse.
>
>> +or for a specified year. Produces a list of makes, the number of
>> +certificates issued for each make, and a list of the models certified
>> +for that make.
>> +
>> +Options:
>> +- C3 username
>> +- C3 API key (from https:/
>> +- Ubuntu release (e.g., "precise" or "trusty")
>> +- Year (e.g., "2015")
>> +- --certnum={value} -- Certificate number for a report on one machine or
>> + "all" for all certified systems. Defaults to "all". Used for debugging,
>> + since retrieving all certificates takes ~10 minutes.
>> +
>> +Copyright (c) 2015 Canonical Ltd.
>
> please move the copyright to a comment at the top of the file
>
>> +
>> +Author: Rod Smith <email address hidden>
>> +'''
>> +
>> +from api_utils import APIQuery, QueryError
>
> Please put imports in this order: stdlib, 3rd party, this app/library. Put a newline between each group
>
>> +from argparse import ArgumentParser
>> +
>> +import sys
>> +import time
>> +
>> +C3_URL = "https:/
>> +CERTIFICATE_API = C3_URL + "/api/v1/
>> +
>> +SERVER_FF = ['Expansion Chassis',
>> + 'Main Server Chassis',
>> + 'Multi-system',
>> + 'Blade',
>> + 'Rack Mount Chassis',
>> + 'Server']
>> +
>> +api = APIQuery(C3_URL)
>> +
>> +def get_server_
>> + '''
>> + Retrieve certificate information from C3
>
> Please follow PEP for docstrings
>
> Retrieve certificate information from C3.
>
> :param username:
> Bla bla bla
> :returns:
> Dictionary with ...
>
>> + Return value: Dictionary with manufacturer names as keys and
>> + list of models as values
>> + '''
>> + request_url = CERTIFICATE_API
>> + summaries = {}
>> +
>> + if certnum == "all":
>> + request_params = {"username": username,
>> + "api_key": api_key}
>> + else:
>> + request_params = {"username": username,
>> + "api_key": api_key,
>> + "name": certnum}
>> +
>> + certs = []
>> + try:
>> + certs = api.batch_
>> + except QueryError:
>> + print "Unable to get certificates"
>
> you can raise SystemExit("Unable to gett...."), this ...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Rod Smith (rodsmith) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Zygmunt Krynicki (zyga) wrote : | # |
On Wed, Mar 18, 2015 at 5:20 PM, Roderick Smith <email address hidden> wrote:
>
>
> Diff comments:
>
>> === added file 'certification_
>> --- certification_
>> +++ certification_
>> @@ -0,0 +1,131 @@
>> +#!/usr/bin/python2
>> +'''
>> +Script to summarize make and model certification for the year to date
>
> It's unclear to me how much of this you want moved. Also: MOVED or COPIED?
Moved, just keep a one-line summary at the top. The idea is to see
useful information when running get_ytd_makes.py --help.
Thanks
ZK
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jeff Lane (bladernr) wrote : | # |
The latest changes to the output look good to me. I'll ack once Zyga is satisfied.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Zygmunt Krynicki (zyga) wrote : | # |
I'll do my best to help to resolve code style issues
On Wed, Mar 18, 2015 at 6:41 PM, Jeff Lane <email address hidden> wrote:
> The latest changes to the output look good to me. I'll ack once Zyga is satisfied.
> --
> https:/
> You are reviewing the proposed merge of lp:~rodsmith/hwcert-tools/get-ytd into lp:~hardware-certification/hwcert-tools/reporting-tools.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Rod Smith (rodsmith) wrote : | # |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On 03/18/2015 12:09 PM, Zygmunt Krynicki wrote:
> Review: Needs Fixing
>
> Hey.
>
> Have a look at my comments below
I've changed most of these things. A few further comments and queries,
though....
>> +from api_utils import APIQuery, QueryError
>
> Please put imports in this order: stdlib, 3rd party, this
> app/library. Put a newline between each group
I think I've got this, but I'm really guessing at what's what. Is this
documented somewhere?
>> + num_of_make += 1 + grand_total += 1 +
>> print make.encode(
>
> This is a code smell, don't do this.
>
> Either move to all unicode strings internally or use all bytes and
> hope for the best. Why make needs this treatment now? I assume you
> get an unicode string from the API, correct?
The problem isn't with internal string handling; it's with what
happens when running the script with redirection when
".encode('utf-8')" is dropped:
$ ./get_ytd_makes.py {blah blah blah} > foo.txt
Traceback (most recent call last):
File "./get_
sys.
File "./get_
print (make + ": " + str(num_of_make))
UnicodeEncodeError: 'ascii' codec can't encode characters in position
17-24: ordinal not in range(128)
It works fine when sending output to the console (or at least, an X
terminal), but when redirecting, it runs into this crash when a name
includes non-ASCII characters. (The trouble point for us is NEC, which
is encoded as "NEC Corporation (日本電気株式会社)"). I've Googled this
to death and using ".encode('utf-8')" is the only solution I've found
that works. (I've tried several ways of explicitly marking strings as
Unicode, and they all fail in one way or another.) If you have another
solution, I'm quite willing to try it.
- --
Rod Smith
Server and Cloud Certification Engineer
<email address hidden>
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQEcBAEBAgAGBQJ
jkvUeZjI3BPwKHI
JT1Hvl8yUllOLjy
KZVw2Uxi36bZXBi
qE7W3Ho3zkc17Li
60dWgE0Agnc+
=LSKD
-----END PGP SIGNATURE-----
Unmerged revisions
Preview Diff
1 | === added file 'certification_reports/get_ytd_makes.py' |
2 | --- certification_reports/get_ytd_makes.py 1970-01-01 00:00:00 +0000 |
3 | +++ certification_reports/get_ytd_makes.py 2015-03-18 19:49:16 +0000 |
4 | @@ -0,0 +1,137 @@ |
5 | +#!/usr/bin/python2 |
6 | +""" |
7 | +Script to summarize make and model certification for the year to date |
8 | +or for a specified year. |
9 | + |
10 | +Copyright (c) 2015 Canonical Ltd. |
11 | + |
12 | +Author: Rod Smith <rod.smith@canonical.com> |
13 | +""" |
14 | + |
15 | +from __future__ import print_function, absolute_import |
16 | + |
17 | +import sys |
18 | +import time |
19 | + |
20 | +from api_utils import APIQuery, QueryError |
21 | +from argparse import ArgumentParser |
22 | + |
23 | +C3_URL = "https://certification.canonical.com" |
24 | +CERTIFICATE_API = C3_URL + "/api/v1/certificates" |
25 | + |
26 | +SERVER_FF = ['Expansion Chassis', |
27 | + 'Main Server Chassis', |
28 | + 'Multi-system', |
29 | + 'Blade', |
30 | + 'Rack Mount Chassis', |
31 | + 'Server'] |
32 | + |
33 | +api = APIQuery(C3_URL) |
34 | + |
35 | + |
36 | +def get_server_certs(username, api_key, release, year, certnum): |
37 | + """ Retrieve certificate information from C3 |
38 | + :param username: |
39 | + C3 username |
40 | + :param api_key: |
41 | + C3 API key, obtainable from https://certification.canonical.com/me/ |
42 | + :param release: |
43 | + Ubuntu release codename (e.g., "precise", "trusty") |
44 | + :param year: |
45 | + Year for which the report is desired |
46 | + :param certnum: |
47 | + Certificate number (e.g., "1502-7107" or "all" for all certificates) |
48 | + :returns: |
49 | + Dictionary with manufacturer names as keys and list of models as values |
50 | + """ |
51 | + request_url = CERTIFICATE_API |
52 | + summaries = {} |
53 | + |
54 | + if certnum == "all": |
55 | + request_params = {"username": username, |
56 | + "api_key": api_key} |
57 | + else: |
58 | + request_params = {"username": username, |
59 | + "api_key": api_key, |
60 | + "name": certnum} |
61 | + |
62 | + certs = [] |
63 | + try: |
64 | + certs = api.batch_query(request_url, params=request_params) |
65 | + except QueryError: |
66 | + raise SystemExit("Unable to get certificates") |
67 | + certs = [cert for cert in certs if cert['name']] |
68 | + |
69 | + for cert in certs: |
70 | + if cert['created_at'][0:4] == year: |
71 | + machine = api.single_query(C3_URL + cert['machine'], |
72 | + params=request_params) |
73 | + if (machine['platform']['form_factor'] in SERVER_FF and |
74 | + cert['release']['codename'] == release): |
75 | + make = machine['account']['name'] |
76 | + details = {'model': machine['platform']['name'], |
77 | + 'certnum': cert['name'], |
78 | + 'cid': machine['canonical_id'].encode('utf-8')} |
79 | + try: |
80 | + summaries[make].append(details) |
81 | + except LookupError: |
82 | + summaries[make] = [details] |
83 | + |
84 | + return summaries |
85 | + |
86 | + |
87 | +def main(): |
88 | + parser = ArgumentParser( |
89 | + description="Script to summarize make and model certificates " + |
90 | + "issued for the year to date or for a specified year. Produces " + |
91 | + "a list of makes, the number of certificates issued for each" + |
92 | + "make, and a list of the models certified for that make.") |
93 | + parser.add_argument( |
94 | + "username", help="Launchpad username used to access C3.") |
95 | + parser.add_argument( |
96 | + "api_key", help="API key used to access C3, from " + |
97 | + "https://certification.canonical.com/me/") |
98 | + parser.add_argument( |
99 | + "release", help="Ubuntu release ('trusty', 'precise', etc.)") |
100 | + parser.add_argument("year", help="The year to summarize") |
101 | + parser.add_argument( |
102 | + "--certnum", help="Certificate number or 'all' (used for debugging)", |
103 | + default="all") |
104 | + args = parser.parse_args() |
105 | + |
106 | + summaries = get_server_certs(args.username, args.api_key, args.release, |
107 | + args.year, args.certnum) |
108 | + |
109 | + print ("YTD server certificates for {0} in {1}".format(args.release, |
110 | + args.year)) |
111 | + print ("\nReport generated: {0}\n".format(time.strftime("%c"))) |
112 | + |
113 | + # First pass: Generate "executive summary" |
114 | + print ("Summary:") |
115 | + print ("--------\n") |
116 | + grand_total = 0 |
117 | + sorted_make = sorted(summaries) |
118 | + for make in sorted_make: |
119 | + num_of_make = 0 |
120 | + for model in sorted(summaries[make]): |
121 | + num_of_make += 1 |
122 | + grand_total += 1 |
123 | + print (make.encode('utf-8') + ": " + str(num_of_make)) |
124 | + print ("\nGrand total: ", grand_total) |
125 | + |
126 | + # Second pass: Generate detailed report |
127 | + print ("\n\nDetailed information:") |
128 | + print ("---------------------\n") |
129 | + for make in sorted_make: |
130 | + print (make.encode('utf-8') + ":") |
131 | + num_of_make = 0 |
132 | + for model in sorted(summaries[make]): |
133 | + num_of_make += 1 |
134 | + print (" * {0} (CID {1}):".format(model['model'], model['cid'])) |
135 | + print (" https://certification.canonical.com/certificates/{0}". |
136 | + format(model['certnum'])) |
137 | + print (make.encode('utf-8'), " TOTAL: ", num_of_make, "\n") |
138 | + |
139 | + |
140 | +if __name__ == "__main__": |
141 | + sys.exit(main()) |
Hey.
Have a look at my comments below