Merge lp:~robert-ancell/update-manager/ua into lp:update-manager

Proposed by Robert Ancell
Status: Merged
Approved by: Robert Ancell
Approved revision: 2960
Merged at revision: 2950
Proposed branch: lp:~robert-ancell/update-manager/ua
Merge into: lp:update-manager
Diff against target: 328 lines (+97/-18)
4 files modified
UpdateManager/Core/UpdateList.py (+45/-13)
UpdateManager/UpdateManager.py (+26/-1)
UpdateManager/UpdatesAvailable.py (+20/-4)
debian/changelog (+6/-0)
To merge this branch: bzr merge lp:~robert-ancell/update-manager/ua
Reviewer Review Type Date Requested Status
Julian Andres Klode Approve
Review via email: mp+430517@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Julian Andres Klode (juliank) wrote :

It's an interesting proof of concept, but I'd really like to see this done properly by investigating the cache to find out the Pro packages and then using the real Version objects vs. a query in a subprocess, see update-notifier's apt_check.py for example.

Then we also get the description and everything working correctly.

review: Needs Fixing
Revision history for this message
Julian Andres Klode (juliank) :
Revision history for this message
Julian Andres Klode (juliank) wrote :

Though tbh, we probably should ship a python library in ubuntu-advantage-tools and use that in update-notifier and update-manager rather than implement the logic in a third place, but consolidation is not a blocker.

lp:~robert-ancell/update-manager/ua updated
2959. By Robert Ancell

Remove unused code

2960. By Robert Ancell

Use json.load instead of json.loads

Revision history for this message
Robert Ancell (robert-ancell) wrote :

I've fixed the small issues you pointed out.

As you discussed with Seb, we wont be able to access the cache directly in the timeframe required so this will have to be the current solution, right?

Revision history for this message
Julian Andres Klode (juliank) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'UpdateManager/Core/UpdateList.py'
2--- UpdateManager/Core/UpdateList.py 2021-02-05 21:17:38 +0000
3+++ UpdateManager/Core/UpdateList.py 2022-09-27 23:18:51 +0000
4@@ -44,11 +44,12 @@
5
6
7 class UpdateItem():
8- def __init__(self, pkg, name, icon, to_remove):
9+ def __init__(self, pkg, name, icon, to_remove, sensitive=True):
10 self.icon = icon
11 self.name = name
12 self.pkg = pkg
13 self.to_remove = to_remove
14+ self.sensitive = sensitive
15
16 def is_selected(self):
17 if not self.to_remove:
18@@ -60,13 +61,13 @@
19 class UpdateGroup(UpdateItem):
20 _depcache = {}
21
22- def __init__(self, pkg, name, icon, to_remove):
23- UpdateItem.__init__(self, pkg, name, icon, to_remove)
24+ def __init__(self, pkg, name, icon, to_remove, sensitive=True):
25+ UpdateItem.__init__(self, pkg, name, icon, to_remove, sensitive)
26 self._items = set()
27 self._deps = set()
28 self.core_item = None
29 if pkg is not None:
30- self.core_item = UpdateItem(pkg, name, icon, to_remove)
31+ self.core_item = UpdateItem(pkg, name, icon, to_remove, sensitive)
32 self._items.add(self.core_item)
33
34 @property
35@@ -75,10 +76,10 @@
36 all_items.extend(self._items)
37 return sorted(all_items, key=lambda a: a.name.lower())
38
39- def add(self, pkg, cache=None, eventloop_callback=None, to_remove=False):
40+ def add(self, pkg, cache=None, eventloop_callback=None, to_remove=False, sensitive=True):
41 name = utils.get_package_label(pkg)
42 icon = Gio.ThemedIcon.new("package")
43- self._items.add(UpdateItem(pkg, name, icon, to_remove))
44+ self._items.add(UpdateItem(pkg, name, icon, to_remove, sensitive))
45 # If the pkg is in self._deps, stop here. We have already calculated
46 # the recursive dependencies for this package, no need to do it again.
47 if cache and pkg.name in cache and pkg.name not in self._deps:
48@@ -154,27 +155,27 @@
49
50
51 class UpdateApplicationGroup(UpdateGroup):
52- def __init__(self, pkg, application, to_remove):
53+ def __init__(self, pkg, application, to_remove, sensitive=True):
54 name = application.get_display_name()
55 icon = application.get_icon()
56 super(UpdateApplicationGroup, self).__init__(pkg, name, icon,
57- to_remove)
58+ to_remove, sensitive)
59
60
61 class UpdatePackageGroup(UpdateGroup):
62- def __init__(self, pkg, to_remove):
63+ def __init__(self, pkg, to_remove, sensitive=True):
64 name = utils.get_package_label(pkg)
65 icon = Gio.ThemedIcon.new("package")
66- super(UpdatePackageGroup, self).__init__(pkg, name, icon, to_remove)
67+ super(UpdatePackageGroup, self).__init__(pkg, name, icon, to_remove, sensitive)
68
69
70 class UpdateSystemGroup(UpdateGroup):
71- def __init__(self, cache, to_remove):
72+ def __init__(self, cache, to_remove, sensitive=True):
73 # Translators: the %s is a distro name, like 'Ubuntu' and 'base' as in
74 # the core components and packages.
75 name = _("%s base") % utils.get_ubuntu_flavor_name(cache=cache)
76 icon = Gio.ThemedIcon.new("distributor-logo")
77- super(UpdateSystemGroup, self).__init__(None, name, icon, to_remove)
78+ super(UpdateSystemGroup, self).__init__(None, name, icon, to_remove, sensitive)
79
80
81 class UpdateOrigin():
82@@ -216,6 +217,7 @@
83 self.security_groups = []
84 self.kernel_autoremove_groups = []
85 self.duplicate_groups = []
86+ self.ubuntu_pro_groups = []
87 self.num_updates = 0
88 self.random = random.Random()
89 self.ignored_phased_updates = []
90@@ -464,7 +466,7 @@
91
92 return app_groups + pkg_groups
93
94- def update(self, cache, eventloop_callback=None, duplicate_packages=[]):
95+ def update(self, cache, eventloop_callback=None, duplicate_packages=[], ua_security_packages=[]):
96 self.held_back = []
97
98 # do the upgrade
99@@ -476,6 +478,31 @@
100 kernel_autoremove_pkgs = []
101 duplicate_pkgs = []
102
103+ class FakeUbuntuProPackageCandidate:
104+ def __init__ (self, source_name, version, size):
105+ self.source_name = source_name
106+ self.summary = source_name
107+ self.description = source_name
108+ self.version = version
109+ self.size = size
110+ self.downloadable = False
111+ self.record = {}
112+ class FakeUbuntuProPackage:
113+ def __init__ (self, package_name, version, size):
114+ self.name = package_name
115+ self.candidate = FakeUbuntuProPackageCandidate(package_name, version, size)
116+ self.marked_install = False
117+ self.marked_upgrade = False
118+
119+ def mark_install(self):
120+ pass
121+
122+ def mark_delete(self):
123+ pass
124+ fake_ua_packages = []
125+ for (package_name, version, size) in ua_security_packages:
126+ fake_ua_packages.append(FakeUbuntuProPackage(package_name, version, size))
127+
128 # Find all upgradable packages
129 for pkg in cache:
130 if pkg.is_upgradable or pkg.marked_install:
131@@ -541,3 +568,8 @@
132 cache, kernel_autoremove_pkgs, eventloop_callback, True)
133 self.duplicate_groups = self._make_groups(
134 cache, duplicate_pkgs, eventloop_callback, True)
135+ if len(fake_ua_packages) > 0:
136+ ubuntu_pro_group = UpdateGroup(None, "Ubuntu Pro (enable in Settingsā€¦)", None, False, False)
137+ for package in fake_ua_packages:
138+ ubuntu_pro_group.add(package)
139+ self.ubuntu_pro_groups = [ubuntu_pro_group]
140
141=== modified file 'UpdateManager/UpdateManager.py'
142--- UpdateManager/UpdateManager.py 2021-12-10 20:29:40 +0000
143+++ UpdateManager/UpdateManager.py 2022-09-27 23:18:51 +0000
144@@ -36,6 +36,7 @@
145
146 import distro_info
147 import fnmatch
148+import json
149 import os
150 import subprocess
151 import sys
152@@ -85,6 +86,7 @@
153 self.unity = UnitySupport()
154 self.controller = None
155 self.cache = None
156+ self.ua_security_packages = []
157 self.update_list = None
158 self.meta_release = None
159 self.hwe_replacement_packages = None
160@@ -268,6 +270,27 @@
161 and pkg.installed:
162 self.oem_metapackages.add(pkg)
163
164+ def _get_ua_security_status(self):
165+ self.ua_security_packages = []
166+ try:
167+ p = subprocess.Popen(['ua', 'security-status', '--format=json'], stdout=subprocess.PIPE)
168+ except OSError:
169+ pass
170+ else:
171+ while p.poll() is None:
172+ while Gtk.events_pending():
173+ Gtk.main_iteration()
174+ time.sleep(0.05)
175+ s = json.load(p.stdout)
176+ ua_attached = s.get('summary', {}).get('ua', {}).get('attached', False)
177+ for package in s.get('packages', []):
178+ status = package.get('status', '')
179+ if status == 'pending_attach':
180+ name = package.get('name', '')
181+ version = package.get('version', '')
182+ size = package.get('download_size', 0)
183+ self.ua_security_packages.append((name, version, size))
184+
185 def _make_available_pane(self, install_count, need_reboot=False,
186 cancelled_update=False, error_occurred=False):
187 self._check_hwe_support_status()
188@@ -423,6 +446,8 @@
189
190 self._check_oem_metapackages()
191
192+ self._get_ua_security_status()
193+
194 for pkgname in self.oem_metapackages:
195 try:
196 if not self.cache[pkgname].is_installed:
197@@ -433,7 +458,7 @@
198 self.update_list = UpdateList(self)
199 try:
200 self.update_list.update(self.cache, eventloop_callback=iterate,
201- duplicate_packages=self.duplicate_packages)
202+ duplicate_packages=self.duplicate_packages, ua_security_packages=self.ua_security_packages)
203 except SystemError as e:
204 header = _("Could not calculate the upgrade")
205 desc = _("An unresolvable problem occurred while "
206
207=== modified file 'UpdateManager/UpdatesAvailable.py'
208--- UpdateManager/UpdatesAvailable.py 2020-12-14 11:10:57 +0000
209+++ UpdateManager/UpdatesAvailable.py 2022-09-27 23:18:51 +0000
210@@ -74,7 +74,7 @@
211 # - screen reader does not say "Downloaded" for downloaded updates
212
213 # list constants
214-(LIST_NAME, LIST_UPDATE_DATA, LIST_SIZE, LIST_TOGGLE_ACTIVE) = range(4)
215+(LIST_NAME, LIST_UPDATE_DATA, LIST_SIZE, LIST_TOGGLE_ACTIVE, LIST_SENSITIVE) = range(5)
216
217 # NetworkManager enums
218 from .Core.roam import NetworkManagerHelper
219@@ -261,7 +261,7 @@
220 changes_buffer.create_tag("versiontag", weight=Pango.Weight.BOLD)
221
222 # the treeview (move into it's own code!)
223- self.store = Gtk.TreeStore(str, GObject.TYPE_PYOBJECT, str, bool)
224+ self.store = Gtk.TreeStore(str, GObject.TYPE_PYOBJECT, str, bool, bool)
225 self.treeview_update.set_model(None)
226
227 self.image_restart.set_from_gicon(self.get_restart_icon(),
228@@ -293,6 +293,8 @@
229 pkg_column.pack_start(pkg_toggle_renderer, False)
230 pkg_column.add_attribute(pkg_toggle_renderer,
231 'active', LIST_TOGGLE_ACTIVE)
232+ pkg_column.add_attribute(pkg_toggle_renderer,
233+ 'sensitive', LIST_SENSITIVE)
234 pkg_column.set_cell_data_func(pkg_toggle_renderer,
235 self.pkg_toggle_renderer_data_func)
236
237@@ -319,6 +321,8 @@
238 size_column = Gtk.TreeViewColumn(_("Download"), size_renderer,
239 text=LIST_SIZE)
240 size_column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
241+ size_column.add_attribute(size_renderer,
242+ 'sensitive', LIST_SENSITIVE)
243 self.treeview_update.append_column(size_column)
244
245 self.treeview_update.set_headers_visible(True)
246@@ -404,8 +408,11 @@
247 def restart_icon_renderer_data_func(self, cell_layout, renderer, model,
248 iter, user_data):
249 data = model.get_value(iter, LIST_UPDATE_DATA)
250+ sensitive = model.get_value(iter, LIST_SENSITIVE)
251 path = model.get_path(iter)
252
253+ renderer.set_sensitive(sensitive)
254+
255 requires_restart = False
256 if data.item and data.item.pkg:
257 requires_restart = self.pkg_requires_restart(data.item.pkg)
258@@ -433,6 +440,7 @@
259 if data.item:
260 activatable = data.item.pkg.name not in self.list.held_back
261 inconsistent = False
262+ renderer.set_sensitive(data.item.sensitive)
263 elif data.group:
264 activatable = True
265 inconsistent = data.group.selection_is_inconsistent()
266@@ -485,8 +493,11 @@
267 def pkg_label_renderer_data_func(self, cell_layout, renderer, model,
268 iter, user_data):
269 data = model.get_value(iter, LIST_UPDATE_DATA)
270+ sensitive = model.get_value(iter, LIST_SENSITIVE)
271 name = GLib.markup_escape_text(model.get_value(iter, LIST_NAME))
272
273+ renderer.set_sensitive(sensitive)
274+
275 if data.group:
276 markup = name
277 elif data.item:
278@@ -1000,6 +1011,7 @@
279 name,
280 UpdateData(groups, None, None),
281 humanize_size(total_size),
282+ True,
283 True
284 ]
285 return self.store.append(None, header_row)
286@@ -1023,7 +1035,8 @@
287 group.name,
288 UpdateData(None, group, group_is_item),
289 humanize_size(group.get_total_size()),
290- True
291+ True,
292+ group.sensitive
293 ]
294 group_iter = self.store.append(None, group_row)
295
296@@ -1034,7 +1047,8 @@
297 item.name,
298 UpdateData(None, None, item),
299 humanize_size(getattr(item.pkg.candidate, "size", 0)),
300- True
301+ True,
302+ group.sensitive
303 ]
304 self.store.append(group_iter, item_row)
305
306@@ -1077,6 +1091,8 @@
307 _("Duplicate packages to be removed"),
308 self.list.duplicate_groups)
309 self._add_groups(self.list.duplicate_groups)
310+ if self.list.ubuntu_pro_groups:
311+ self._add_groups(self.list.ubuntu_pro_groups)
312
313 self.treeview_update.set_model(self.store)
314 self.pkg_cell_area.indent_toplevel = (
315
316=== modified file 'debian/changelog'
317--- debian/changelog 2022-08-26 01:52:12 +0000
318+++ debian/changelog 2022-09-27 23:18:51 +0000
319@@ -1,3 +1,9 @@
320+update-manager (1:22.10.1+ubuntupro1) kinetic; urgency=medium
321+
322+ * Show pending Ubuntu pro packages
323+
324+ -- Robert Ancell <robert.ancell@canonical.com> Fri, 01 Jul 2022 16:15:05 +1200
325+
326 update-manager (1:22.10.1) kinetic; urgency=medium
327
328 * Build with snapd-glib 1.61

Subscribers

People subscribed via source and target branches

to status/vote changes: