Merge lp:~didrocks/ubuntu-release-upgrader/add_telemetry into lp:ubuntu-release-upgrader
- add_telemetry
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Didier Roche-Tolomelli |
Approved revision: | 3135 |
Merged at revision: | 3131 |
Proposed branch: | lp:~didrocks/ubuntu-release-upgrader/add_telemetry |
Merge into: | lp:ubuntu-release-upgrader |
Diff against target: |
549 lines (+176/-55) 8 files modified
DistUpgrade/DistUpgradeController.py (+16/-20) DistUpgrade/DistUpgradeView.py (+11/-7) DistUpgrade/DistUpgradeViewGtk3.py (+20/-16) DistUpgrade/DistUpgradeViewKDE.py (+14/-10) DistUpgrade/DistUpgradeViewNonInteractive.py (+3/-0) DistUpgrade/DistUpgradeViewText.py (+5/-2) DistUpgrade/telemetry.py (+101/-0) debian/changelog (+6/-0) |
To merge this branch: | bzr merge lp:~didrocks/ubuntu-release-upgrader/add_telemetry |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Didier Roche-Tolomelli | Approve | ||
Brian Murray | Approve | ||
Review via email: mp+345088@code.launchpad.net |
Commit message
Add upgrade telemetry data for later collect in ubuntu-report.
Description of the change
Add upgrade telemetry data for later collect in ubuntu-report.
This was tested with 17.04 -> 18.04 upgrade, in text mode, GTK3 and KDE.
Didier Roche-Tolomelli (didrocks) wrote : | # |
Thanks for the review!
There isn't a fixed database structure schema. However, keep in mind that ubuntu-report gather this results (as well as installers one) under some subfields on the json, alongside collecting a lot of others information.
So basically, we have the "To" and a lot of other details. Moreinfo on https:/
For now, we collect those details, similarly than for ubiquity, and we'll organize them next cycle if nobody on IS step up first.
Typos fixed and pushed. Thanks!
- 3132. By Didier Roche-Tolomelli
-
Fix some typos
- 3133. By Didier Roche-Tolomelli
-
Use uptime time instead of time.time(), which is sensitive to system
clock reset by NTP or others… - 3134. By Didier Roche-Tolomelli
-
Fetch if users enabled third party repos before upgrade
- 3135. By Didier Roche-Tolomelli
-
Fetch original install media
Brian Murray (brian-murray) wrote : | # |
This looks good to me to merge and upload after the one change is made.
While this may be more useful for the failure case you might consider including what components (-updates, -security, main, universe, etc...) are enabled on the system that was upgraded. Here's an example from an upgrade log file.
2018-05-12 20:32:36,997 DEBUG found components: {'bionic-updates': {'main', 'universe', 'multiverse', 'restricted'}, 'bionic-security': {'main', 'universe', 'multiverse', 'restricted'}, 'bionic': {'main', 'universe', 'multiverse', 'restricted'}}
Didier Roche-Tolomelli (didrocks) wrote : | # |
We don't want to include the enabled components for now, but thanks for the suggestion! I'll fix the current uptime comment.
Thanks again for the review :)
Brian Murray (brian-murray) : | # |
Didier Roche-Tolomelli (didrocks) wrote : | # |
Tested artful -> bionic upgrade with this code and it reports the expected telemetry data.
Preview Diff
1 | === modified file 'DistUpgrade/DistUpgradeController.py' |
2 | --- DistUpgrade/DistUpgradeController.py 2018-03-16 17:56:15 +0000 |
3 | +++ DistUpgrade/DistUpgradeController.py 2018-05-18 10:01:29 +0000 |
4 | @@ -33,6 +33,7 @@ |
5 | import copy |
6 | from configparser import NoOptionError |
7 | from configparser import ConfigParser as SafeConfigParser |
8 | +from .telemetry import get as get_telemetry |
9 | from .utils import (country_mirror, |
10 | url_downloadable, |
11 | check_and_fix_xbit, |
12 | @@ -45,14 +46,7 @@ |
13 | from string import Template |
14 | from urllib.parse import urlsplit |
15 | |
16 | -from .DistUpgradeView import ( |
17 | - STEP_PREPARE, |
18 | - STEP_MODIFY_SOURCES, |
19 | - STEP_FETCH, |
20 | - STEP_INSTALL, |
21 | - STEP_CLEANUP, |
22 | - STEP_REBOOT, |
23 | -) |
24 | +from .DistUpgradeView import Step |
25 | from .DistUpgradeCache import MyCache |
26 | from .DistUpgradeConfigParser import DistUpgradeConfig |
27 | from .DistUpgradeQuirks import DistUpgradeQuirks |
28 | @@ -866,6 +860,7 @@ |
29 | "'software-properties' tool or " |
30 | "your package manager." |
31 | )) |
32 | + get_telemetry().set_using_third_party_sources(self.sources_disabled) |
33 | return True |
34 | |
35 | def _logChanges(self): |
36 | @@ -1760,7 +1755,7 @@ |
37 | def fullUpgrade(self): |
38 | # sanity check (check for ubuntu-desktop, brokenCache etc) |
39 | self._view.updateStatus(_("Checking package manager")) |
40 | - self._view.setStep(STEP_PREPARE) |
41 | + self._view.setStep(Step.PREPARE) |
42 | |
43 | if not self.prepare(): |
44 | logging.error("self.prepare() failed") |
45 | @@ -1829,7 +1824,7 @@ |
46 | self.abort() |
47 | |
48 | # update sources.list |
49 | - self._view.setStep(STEP_MODIFY_SOURCES) |
50 | + self._view.setStep(Step.MODIFY_SOURCES) |
51 | self._view.updateStatus(_("Updating repository information")) |
52 | if not self.updateSourcesList(): |
53 | self.abort() |
54 | @@ -1901,14 +1896,14 @@ |
55 | self.abort() |
56 | |
57 | # fetch the stuff |
58 | - self._view.setStep(STEP_FETCH) |
59 | + self._view.setStep(Step.FETCH) |
60 | self._view.updateStatus(_("Fetching")) |
61 | if not self.doDistUpgradeFetching(): |
62 | self._enableAptCronJob() |
63 | self.abort() |
64 | |
65 | # now do the upgrade |
66 | - self._view.setStep(STEP_INSTALL) |
67 | + self._view.setStep(Step.INSTALL) |
68 | self._view.updateStatus(_("Upgrading")) |
69 | if not self.doDistUpgrade(): |
70 | # run the post install scripts (for stuff like UUID conversion) |
71 | @@ -1922,7 +1917,7 @@ |
72 | sys.exit(1) |
73 | |
74 | # do post-upgrade stuff |
75 | - self._view.setStep(STEP_CLEANUP) |
76 | + self._view.setStep(Step.CLEANUP) |
77 | self._view.updateStatus(_("Searching for obsolete software")) |
78 | self.doPostUpgrade() |
79 | |
80 | @@ -1935,8 +1930,9 @@ |
81 | os.unlink("/var/lib/ubuntu-release-upgrader/release-upgrade-available") |
82 | |
83 | # done, ask for reboot |
84 | - self._view.setStep(STEP_REBOOT) |
85 | + self._view.setStep(Step.REBOOT) |
86 | self._view.updateStatus(_("System upgrade is complete.")) |
87 | + get_telemetry().done() |
88 | # FIXME should we look into /var/run/reboot-required here? |
89 | if (not inside_chroot() and |
90 | self._view.confirmRestart()): |
91 | @@ -1950,20 +1946,20 @@ |
92 | |
93 | def doPartialUpgrade(self): |
94 | " partial upgrade mode, useful for repairing " |
95 | - self._view.setStep(STEP_PREPARE) |
96 | - self._view.hideStep(STEP_MODIFY_SOURCES) |
97 | - self._view.hideStep(STEP_REBOOT) |
98 | + self._view.setStep(Step.PREPARE) |
99 | + self._view.hideStep(Step.MODIFY_SOURCES) |
100 | + self._view.hideStep(Step.REBOOT) |
101 | self._partialUpgrade = True |
102 | self.prepare() |
103 | if not self.doPostInitialUpdate(): |
104 | return False |
105 | if not self.askDistUpgrade(): |
106 | return False |
107 | - self._view.setStep(STEP_FETCH) |
108 | + self._view.setStep(Step.FETCH) |
109 | self._view.updateStatus(_("Fetching")) |
110 | if not self.doDistUpgradeFetching(): |
111 | return False |
112 | - self._view.setStep(STEP_INSTALL) |
113 | + self._view.setStep(Step.INSTALL) |
114 | self._view.updateStatus(_("Upgrading")) |
115 | if not self.doDistUpgrade(): |
116 | self._view.information(_("Upgrade complete"), |
117 | @@ -1971,7 +1967,7 @@ |
118 | "were errors during the upgrade " |
119 | "process.")) |
120 | return False |
121 | - self._view.setStep(STEP_CLEANUP) |
122 | + self._view.setStep(Step.CLEANUP) |
123 | if not self.doPostUpgrade(): |
124 | self._view.information(_("Upgrade complete"), |
125 | _("The upgrade has completed but there " |
126 | |
127 | === modified file 'DistUpgrade/DistUpgradeView.py' |
128 | --- DistUpgrade/DistUpgradeView.py 2017-05-18 17:33:48 +0000 |
129 | +++ DistUpgrade/DistUpgradeView.py 2018-05-18 10:01:29 +0000 |
130 | @@ -21,7 +21,9 @@ |
131 | |
132 | from .DistUpgradeGettext import gettext as _ |
133 | from .DistUpgradeGettext import ngettext |
134 | +from .telemetry import get as get_telemetry |
135 | import apt |
136 | +from enum import Enum |
137 | import errno |
138 | import os |
139 | import apt_pkg |
140 | @@ -252,13 +254,14 @@ |
141 | def hide(self): |
142 | pass |
143 | |
144 | -(STEP_PREPARE, |
145 | - STEP_MODIFY_SOURCES, |
146 | - STEP_FETCH, |
147 | - STEP_INSTALL, |
148 | - STEP_CLEANUP, |
149 | - STEP_REBOOT, |
150 | - STEP_N) = range(1,8) |
151 | +class Step(Enum): |
152 | + PREPARE = 1 |
153 | + MODIFY_SOURCES = 2 |
154 | + FETCH = 3 |
155 | + INSTALL = 4 |
156 | + CLEANUP = 5 |
157 | + REBOOT = 6 |
158 | + N = 7 |
159 | |
160 | # Declare these translatable strings from the .ui files here so that |
161 | # xgettext picks them up. |
162 | @@ -304,6 +307,7 @@ |
163 | 4. Post upgrade stuff |
164 | 5. Complete |
165 | """ |
166 | + get_telemetry().add_stage(step.name) |
167 | pass |
168 | def hideStep(self, step): |
169 | " hide a certain step from the GUI " |
170 | |
171 | === modified file 'DistUpgrade/DistUpgradeViewGtk3.py' |
172 | --- DistUpgrade/DistUpgradeViewGtk3.py 2016-02-23 17:06:42 +0000 |
173 | +++ DistUpgrade/DistUpgradeViewGtk3.py 2018-05-18 10:01:29 +0000 |
174 | @@ -52,6 +52,7 @@ |
175 | from .DistUpgradeApport import run_apport, apport_crash |
176 | |
177 | from .DistUpgradeView import DistUpgradeView, FuzzyTimeToStr, InstallProgress, AcquireProgress |
178 | +from .telemetry import get as get_telemetry |
179 | from .SimpleGtk3builderApp import SimpleGtkbuilderApp |
180 | |
181 | import gettext |
182 | @@ -70,7 +71,7 @@ |
183 | """ update is called regularly so that the gui can be redrawn """ |
184 | if text: |
185 | self.status.set_text(text) |
186 | - self.progress.set_fraction(step/float(self.totalSteps)) |
187 | + self.progress.set_fraction(step.value/float(self.totalSteps)) |
188 | while Gtk.events_pending(): |
189 | Gtk.main_iteration() |
190 | def ask_cdrom_name(self): |
191 | @@ -452,6 +453,8 @@ |
192 | # check if we have a display etc |
193 | Gtk.init_check(sys.argv) |
194 | |
195 | + get_telemetry().set_updater_type('GTK') |
196 | + |
197 | try: |
198 | locale.bindtextdomain("ubuntu-release-upgrader",localedir) |
199 | gettext.textdomain("ubuntu-release-upgrader") |
200 | @@ -472,7 +475,7 @@ |
201 | # terminal stuff |
202 | self.create_terminal() |
203 | |
204 | - self.prev_step = 0 # keep a record of the latest step |
205 | + self.prev_step = None # keep a record of the latest step |
206 | # we don't use this currently |
207 | #self.window_main.set_keep_above(True) |
208 | self.icontheme = Gtk.IconTheme.get_default() |
209 | @@ -598,44 +601,45 @@ |
210 | def updateStatus(self, msg): |
211 | self.label_status.set_text("%s" % msg) |
212 | def hideStep(self, step): |
213 | - image = getattr(self,"image_step%i" % step) |
214 | - label = getattr(self,"label_step%i" % step) |
215 | - #arrow = getattr(self,"arrow_step%i" % step) |
216 | + image = getattr(self,"image_step%i" % step.value) |
217 | + label = getattr(self,"label_step%i" % step.value) |
218 | + #arrow = getattr(self,"arrow_step%i" % step.value) |
219 | image.hide() |
220 | label.hide() |
221 | def showStep(self, step): |
222 | - image = getattr(self,"image_step%i" % step) |
223 | - label = getattr(self,"label_step%i" % step) |
224 | + image = getattr(self,"image_step%i" % step.value) |
225 | + label = getattr(self,"label_step%i" % step.value) |
226 | image.show() |
227 | label.show() |
228 | def abort(self): |
229 | size = Gtk.IconSize.MENU |
230 | step = self.prev_step |
231 | - if step > 0: |
232 | - image = getattr(self,"image_step%i" % step) |
233 | - arrow = getattr(self,"arrow_step%i" % step) |
234 | + if step: |
235 | + image = getattr(self,"image_step%i" % step.value) |
236 | + arrow = getattr(self,"arrow_step%i" % step.value) |
237 | image.set_from_stock(Gtk.STOCK_CANCEL, size) |
238 | image.show() |
239 | arrow.hide() |
240 | def setStep(self, step): |
241 | + super(DistUpgradeViewGtk3, self).setStep(step) |
242 | if self.icontheme.rescan_if_needed(): |
243 | logging.debug("icon theme changed, re-reading") |
244 | # first update the "previous" step as completed |
245 | size = Gtk.IconSize.MENU |
246 | attrlist=Pango.AttrList() |
247 | if self.prev_step: |
248 | - image = getattr(self,"image_step%i" % self.prev_step) |
249 | - label = getattr(self,"label_step%i" % self.prev_step) |
250 | - arrow = getattr(self,"arrow_step%i" % self.prev_step) |
251 | + image = getattr(self,"image_step%i" % self.prev_step.value) |
252 | + label = getattr(self,"label_step%i" % self.prev_step.value) |
253 | + arrow = getattr(self,"arrow_step%i" % self.prev_step.value) |
254 | label.set_property("attributes",attrlist) |
255 | image.set_from_stock(Gtk.STOCK_APPLY, size) |
256 | image.show() |
257 | arrow.hide() |
258 | self.prev_step = step |
259 | # show the an arrow for the current step and make the label bold |
260 | - image = getattr(self,"image_step%i" % step) |
261 | - label = getattr(self,"label_step%i" % step) |
262 | - arrow = getattr(self,"arrow_step%i" % step) |
263 | + image = getattr(self,"image_step%i" % step.value) |
264 | + label = getattr(self,"label_step%i" % step.value) |
265 | + arrow = getattr(self,"arrow_step%i" % step.value) |
266 | # check if that step was not hidden with hideStep() |
267 | if not label.get_property("visible"): |
268 | return |
269 | |
270 | === modified file 'DistUpgrade/DistUpgradeViewKDE.py' |
271 | --- DistUpgrade/DistUpgradeViewKDE.py 2018-01-22 11:12:39 +0000 |
272 | +++ DistUpgrade/DistUpgradeViewKDE.py 2018-05-18 10:01:29 +0000 |
273 | @@ -62,6 +62,7 @@ |
274 | from .DistUpgradeApport import run_apport, apport_crash |
275 | |
276 | from .DistUpgradeView import DistUpgradeView, FuzzyTimeToStr, InstallProgress, AcquireProgress |
277 | +from .telemetry import get as get_telemetry |
278 | |
279 | import select |
280 | import gettext |
281 | @@ -251,7 +252,7 @@ |
282 | """ update is called regularly so that the gui can be redrawn """ |
283 | if text: |
284 | self.status.setText(text) |
285 | - self.progressbar.setValue(step/float(self.totalSteps)) |
286 | + self.progressbar.setValue(step.value/float(self.totalSteps)) |
287 | QApplication.processEvents() |
288 | |
289 | def ask_cdrom_name(self): |
290 | @@ -560,6 +561,8 @@ |
291 | """KDE frontend of the distUpgrade tool""" |
292 | def __init__(self, datadir=None, logdir=None): |
293 | DistUpgradeView.__init__(self) |
294 | + |
295 | + get_telemetry().set_updater_type('KDE') |
296 | # silence the PyQt4 logger |
297 | logger = logging.getLogger("PyQt4") |
298 | logger.setLevel(logging.INFO) |
299 | @@ -613,7 +616,7 @@ |
300 | self.window_main.setParent(self) |
301 | self.window_main.show() |
302 | |
303 | - self.prev_step = 0 # keep a record of the latest step |
304 | + self.prev_step = None # keep a record of the latest step |
305 | |
306 | self._opCacheProgress = KDEOpProgress(self.window_main.progressbar_cache, self.window_main.progress_text) |
307 | self._acquireProgress = KDEAcquireProgressAdapter(self) |
308 | @@ -733,15 +736,15 @@ |
309 | self.window_main.label_status.setText(msg) |
310 | |
311 | def hideStep(self, step): |
312 | - image = getattr(self.window_main,"image_step%i" % step) |
313 | - label = getattr(self.window_main,"label_step%i" % step) |
314 | + image = getattr(self.window_main,"image_step%i" % step.value) |
315 | + label = getattr(self.window_main,"label_step%i" % step.value) |
316 | image.hide() |
317 | label.hide() |
318 | |
319 | def abort(self): |
320 | step = self.prev_step |
321 | - if step > 0: |
322 | - image = getattr(self.window_main,"image_step%i" % step) |
323 | + if step: |
324 | + image = getattr(self.window_main,"image_step%i" % step.value) |
325 | cancelIcon = _icon("dialog-cancel", |
326 | fallbacks=["/usr/share/icons/oxygen/16x16/actions/dialog-cancel.png", |
327 | "/usr/lib/kde4/share/icons/oxygen/16x16/actions/dialog-cancel.png", |
328 | @@ -750,6 +753,7 @@ |
329 | image.show() |
330 | |
331 | def setStep(self, step): |
332 | + super(DistUpgradeViewKDE , self).setStep(step) |
333 | okIcon = _icon("dialog-ok", |
334 | fallbacks=["/usr/share/icons/oxygen/16x16/actions/dialog-ok.png", |
335 | "/usr/lib/kde4/share/icons/oxygen/16x16/actions/dialog-ok.png", |
336 | @@ -760,15 +764,15 @@ |
337 | "/usr/share/icons/crystalsvg/16x16/actions/1rightarrow.png"]) |
338 | |
339 | if self.prev_step: |
340 | - image = getattr(self.window_main,"image_step%i" % self.prev_step) |
341 | - label = getattr(self.window_main,"label_step%i" % self.prev_step) |
342 | + image = getattr(self.window_main,"image_step%i" % self.prev_step.value) |
343 | + label = getattr(self.window_main,"label_step%i" % self.prev_step.value) |
344 | image.setPixmap(okIcon.pixmap(16, 16)) |
345 | image.show() |
346 | ##arrow.hide() |
347 | self.prev_step = step |
348 | # show the an arrow for the current step and make the label bold |
349 | - image = getattr(self.window_main,"image_step%i" % step) |
350 | - label = getattr(self.window_main,"label_step%i" % step) |
351 | + image = getattr(self.window_main,"image_step%i" % step.value) |
352 | + label = getattr(self.window_main,"label_step%i" % step.value) |
353 | image.setPixmap(arrowIcon.pixmap(16, 16)) |
354 | image.show() |
355 | label.setText("<b>" + label.text() + "</b>") |
356 | |
357 | === modified file 'DistUpgrade/DistUpgradeViewNonInteractive.py' |
358 | --- DistUpgrade/DistUpgradeViewNonInteractive.py 2016-10-10 18:05:08 +0000 |
359 | +++ DistUpgrade/DistUpgradeViewNonInteractive.py 2018-05-18 10:01:29 +0000 |
360 | @@ -36,6 +36,7 @@ |
361 | from subprocess import PIPE, Popen |
362 | |
363 | from .DistUpgradeView import DistUpgradeView, InstallProgress, AcquireProgress |
364 | +from .telemetry import get as get_telemetry |
365 | from .DistUpgradeConfigParser import DistUpgradeConfig |
366 | |
367 | |
368 | @@ -251,6 +252,7 @@ |
369 | " non-interactive version of the upgrade view " |
370 | def __init__(self, datadir=None, logdir=None): |
371 | DistUpgradeView.__init__(self) |
372 | + get_telemetry().set_updater_type('NonInteractive') |
373 | self.config = DistUpgradeConfig(".") |
374 | self._acquireProgress = NonInteractiveAcquireProgress() |
375 | self._installProgress = NonInteractiveInstallProgress(logdir) |
376 | @@ -286,6 +288,7 @@ |
377 | 4. Post upgrade stuff |
378 | 5. Complete |
379 | """ |
380 | + super(DistUpgradeViewNonInteractive, self).setStep(step) |
381 | pass |
382 | def confirmChanges(self, summary, changes, demotions, downloadSize, |
383 | actions=None, removal_bold=True): |
384 | |
385 | === modified file 'DistUpgrade/DistUpgradeViewText.py' |
386 | --- DistUpgrade/DistUpgradeViewText.py 2016-04-22 19:33:04 +0000 |
387 | +++ DistUpgrade/DistUpgradeViewText.py 2018-05-18 10:01:29 +0000 |
388 | @@ -36,6 +36,7 @@ |
389 | ENCODING, |
390 | InstallProgress, |
391 | ) |
392 | +from .telemetry import get as get_telemetry |
393 | import apt.progress |
394 | |
395 | import gettext |
396 | @@ -88,7 +89,7 @@ |
397 | def update(self, text, step): |
398 | """ update is called regularly so that the gui can be redrawn """ |
399 | if text: |
400 | - print("%s (%f)" % (text, step/float(self.totalSteps)*100)) |
401 | + print("%s (%f)" % (text, step.value/float(self.totalSteps)*100)) |
402 | def ask_cdrom_name(self): |
403 | return (False, "") |
404 | def change_cdrom(self): |
405 | @@ -101,6 +102,7 @@ |
406 | def __init__(self, datadir=None, logdir=None): |
407 | # indicate that we benefit from using gnu screen |
408 | self.needs_screen = True |
409 | + get_telemetry().set_updater_type('Text') |
410 | # its important to have a debconf frontend for |
411 | # packages like "quagga" |
412 | if "DEBIAN_FRONTEND" not in os.environ: |
413 | @@ -116,7 +118,7 @@ |
414 | except Exception as e: |
415 | logging.warning("Error setting locales (%s)" % e) |
416 | |
417 | - self.last_step = 0 # keep a record of the latest step |
418 | + self.last_step = None # keep a record of the latest step |
419 | self._opCacheProgress = apt.progress.text.OpProgress() |
420 | self._acquireProgress = TextAcquireProgress() |
421 | self._cdromProgress = TextCdromProgressAdapter() |
422 | @@ -162,6 +164,7 @@ |
423 | print() |
424 | print(_("Aborting")) |
425 | def setStep(self, step): |
426 | + super(DistUpgradeViewText, self).setStep(step) |
427 | self.last_step = step |
428 | def showDemotions(self, summary, msg, demotions): |
429 | self.information(summary, msg, |
430 | |
431 | === added file 'DistUpgrade/telemetry.py' |
432 | --- DistUpgrade/telemetry.py 1970-01-01 00:00:00 +0000 |
433 | +++ DistUpgrade/telemetry.py 2018-05-18 10:01:29 +0000 |
434 | @@ -0,0 +1,101 @@ |
435 | +# -*- coding: utf-8; Mode: Python; indent-tabs-mode: nil; tab-width: 4 -*- |
436 | + |
437 | +# Copyright (C) 2018 Canonical Ltd. |
438 | +# |
439 | +# Functions useful for the final install.py script and for ubiquity |
440 | +# plugins to use |
441 | +# |
442 | +# This program is free software; you can redistribute it and/or modify |
443 | +# it under the terms of the GNU General Public License as published by |
444 | +# the Free Software Foundation; either version 2 of the License, or |
445 | +# (at your option) any later version. |
446 | +# |
447 | +# This program is distributed in the hope that it will be useful, |
448 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
449 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
450 | +# GNU General Public License for more details. |
451 | +# |
452 | +# You should have received a copy of the GNU General Public License |
453 | +# along with this program; if not, write to the Free Software |
454 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
455 | + |
456 | + |
457 | +import logging |
458 | +import json |
459 | +import os |
460 | +import stat |
461 | +import subprocess |
462 | + |
463 | + |
464 | +def get(): |
465 | + """Return a singleton _Telemetry instance.""" |
466 | + if _Telemetry._telemetry is None: |
467 | + _Telemetry._telemetry = _Telemetry() |
468 | + return _Telemetry._telemetry |
469 | + |
470 | + |
471 | +class _Telemetry(): |
472 | + |
473 | + _telemetry = None |
474 | + |
475 | + def __init__(self): |
476 | + self._metrics = {} |
477 | + self._stages_hist = {} |
478 | + self._start_time = self._get_current_uptime() |
479 | + self._metrics["From"] = subprocess.Popen( |
480 | + ["lsb_release", "-r", "-s"], stdout=subprocess.PIPE, |
481 | + universal_newlines=True).communicate()[0].strip() |
482 | + self.add_stage('start') |
483 | + self._dest_path = '/var/log/upgrade/telemetry' |
484 | + try: |
485 | + with open('/var/log/installer/media-info') as f: |
486 | + self._metrics['InstallMedia'] = f.readline() |
487 | + except FileNotFoundError: |
488 | + pass |
489 | + |
490 | + def _get_current_uptime(self): |
491 | + """Get current uptime info. None if we couldn't fetch it.""" |
492 | + uptime = None |
493 | + try: |
494 | + with open('/proc/uptime') as f: |
495 | + uptime = float(f.read().split()[0]) |
496 | + except (FileNotFoundError, OSError, ValueError) as e: |
497 | + logging.warning("Exception while fetching current uptime: " + str(e)) |
498 | + return uptime |
499 | + |
500 | + def add_stage(self, stage_name): |
501 | + """Record installer stage with current time""" |
502 | + now = self._get_current_uptime() |
503 | + if self._start_time is None or now is None: |
504 | + return |
505 | + self._stages_hist[int(now-self._start_time)] = stage_name |
506 | + |
507 | + def set_updater_type(self, updater_type): |
508 | + """Record updater type""" |
509 | + self._metrics['Type'] = updater_type |
510 | + |
511 | + def set_using_third_party_sources(self, using): |
512 | + """Record if the user had third party sources""" |
513 | + self._metrics['ThirdPartySources'] = using |
514 | + |
515 | + def done(self): |
516 | + """Close telemetry collection |
517 | + |
518 | + Save to destination file""" |
519 | + |
520 | + self._metrics['Stages'] = self._stages_hist |
521 | + |
522 | + target_dir = os.path.dirname(self._dest_path) |
523 | + try: |
524 | + if not os.path.exists(target_dir): |
525 | + os.makedirs(target_dir) |
526 | + with open(self._dest_path, 'w') as f: |
527 | + json.dump(self._metrics, f) |
528 | + os.chmod(self._dest_path, |
529 | + stat.S_IRUSR | stat.S_IWUSR | |
530 | + stat.S_IRGRP | stat.S_IROTH) |
531 | + except OSError as e: |
532 | + logging.warning("Exception while storing telemetry data: " + |
533 | + str(e)) |
534 | + |
535 | +# vim:ai:et:sts=4:tw=80:sw=4: |
536 | |
537 | === modified file 'debian/changelog' |
538 | --- debian/changelog 2018-05-02 19:43:50 +0000 |
539 | +++ debian/changelog 2018-05-18 10:01:29 +0000 |
540 | @@ -1,3 +1,9 @@ |
541 | +ubuntu-release-upgrader (1:18.04.19) UNRELEASED; urgency=medium |
542 | + |
543 | + * Add upgrade telemetry information (LP: #1755456) |
544 | + |
545 | + -- Didier Roche <didrocks@ubuntu.com> Thu, 03 May 2018 15:55:34 +0200 |
546 | + |
547 | ubuntu-release-upgrader (1:18.04.18) bionic; urgency=medium |
548 | |
549 | * data/removal_blacklist.cfg: Drop unity, ubuntu-gnome-desktop, and |
Keep in mind that it is possible to from one release to multiple releases e.g. from xenial to artful or bionic. So without knowing the database structure I think it would make sense to also record the "To" release. You can find this in DistUpgradeCont roller. py.
I'm also curious about how this information will be used because the use cases might benefit from gathering additional information like the version of release upgrader being used or environmental variables set.
Otherwise there are a couple of typos and one nitpick to have a look at.