Merge ~jansdhillon/ubuntu/+source/landscape-client:lp2099283-package-reporter-fixes-jammy into ubuntu/+source/landscape-client:ubuntu/jammy-devel
- Git
- lp:~jansdhillon/ubuntu/+source/landscape-client
- lp2099283-package-reporter-fixes-jammy
- Merge into ubuntu/jammy-devel
Proposed by
Jan-Yaeger Dhillon
Status: | Needs review |
---|---|
Proposed branch: | ~jansdhillon/ubuntu/+source/landscape-client:lp2099283-package-reporter-fixes-jammy |
Merge into: | ubuntu/+source/landscape-client:ubuntu/jammy-devel |
Diff against target: |
1661 lines (+1634/-0) 4 files modified
debian/changelog (+7/-0) debian/patches/package-reporter-high-cpu.patch (+110/-0) debian/patches/parse-lsb-output.patch (+1515/-0) debian/patches/series (+2/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andreas Hasenack | Needs Fixing | ||
David McLain | Pending | ||
Ubuntu Sponsors | Pending | ||
Canonical Server Reporter | Pending | ||
Review via email:
|
Commit message
Description of the change
To post a comment you must log in.
- 07e8936... by jansdhillon <email address hidden>
-
fix lp question number to avoid confusion with a separate bug
- f473b7a... by jansdhillon <email address hidden>
-
update LP numbers in patches/changelog
Revision history for this message

Andreas Hasenack (ahasenack) : | # |
Unmerged commits
- f473b7a... by jansdhillon <email address hidden>
-
update LP numbers in patches/changelog
- 07e8936... by jansdhillon <email address hidden>
-
fix lp question number to avoid confusion with a separate bug
- ea43999... by jansdhillon <email address hidden>
-
add lp403745-
package- reporter- high-cpu. patch, lp2031036- parse-lsb- output. patch, and update changelog
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/debian/changelog b/debian/changelog |
2 | index 941e2a5..157efde 100644 |
3 | --- a/debian/changelog |
4 | +++ b/debian/changelog |
5 | @@ -1,3 +1,10 @@ |
6 | +landscape-client (23.02-0ubuntu1~22.04.5) jammy; urgency=medium |
7 | + |
8 | + * d/p/package-reporter-high-cpu.patch: backport fix to reduce CPU usage of package-reporter by avoiding creating Origin objects from the Python apt package. (LP: #2099283) |
9 | + * d/p/parse-lsb-output.patch: backport fix for error raised by package-reporter when parsing lsb output and add support for lsb modules. (LP: #2031036) |
10 | + |
11 | + -- Jan-Yaeger Dhillon <jan.dhillon@canonical.com> Mon, 10 Mar 2025 16:50:45 -0700 |
12 | + |
13 | landscape-client (23.02-0ubuntu1~22.04.4) jammy; urgency=medium |
14 | |
15 | * d/p/add-non-filtered-interfaces-to-the-api-response.patch: include |
16 | diff --git a/debian/patches/package-reporter-high-cpu.patch b/debian/patches/package-reporter-high-cpu.patch |
17 | new file mode 100644 |
18 | index 0000000..d0ea0f1 |
19 | --- /dev/null |
20 | +++ b/debian/patches/package-reporter-high-cpu.patch |
21 | @@ -0,0 +1,110 @@ |
22 | +Description: Reduce CPU usage of package-reporter (LP: #2099283) |
23 | +Author: Jan-Yaeger Dhillon, jan.dhillon@canonical.com |
24 | +Origin: backport, https://github.com/canonical/landscape-client/commit/f5a6d8a924097a37e70ec25522bd748a341212bf |
25 | +Bug-Ubuntu: https://answers.launchpad.net/landscape-client/+question/403745 |
26 | +Reviewed-by: Kevin Nasto, kevin.nasto@canonical.com |
27 | +Last-Update: 2025-03-09 |
28 | +Index: landscape-client/landscape/client/package/reporter.py |
29 | +=================================================================== |
30 | +--- landscape-client.orig/landscape/client/package/reporter.py |
31 | ++++ landscape-client/landscape/client/package/reporter.py |
32 | +@@ -638,48 +638,53 @@ class PackageReporter(PackageTaskHandler |
33 | + backports_archive = "{}-backports".format(lsb["code-name"]) |
34 | + security_archive = "{}-security".format(lsb["code-name"]) |
35 | + |
36 | +- for package in self._facade.get_packages(): |
37 | ++ for package_version in self._facade.get_packages(): |
38 | ++ # Get archives from the list of PackageFiles |
39 | ++ # for the given package version rather than using |
40 | ++ # package_version.origins from the Python apt package. |
41 | ++ # We only want to check the archives, and creating |
42 | ++ # Origins using package_version.origins is expensive. |
43 | ++ # See /usr/lib/python3/dist-packages/apt/package.py |
44 | ++ archives = [ |
45 | ++ # Ex. jammy-backports |
46 | ++ package_file.archive |
47 | ++ for package_file, _ in package_version._cand.file_list |
48 | ++ ] |
49 | ++ |
50 | + # Don't include package versions from the official backports |
51 | + # archive. The backports archive is enabled by default since |
52 | + # xenial with a pinning policy of 100. Ideally we would |
53 | + # support pinning, but we don't yet. In the mean time, we |
54 | + # ignore backports, so that packages don't get automatically |
55 | + # upgraded to the backports version. |
56 | +- backport_origins = [ |
57 | +- origin for origin in package.origins |
58 | +- if origin.archive == backports_archive] |
59 | +- if backport_origins and ( |
60 | +- len(backport_origins) == len(package.origins)): |
61 | ++ if all(archive == backports_archive for archive in archives): |
62 | + # Ignore the version if it's only in the official |
63 | + # backports archive. If it's somewhere else as well, |
64 | + # e.g. a PPA, we assume it was added manually and the |
65 | + # user wants to get updates from it. |
66 | + continue |
67 | +- hash = self._facade.get_package_hash(package) |
68 | ++ hash = self._facade.get_package_hash(package_version) |
69 | + id = self._store.get_hash_id(hash) |
70 | + if id is not None: |
71 | +- if self._facade.is_package_installed(package): |
72 | ++ if self._facade.is_package_installed(package_version): |
73 | + current_installed.add(id) |
74 | +- if self._facade.is_package_available(package): |
75 | ++ if self._facade.is_package_available(package_version): |
76 | + current_available.add(id) |
77 | +- if self._facade.is_package_autoremovable(package): |
78 | ++ if self._facade.is_package_autoremovable(package_version): |
79 | + current_autoremovable.add(id) |
80 | + else: |
81 | + current_available.add(id) |
82 | + |
83 | + # Are there any packages that this package is an upgrade for? |
84 | +- if self._facade.is_package_upgrade(package): |
85 | ++ if self._facade.is_package_upgrade(package_version): |
86 | + current_upgrades.add(id) |
87 | + |
88 | + # Is this package present in the security pocket? |
89 | +- security_origins = any( |
90 | +- origin for origin in package.origins |
91 | +- if origin.archive == security_archive) |
92 | +- if security_origins: |
93 | ++ if security_archive in archives: |
94 | + current_security.add(id) |
95 | + |
96 | +- for package in self._facade.get_locked_packages(): |
97 | +- hash = self._facade.get_package_hash(package) |
98 | ++ for package_version in self._facade.get_locked_packages(): |
99 | ++ hash = self._facade.get_package_hash(package_version) |
100 | + id = self._store.get_hash_id(hash) |
101 | + if id is not None: |
102 | + current_locked.add(id) |
103 | +Index: landscape-client/landscape/client/package/tests/test_reporter.py |
104 | +=================================================================== |
105 | +--- landscape-client.orig/landscape/client/package/tests/test_reporter.py |
106 | ++++ landscape-client/landscape/client/package/tests/test_reporter.py |
107 | +@@ -1067,6 +1067,24 @@ class PackageReporterAptTest(LandscapeTe |
108 | + result = self.reporter.detect_packages_changes() |
109 | + return result.addCallback(got_result) |
110 | + |
111 | ++ def test_compute_packages_changes_package_origins_not_called(self): |
112 | ++ """ |
113 | ++ Archive info is extracted directly from the package versions |
114 | ++ and the apt.package.Version.origins property is not called |
115 | ++ """ |
116 | ++ with mock.patch("apt.package.Version.origins") as version_origins_mock: |
117 | ++ self.successResultOf(self.reporter._compute_packages_changes()) |
118 | ++ version_origins_mock.assert_not_called() |
119 | ++ |
120 | ++ def test_compute_packages_changes_origins_not_created(self): |
121 | ++ """ |
122 | ++ Archive info is extracted directly from the package versions |
123 | ++ and no Origins are created (expensive find_index() is not called) |
124 | ++ """ |
125 | ++ with mock.patch("apt.package.Origin.__init__") as origin_mock: |
126 | ++ self.successResultOf(self.reporter._compute_packages_changes()) |
127 | ++ origin_mock.assert_not_called() |
128 | ++ |
129 | + def test_detect_packages_changes_with_backports_others(self): |
130 | + """ |
131 | + Packages coming from backport archives that aren't named like |
132 | diff --git a/debian/patches/parse-lsb-output.patch b/debian/patches/parse-lsb-output.patch |
133 | new file mode 100644 |
134 | index 0000000..ac99551 |
135 | --- /dev/null |
136 | +++ b/debian/patches/parse-lsb-output.patch |
137 | @@ -0,0 +1,1515 @@ |
138 | +Description: Fix package-reporter error when parsing lsb output and support lsb modules (LP: #2031036) |
139 | +Origin: backport, https://github.com/canonical/landscape-client/commit/75742a35074a9a8296ea3d3f445a80df1764404d |
140 | +Bug-Ubuntu: https://bugs.launchpad.net/landscape-client/+bug/2031036 |
141 | +Index: landscape-client/landscape/client/monitor/computerinfo.py |
142 | +=================================================================== |
143 | +--- landscape-client.orig/landscape/client/monitor/computerinfo.py |
144 | ++++ landscape-client/landscape/client/monitor/computerinfo.py |
145 | +@@ -1,13 +1,16 @@ |
146 | +-import os |
147 | + import logging |
148 | +-from twisted.internet.defer import inlineCallbacks, returnValue |
149 | ++import os |
150 | + |
151 | ++from twisted.internet.defer import inlineCallbacks |
152 | ++from twisted.internet.defer import returnValue |
153 | ++ |
154 | ++from landscape.client.monitor.plugin import MonitorPlugin |
155 | ++from landscape.lib.cloud import fetch_ec2_meta_data |
156 | + from landscape.lib.fetch import fetch_async |
157 | + from landscape.lib.fs import read_text_file |
158 | +-from landscape.lib.lsb_release import LSB_RELEASE_FILENAME, parse_lsb_release |
159 | +-from landscape.lib.cloud import fetch_ec2_meta_data |
160 | + from landscape.lib.network import get_fqdn |
161 | +-from landscape.client.monitor.plugin import MonitorPlugin |
162 | ++from landscape.lib.os_release import OS_RELEASE_FILENAME |
163 | ++from landscape.lib.os_release import parse_os_release |
164 | + |
165 | + METADATA_RETRY_MAX = 3 # Number of retries to get EC2 meta-data |
166 | + |
167 | +@@ -22,13 +25,17 @@ class ComputerInfo(MonitorPlugin): |
168 | + persist_name = "computer-info" |
169 | + scope = "computer" |
170 | + |
171 | +- def __init__(self, get_fqdn=get_fqdn, |
172 | +- meminfo_filename="/proc/meminfo", |
173 | +- lsb_release_filename=LSB_RELEASE_FILENAME, |
174 | +- root_path="/", fetch_async=fetch_async): |
175 | ++ def __init__( |
176 | ++ self, |
177 | ++ get_fqdn=get_fqdn, |
178 | ++ meminfo_filename="/proc/meminfo", |
179 | ++ os_release_filename=OS_RELEASE_FILENAME, |
180 | ++ root_path="/", |
181 | ++ fetch_async=fetch_async, |
182 | ++ ): |
183 | + self._get_fqdn = get_fqdn |
184 | + self._meminfo_filename = meminfo_filename |
185 | +- self._lsb_release_filename = lsb_release_filename |
186 | ++ self._os_release_filename = os_release_filename |
187 | + self._root_path = root_path |
188 | + self._cloud_instance_metadata = None |
189 | + self._cloud_retries = 0 |
190 | +@@ -125,17 +132,17 @@ class ComputerInfo(MonitorPlugin): |
191 | + def _get_distribution_info(self): |
192 | + """Get details about the distribution.""" |
193 | + message = {} |
194 | +- message.update(parse_lsb_release(self._lsb_release_filename)) |
195 | ++ message.update(parse_os_release(self._os_release_filename)) |
196 | + return message |
197 | + |
198 | + @inlineCallbacks |
199 | + def _create_cloud_instance_metadata_message(self): |
200 | + """Fetch cloud metadata and insert it in a message.""" |
201 | + message = None |
202 | +- if (self._cloud_instance_metadata is None and |
203 | +- self._cloud_retries < METADATA_RETRY_MAX |
204 | +- ): |
205 | +- |
206 | ++ if ( |
207 | ++ self._cloud_instance_metadata is None |
208 | ++ and self._cloud_retries < METADATA_RETRY_MAX |
209 | ++ ): |
210 | + self._cloud_instance_metadata = yield self._fetch_ec2_meta_data() |
211 | + message = self._cloud_instance_metadata |
212 | + returnValue(message) |
213 | +Index: landscape-client/landscape/client/monitor/tests/test_computerinfo.py |
214 | +=================================================================== |
215 | +--- landscape-client.orig/landscape/client/monitor/tests/test_computerinfo.py |
216 | ++++ landscape-client/landscape/client/monitor/tests/test_computerinfo.py |
217 | +@@ -1,19 +1,32 @@ |
218 | +-import mock |
219 | + import os |
220 | + import re |
221 | ++from unittest import mock |
222 | + |
223 | +-from twisted.internet.defer import succeed, fail, inlineCallbacks |
224 | +- |
225 | +-from landscape.lib.fetch import HTTPCodeError, PyCurlError |
226 | ++from twisted.internet.defer import fail |
227 | ++from twisted.internet.defer import inlineCallbacks |
228 | ++from twisted.internet.defer import succeed |
229 | ++ |
230 | ++from landscape.client.monitor.computerinfo import ComputerInfo |
231 | ++from landscape.client.monitor.computerinfo import METADATA_RETRY_MAX |
232 | ++from landscape.client.tests.helpers import LandscapeTest |
233 | ++from landscape.client.tests.helpers import MonitorHelper |
234 | ++from landscape.lib.fetch import HTTPCodeError |
235 | ++from landscape.lib.fetch import PyCurlError |
236 | + from landscape.lib.fs import create_text_file |
237 | +-from landscape.client.monitor.computerinfo import ( |
238 | +- ComputerInfo, METADATA_RETRY_MAX) |
239 | +-from landscape.client.tests.helpers import LandscapeTest, MonitorHelper |
240 | +- |
241 | +-SAMPLE_LSB_RELEASE = "DISTRIB_ID=Ubuntu\n" \ |
242 | +- "DISTRIB_RELEASE=6.06\n" \ |
243 | +- "DISTRIB_CODENAME=dapper\n" \ |
244 | +- "DISTRIB_DESCRIPTION=\"Ubuntu 6.06.1 LTS\"\n" |
245 | ++ |
246 | ++SAMPLE_OS_RELEASE = """PRETTY_NAME="Ubuntu 22.04.3 LTS" |
247 | ++NAME="Ubuntu" |
248 | ++VERSION_ID="22.04" |
249 | ++VERSION="22.04.3 LTS (Jammy Jellyfish)" |
250 | ++VERSION_CODENAME=codename |
251 | ++ID=ubuntu |
252 | ++ID_LIKE=debian |
253 | ++HOME_URL="https://www.ubuntu.com/" |
254 | ++SUPPORT_URL="https://help.ubuntu.com/" |
255 | ++BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" |
256 | ++PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" |
257 | ++UBUNTU_CODENAME=codename |
258 | ++""" |
259 | + |
260 | + |
261 | + def get_fqdn(): |
262 | +@@ -21,7 +34,6 @@ def get_fqdn(): |
263 | + |
264 | + |
265 | + class ComputerInfoTest(LandscapeTest): |
266 | +- |
267 | + helpers = [MonitorHelper] |
268 | + |
269 | + sample_memory_info = """ |
270 | +@@ -52,7 +64,7 @@ VmallocChunk: 107432 kB |
271 | + |
272 | + def setUp(self): |
273 | + LandscapeTest.setUp(self) |
274 | +- self.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
275 | ++ self.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
276 | + self.query_results = {} |
277 | + |
278 | + def fetch_stub(url, **kwargs): |
279 | +@@ -214,16 +226,16 @@ VmallocChunk: 107432 kB |
280 | + the distribution data reported by the plugin. |
281 | + """ |
282 | + self.mstore.set_accepted_types(["distribution-info"]) |
283 | +- plugin = ComputerInfo(lsb_release_filename=self.lsb_release_filename) |
284 | ++ plugin = ComputerInfo(os_release_filename=self.os_release_filename) |
285 | + self.monitor.add(plugin) |
286 | + |
287 | + plugin.exchange() |
288 | + message = self.mstore.get_pending_messages()[0] |
289 | + self.assertEqual(message["type"], "distribution-info") |
290 | + self.assertEqual(message["distributor-id"], "Ubuntu") |
291 | +- self.assertEqual(message["description"], "Ubuntu 6.06.1 LTS") |
292 | +- self.assertEqual(message["release"], "6.06") |
293 | +- self.assertEqual(message["code-name"], "dapper") |
294 | ++ self.assertEqual(message["description"], "Ubuntu 22.04.3 LTS") |
295 | ++ self.assertEqual(message["release"], "22.04") |
296 | ++ self.assertEqual(message["code-name"], "codename") |
297 | + |
298 | + def test_distribution_reported_only_once(self): |
299 | + """ |
300 | +@@ -249,23 +261,25 @@ VmallocChunk: 107432 kB |
301 | + the server. |
302 | + """ |
303 | + self.mstore.set_accepted_types(["distribution-info"]) |
304 | +- plugin = ComputerInfo(lsb_release_filename=self.lsb_release_filename) |
305 | ++ plugin = ComputerInfo(os_release_filename=self.os_release_filename) |
306 | + self.monitor.add(plugin) |
307 | + |
308 | + plugin.exchange() |
309 | + message = self.mstore.get_pending_messages()[0] |
310 | + self.assertEqual(message["type"], "distribution-info") |
311 | + self.assertEqual(message["distributor-id"], "Ubuntu") |
312 | +- self.assertEqual(message["description"], "Ubuntu 6.06.1 LTS") |
313 | +- self.assertEqual(message["release"], "6.06") |
314 | +- self.assertEqual(message["code-name"], "dapper") |
315 | +- |
316 | +- plugin._lsb_release_filename = self.makeFile("""\ |
317 | +-DISTRIB_ID=Ubuntu |
318 | +-DISTRIB_RELEASE=6.10 |
319 | +-DISTRIB_CODENAME=edgy |
320 | +-DISTRIB_DESCRIPTION="Ubuntu 6.10" |
321 | +-""") |
322 | ++ self.assertEqual(message["description"], "Ubuntu 22.04.3 LTS") |
323 | ++ self.assertEqual(message["release"], "22.04") |
324 | ++ self.assertEqual(message["code-name"], "codename") |
325 | ++ |
326 | ++ plugin._os_release_filename = self.makeFile( |
327 | ++ """\ |
328 | ++NAME=Ubuntu |
329 | ++VERSION_ID=6.10 |
330 | ++VERSION_CODENAME=edgy |
331 | ++PRETTY_NAME="Ubuntu 6.10" |
332 | ++""", |
333 | ++ ) |
334 | + plugin.exchange() |
335 | + message = self.mstore.get_pending_messages()[1] |
336 | + self.assertEqual(message["type"], "distribution-info") |
337 | +@@ -276,23 +290,25 @@ DISTRIB_DESCRIPTION="Ubuntu 6.10" |
338 | + |
339 | + def test_unknown_distribution_key(self): |
340 | + self.mstore.set_accepted_types(["distribution-info"]) |
341 | +- lsb_release_filename = self.makeFile("""\ |
342 | +-DISTRIB_ID=Ubuntu |
343 | +-DISTRIB_RELEASE=6.10 |
344 | +-DISTRIB_CODENAME=edgy |
345 | +-DISTRIB_DESCRIPTION="Ubuntu 6.10" |
346 | ++ os_release_filename = self.makeFile( |
347 | ++ """\ |
348 | ++NAME=Ubuntu |
349 | ++VERSION_ID=22.04 |
350 | ++VERSION_CODENAME=codename |
351 | ++PRETTY_NAME="Ubuntu 22.04.3 LTS" |
352 | + DISTRIB_NEW_UNEXPECTED_KEY=ooga |
353 | +-""") |
354 | +- plugin = ComputerInfo(lsb_release_filename=lsb_release_filename) |
355 | ++""", |
356 | ++ ) |
357 | ++ plugin = ComputerInfo(os_release_filename=os_release_filename) |
358 | + self.monitor.add(plugin) |
359 | + |
360 | + plugin.exchange() |
361 | + message = self.mstore.get_pending_messages()[0] |
362 | + self.assertEqual(message["type"], "distribution-info") |
363 | + self.assertEqual(message["distributor-id"], "Ubuntu") |
364 | +- self.assertEqual(message["description"], "Ubuntu 6.10") |
365 | +- self.assertEqual(message["release"], "6.10") |
366 | +- self.assertEqual(message["code-name"], "edgy") |
367 | ++ self.assertEqual(message["description"], "Ubuntu 22.04.3 LTS") |
368 | ++ self.assertEqual(message["release"], "22.04") |
369 | ++ self.assertEqual(message["code-name"], "codename") |
370 | + |
371 | + def test_resynchronize(self): |
372 | + """ |
373 | +@@ -301,11 +317,13 @@ DISTRIB_NEW_UNEXPECTED_KEY=ooga |
374 | + """ |
375 | + self.mstore.set_accepted_types(["distribution-info", "computer-info"]) |
376 | + meminfo_filename = self.makeFile(self.sample_memory_info) |
377 | +- plugin = ComputerInfo(get_fqdn=get_fqdn, |
378 | +- meminfo_filename=meminfo_filename, |
379 | +- lsb_release_filename=self.lsb_release_filename, |
380 | +- root_path=self.makeDir(), |
381 | +- fetch_async=self.fetch_func) |
382 | ++ plugin = ComputerInfo( |
383 | ++ get_fqdn=get_fqdn, |
384 | ++ meminfo_filename=meminfo_filename, |
385 | ++ os_release_filename=self.os_release_filename, |
386 | ++ root_path=self.makeDir(), |
387 | ++ fetch_async=self.fetch_func, |
388 | ++ ) |
389 | + self.monitor.add(plugin) |
390 | + plugin.exchange() |
391 | + self.reactor.fire("resynchronize", scopes=["computer"]) |
392 | +@@ -314,12 +332,17 @@ DISTRIB_NEW_UNEXPECTED_KEY=ooga |
393 | + "timestamp": 0, "total-memory": 1510, |
394 | + "total-swap": 1584} |
395 | + |
396 | +- dist_info = {"type": "distribution-info", |
397 | +- "code-name": "dapper", "description": "Ubuntu 6.06.1 LTS", |
398 | +- "distributor-id": "Ubuntu", "release": "6.06"} |
399 | +- self.assertMessages(self.mstore.get_pending_messages(), |
400 | +- [computer_info, dist_info, |
401 | +- computer_info, dist_info]) |
402 | ++ dist_info = { |
403 | ++ "type": "distribution-info", |
404 | ++ "code-name": "codename", |
405 | ++ "description": "Ubuntu 22.04.3 LTS", |
406 | ++ "distributor-id": "Ubuntu", |
407 | ++ "release": "22.04", |
408 | ++ } |
409 | ++ self.assertMessages( |
410 | ++ self.mstore.get_pending_messages(), |
411 | ++ [computer_info, dist_info, computer_info, dist_info], |
412 | ++ ) |
413 | + |
414 | + def test_computer_info_call_on_accepted(self): |
415 | + plugin = ComputerInfo(fetch_async=self.fetch_func) |
416 | +Index: landscape-client/landscape/client/package/releaseupgrader.py |
417 | +=================================================================== |
418 | +--- landscape-client.orig/landscape/client/package/releaseupgrader.py |
419 | ++++ landscape-client/landscape/client/package/releaseupgrader.py |
420 | +@@ -8,16 +8,22 @@ import tarfile |
421 | + |
422 | + from twisted.internet.defer import succeed |
423 | + |
424 | ++from landscape.client.manager.manager import FAILED |
425 | ++from landscape.client.manager.manager import SUCCEEDED |
426 | ++from landscape.client.package.reporter import find_reporter_command |
427 | ++from landscape.client.package.taskhandler import PackageTaskHandler |
428 | ++from landscape.client.package.taskhandler import ( |
429 | ++ PackageTaskHandlerConfiguration, |
430 | ++) |
431 | ++from landscape.client.package.taskhandler import run_task_handler |
432 | + from landscape.lib.config import get_bindir |
433 | +-from landscape.lib.fetch import url_to_filename, fetch_to_files |
434 | +-from landscape.lib.lsb_release import parse_lsb_release, LSB_RELEASE_FILENAME |
435 | +-from landscape.lib.gpg import gpg_verify |
436 | ++from landscape.lib.fetch import fetch_to_files |
437 | ++from landscape.lib.fetch import url_to_filename |
438 | + from landscape.lib.fs import read_text_file |
439 | +-from landscape.client.package.taskhandler import ( |
440 | +- PackageTaskHandlerConfiguration, PackageTaskHandler, run_task_handler) |
441 | ++from landscape.lib.gpg import gpg_verify |
442 | ++from landscape.lib.os_release import OS_RELEASE_FILENAME |
443 | ++from landscape.lib.os_release import parse_os_release |
444 | + from landscape.lib.twisted_util import spawn_process |
445 | +-from landscape.client.manager.manager import SUCCEEDED, FAILED |
446 | +-from landscape.client.package.reporter import find_reporter_command |
447 | + |
448 | + |
449 | + class ReleaseUpgraderConfiguration(PackageTaskHandlerConfiguration): |
450 | +@@ -37,7 +43,7 @@ class ReleaseUpgrader(PackageTaskHandler |
451 | + @cvar config_factory: The configuration class to use to build configuration |
452 | + objects to be passed to our constructor. |
453 | + @cvar queue_name: The queue we pick tasks from. |
454 | +- @cvar lsb_release_filename: The path to the LSB data on the file system. |
455 | ++ @cvar os_release_filename: The path to the OS data on the file system. |
456 | + @cvar landscape_ppa_url: The URL of the Landscape PPA, if it is present |
457 | + in the computer's sources.list it won't be commented out. |
458 | + @cvar logs_directory: Path to the directory holding the upgrade-tool logs. |
459 | +@@ -47,7 +53,7 @@ class ReleaseUpgrader(PackageTaskHandler |
460 | + |
461 | + config_factory = ReleaseUpgraderConfiguration |
462 | + queue_name = "release-upgrader" |
463 | +- lsb_release_filename = LSB_RELEASE_FILENAME |
464 | ++ os_release_filename = OS_RELEASE_FILENAME |
465 | + landscape_ppa_url = "http://ppa.launchpad.net/landscape/trunk/ubuntu/" |
466 | + logs_directory = "/var/log/dist-upgrade" |
467 | + logs_limit = 100000 # characters |
468 | +@@ -73,8 +79,8 @@ class ReleaseUpgrader(PackageTaskHandler |
469 | + """ |
470 | + target_code_name = message["code-name"] |
471 | + operation_id = message["operation-id"] |
472 | +- lsb_release_info = parse_lsb_release(self.lsb_release_filename) |
473 | +- current_code_name = lsb_release_info["code-name"] |
474 | ++ os_release_info = parse_os_release(self.os_release_filename) |
475 | ++ current_code_name = os_release_info["code-name"] |
476 | + |
477 | + if target_code_name == current_code_name: |
478 | + message = self.make_operation_result_message( |
479 | +Index: landscape-client/landscape/client/package/reporter.py |
480 | +=================================================================== |
481 | +--- landscape-client.orig/landscape/client/package/reporter.py |
482 | ++++ landscape-client/landscape/client/package/reporter.py |
483 | +@@ -12,19 +12,28 @@ import apt_pkg |
484 | + import re |
485 | + |
486 | + from twisted.internet.defer import ( |
487 | +- Deferred, succeed, inlineCallbacks, returnValue) |
488 | ++ Deferred, |
489 | ++ succeed, |
490 | ++ inlineCallbacks, |
491 | ++ returnValue, |
492 | ++) |
493 | + |
494 | + from landscape.lib import bpickle |
495 | + from landscape.lib.apt.package.store import ( |
496 | +- UnknownHashIDRequest, FakePackageStore) |
497 | ++ UnknownHashIDRequest, |
498 | ++ FakePackageStore, |
499 | ++) |
500 | + from landscape.lib.config import get_bindir |
501 | + from landscape.lib.sequenceranges import sequence_to_ranges |
502 | + from landscape.lib.twisted_util import gather_results, spawn_process |
503 | + from landscape.lib.fetch import fetch_async |
504 | + from landscape.lib.fs import touch_file, create_binary_file |
505 | +-from landscape.lib.lsb_release import parse_lsb_release, LSB_RELEASE_FILENAME |
506 | ++from landscape.lib.os_release import parse_os_release |
507 | + from landscape.client.package.taskhandler import ( |
508 | +- PackageTaskHandlerConfiguration, PackageTaskHandler, run_task_handler) |
509 | ++ PackageTaskHandlerConfiguration, |
510 | ++ PackageTaskHandler, |
511 | ++ run_task_handler, |
512 | ++) |
513 | + |
514 | + |
515 | + HASH_ID_REQUEST_TIMEOUT = 7200 |
516 | +@@ -117,7 +126,6 @@ class PackageReporter(PackageTaskHandler |
517 | + """ |
518 | + |
519 | + def fetch_it(hash_id_db_filename): |
520 | +- |
521 | + if hash_id_db_filename is None: |
522 | + # Couldn't determine which hash=>id database to fetch, |
523 | + # just ignore the failure and go on |
524 | +@@ -162,11 +170,9 @@ class PackageReporter(PackageTaskHandler |
525 | + return result |
526 | + |
527 | + def _get_hash_id_db_base_url(self): |
528 | +- |
529 | + base_url = self._config.get("package_hash_id_url") |
530 | + |
531 | + if not base_url: |
532 | +- |
533 | + if not self._config.get("url"): |
534 | + # We really have no idea where to download from |
535 | + return None |
536 | +@@ -265,12 +271,13 @@ class PackageReporter(PackageTaskHandler |
537 | + |
538 | + @return: a deferred returning (out, err, code) |
539 | + """ |
540 | +- if (self._config.force_apt_update or |
541 | +- self._apt_sources_have_changed() or |
542 | +- self._apt_update_timeout_expired(self._config.apt_update_interval) |
543 | +- ) and \ |
544 | +- not self._is_release_upgrader_running(): |
545 | +- |
546 | ++ if ( |
547 | ++ self._config.force_apt_update |
548 | ++ or self._apt_sources_have_changed() |
549 | ++ or self._apt_update_timeout_expired( |
550 | ++ self._config.apt_update_interval, |
551 | ++ ) |
552 | ++ ) and not self._is_release_upgrader_running(): |
553 | + accepted_apt_errors = ( |
554 | + "Problem renaming the file /var/cache/apt/srcpkgcache.bin", |
555 | + "Problem renaming the file /var/cache/apt/pkgcache.bin") |
556 | +@@ -413,7 +420,6 @@ class PackageReporter(PackageTaskHandler |
557 | + self._store.clear_autoremovable() |
558 | + |
559 | + def _handle_unknown_packages(self, hashes): |
560 | +- |
561 | + self._facade.ensure_channels_reloaded() |
562 | + |
563 | + hashes = set(hashes) |
564 | +@@ -634,9 +640,9 @@ class PackageReporter(PackageTaskHandler |
565 | + current_locked = set() |
566 | + current_autoremovable = set() |
567 | + current_security = set() |
568 | +- lsb = parse_lsb_release(LSB_RELEASE_FILENAME) |
569 | +- backports_archive = "{}-backports".format(lsb["code-name"]) |
570 | +- security_archive = "{}-security".format(lsb["code-name"]) |
571 | ++ os_release_info = parse_os_release() |
572 | ++ backports_archive = "{}-backports".format(os_release_info["code-name"]) |
573 | ++ security_archive = "{}-security".format(os_release_info["code-name"]) |
574 | + |
575 | + for package_version in self._facade.get_packages(): |
576 | + # Get archives from the list of PackageFiles |
577 | +Index: landscape-client/landscape/client/package/taskhandler.py |
578 | +=================================================================== |
579 | +--- landscape-client.orig/landscape/client/package/taskhandler.py |
580 | ++++ landscape-client/landscape/client/package/taskhandler.py |
581 | +@@ -1,16 +1,22 @@ |
582 | ++import logging |
583 | + import os |
584 | + import re |
585 | +-import logging |
586 | + |
587 | +-from twisted.internet.defer import succeed, Deferred, maybeDeferred |
588 | ++from twisted.internet.defer import Deferred |
589 | ++from twisted.internet.defer import maybeDeferred |
590 | ++from twisted.internet.defer import succeed |
591 | + |
592 | +-from landscape.lib.apt.package.store import PackageStore, InvalidHashIdDb |
593 | +-from landscape.lib.lock import lock_path, LockError |
594 | +-from landscape.lib.log import log_failure |
595 | +-from landscape.lib.lsb_release import LSB_RELEASE_FILENAME, parse_lsb_release |
596 | +-from landscape.client.reactor import LandscapeReactor |
597 | +-from landscape.client.deployment import Configuration, init_logging |
598 | + from landscape.client.broker.amp import RemoteBrokerConnector |
599 | ++from landscape.client.deployment import Configuration |
600 | ++from landscape.client.deployment import init_logging |
601 | ++from landscape.client.reactor import LandscapeReactor |
602 | ++from landscape.lib.apt.package.store import InvalidHashIdDb |
603 | ++from landscape.lib.apt.package.store import PackageStore |
604 | ++from landscape.lib.lock import lock_path |
605 | ++from landscape.lib.lock import LockError |
606 | ++from landscape.lib.log import log_failure |
607 | ++from landscape.lib.os_release import OS_RELEASE_FILENAME |
608 | ++from landscape.lib.os_release import parse_os_release |
609 | + |
610 | + |
611 | + class PackageTaskError(Exception): |
612 | +@@ -69,7 +75,6 @@ class LazyRemoteBroker(object): |
613 | + self._remote = None |
614 | + |
615 | + def __getattr__(self, method): |
616 | +- |
617 | + if self._remote: |
618 | + return getattr(self._remote, method) |
619 | + |
620 | +@@ -85,12 +90,11 @@ class LazyRemoteBroker(object): |
621 | + return wrapper |
622 | + |
623 | + |
624 | +-class PackageTaskHandler(object): |
625 | +- |
626 | ++class PackageTaskHandler: |
627 | + config_factory = PackageTaskHandlerConfiguration |
628 | + |
629 | + queue_name = "default" |
630 | +- lsb_release_filename = LSB_RELEASE_FILENAME |
631 | ++ os_release_filename = OS_RELEASE_FILENAME |
632 | + package_store_class = PackageStore |
633 | + |
634 | + # This file is touched after every succesful 'apt-get update' run if the |
635 | +@@ -173,7 +177,6 @@ class PackageTaskHandler(object): |
636 | + """ |
637 | + |
638 | + def use_it(hash_id_db_filename): |
639 | +- |
640 | + if hash_id_db_filename is None: |
641 | + # Couldn't determine which hash=>id database to use, |
642 | + # just ignore the failure and go on |
643 | +@@ -206,7 +209,6 @@ class PackageTaskHandler(object): |
644 | + """ |
645 | + |
646 | + def got_server_uuid(server_uuid): |
647 | +- |
648 | + warning = "Couldn't determine which hash=>id database to use: %s" |
649 | + |
650 | + if server_uuid is None: |
651 | +@@ -214,15 +216,17 @@ class PackageTaskHandler(object): |
652 | + return None |
653 | + |
654 | + try: |
655 | +- lsb_release_info = parse_lsb_release(self.lsb_release_filename) |
656 | +- except IOError as error: |
657 | ++ os_release_info = parse_os_release(self.os_release_filename) |
658 | ++ except OSError as error: |
659 | + logging.warning(warning % str(error)) |
660 | + return None |
661 | + try: |
662 | +- codename = lsb_release_info["code-name"] |
663 | ++ codename = os_release_info["code-name"] |
664 | + except KeyError: |
665 | +- logging.warning(warning % "missing code-name key in %s" % |
666 | +- self.lsb_release_filename) |
667 | ++ logging.warning( |
668 | ++ warning |
669 | ++ % f"missing code-name key in {self.os_release_filename}", |
670 | ++ ) |
671 | + return None |
672 | + |
673 | + arch = self._facade.get_arch() |
674 | +Index: landscape-client/landscape/client/package/tests/test_releaseupgrader.py |
675 | +=================================================================== |
676 | +--- landscape-client.orig/landscape/client/package/tests/test_releaseupgrader.py |
677 | ++++ landscape-client/landscape/client/package/tests/test_releaseupgrader.py |
678 | +@@ -1,20 +1,28 @@ |
679 | +-import mock |
680 | + import os |
681 | + import signal |
682 | + import tarfile |
683 | + import unittest |
684 | ++from unittest import mock |
685 | + |
686 | + from twisted.internet import reactor |
687 | +-from twisted.internet.defer import succeed, fail, Deferred |
688 | +- |
689 | ++from twisted.internet.defer import Deferred |
690 | ++from twisted.internet.defer import fail |
691 | ++from twisted.internet.defer import succeed |
692 | ++ |
693 | ++from landscape.client.manager.manager import FAILED |
694 | ++from landscape.client.manager.manager import SUCCEEDED |
695 | ++from landscape.client.package.releaseupgrader import main |
696 | ++from landscape.client.package.releaseupgrader import ReleaseUpgrader |
697 | ++from landscape.client.package.releaseupgrader import ( |
698 | ++ ReleaseUpgraderConfiguration, |
699 | ++) |
700 | ++from landscape.client.tests.helpers import BrokerServiceHelper |
701 | ++from landscape.client.tests.helpers import LandscapeTest |
702 | + from landscape.lib.apt.package.store import PackageStore |
703 | +-from landscape.lib.gpg import InvalidGPGSignature |
704 | + from landscape.lib.fetch import HTTPCodeError |
705 | +-from landscape.lib.testing import LogKeeperHelper, EnvironSaverHelper |
706 | +-from landscape.client.package.releaseupgrader import ( |
707 | +- ReleaseUpgrader, ReleaseUpgraderConfiguration, main) |
708 | +-from landscape.client.tests.helpers import LandscapeTest, BrokerServiceHelper |
709 | +-from landscape.client.manager.manager import SUCCEEDED, FAILED |
710 | ++from landscape.lib.gpg import InvalidGPGSignature |
711 | ++from landscape.lib.testing import EnvironSaverHelper |
712 | ++from landscape.lib.testing import LogKeeperHelper |
713 | + |
714 | + |
715 | + class ReleaseUpgraderConfigurationTest(unittest.TestCase): |
716 | +@@ -31,9 +39,7 @@ class ReleaseUpgraderConfigurationTest(u |
717 | + |
718 | + |
719 | + class ReleaseUpgraderTest(LandscapeTest): |
720 | +- |
721 | +- helpers = [LogKeeperHelper, |
722 | +- EnvironSaverHelper, BrokerServiceHelper] |
723 | ++ helpers = [LogKeeperHelper, EnvironSaverHelper, BrokerServiceHelper] |
724 | + |
725 | + def setUp(self): |
726 | + super(ReleaseUpgraderTest, self).setUp() |
727 | +@@ -307,7 +313,6 @@ class ReleaseUpgraderTest(LandscapeTest) |
728 | + deferred = Deferred() |
729 | + |
730 | + def do_test(): |
731 | +- |
732 | + result = self.upgrader.upgrade("karmic", 100) |
733 | + |
734 | + def check_result(ignored): |
735 | +@@ -356,10 +361,12 @@ class ReleaseUpgraderTest(LandscapeTest) |
736 | + deferred = Deferred() |
737 | + |
738 | + def do_test(): |
739 | +- |
740 | +- result = self.upgrader.upgrade("karmic", 100, |
741 | +- allow_third_party=True, |
742 | +- debug=True) |
743 | ++ result = self.upgrader.upgrade( |
744 | ++ "karmic", |
745 | ++ 100, |
746 | ++ allow_third_party=True, |
747 | ++ debug=True, |
748 | ++ ) |
749 | + |
750 | + def check_result(ignored): |
751 | + result_text = (u"=== Standard output ===\n\n" |
752 | +@@ -402,7 +409,6 @@ class ReleaseUpgraderTest(LandscapeTest) |
753 | + deferred = Deferred() |
754 | + |
755 | + def do_test(): |
756 | +- |
757 | + result = self.upgrader.upgrade("karmic", 100) |
758 | + |
759 | + def check_result(ignored): |
760 | +@@ -458,7 +464,6 @@ class ReleaseUpgraderTest(LandscapeTest) |
761 | + deferred = Deferred() |
762 | + |
763 | + def do_test(): |
764 | +- |
765 | + result = self.upgrader.upgrade("karmic", 100) |
766 | + |
767 | + def kill_child(how): |
768 | +@@ -650,8 +655,9 @@ class ReleaseUpgraderTest(LandscapeTest) |
769 | + self.upgrader.upgrade = upgrade |
770 | + self.upgrader.finish = finish |
771 | + |
772 | +- self.upgrader.lsb_release_filename = self.makeFile( |
773 | +- "DISTRIB_CODENAME=jaunty\n") |
774 | ++ self.upgrader.os_release_filename = self.makeFile( |
775 | ++ "VERSION_CODENAME=jaunty\n", |
776 | ++ ) |
777 | + |
778 | + message = {"type": "release-upgrade", |
779 | + "code-name": "karmic", |
780 | +@@ -674,8 +680,9 @@ class ReleaseUpgraderTest(LandscapeTest) |
781 | + The L{ReleaseUpgrader.handle_release_upgrade} method reports a |
782 | + failure if the system is already running the desired release. |
783 | + """ |
784 | +- self.upgrader.lsb_release_filename = self.makeFile( |
785 | +- "DISTRIB_CODENAME=karmic\n") |
786 | ++ self.upgrader.os_release_filename = self.makeFile( |
787 | ++ "VERSION_CODENAME=karmic\n", |
788 | ++ ) |
789 | + |
790 | + message = {"type": "release-upgrade", |
791 | + "code-name": "karmic", |
792 | +@@ -703,8 +710,9 @@ class ReleaseUpgraderTest(LandscapeTest) |
793 | + The L{ReleaseUpgrader.handle_release_upgrade} method reports a |
794 | + failure if any of the helper method errbacks. |
795 | + """ |
796 | +- self.upgrader.lsb_release_filename = self.makeFile( |
797 | +- "DISTRIB_CODENAME=jaunty\n") |
798 | ++ self.upgrader.os_release_filename = self.makeFile( |
799 | ++ "VERSION_CODENAME=jaunty\n", |
800 | ++ ) |
801 | + |
802 | + calls = [] |
803 | + |
804 | +Index: landscape-client/landscape/client/package/tests/test_reporter.py |
805 | +=================================================================== |
806 | +--- landscape-client.orig/landscape/client/package/tests/test_reporter.py |
807 | ++++ landscape-client/landscape/client/package/tests/test_reporter.py |
808 | +@@ -1,35 +1,61 @@ |
809 | + import locale |
810 | +-import sys |
811 | + import os |
812 | +-import time |
813 | +-import apt_pkg |
814 | +-import mock |
815 | + import shutil |
816 | + import subprocess |
817 | ++import sys |
818 | ++import time |
819 | ++from unittest import mock |
820 | + |
821 | +-from twisted.internet.defer import Deferred, succeed, fail, inlineCallbacks |
822 | ++import apt_pkg |
823 | + from twisted.internet import reactor |
824 | ++from twisted.internet.defer import Deferred |
825 | ++from twisted.internet.defer import fail |
826 | ++from twisted.internet.defer import inlineCallbacks |
827 | ++from twisted.internet.defer import succeed |
828 | + |
829 | +- |
830 | ++from landscape.client.package import reporter |
831 | ++from landscape.client.package.reporter import FakeGlobalReporter |
832 | ++from landscape.client.package.reporter import FakeReporter |
833 | ++from landscape.client.package.reporter import find_reporter_command |
834 | ++from landscape.client.package.reporter import HASH_ID_REQUEST_TIMEOUT |
835 | ++from landscape.client.package.reporter import main |
836 | ++from landscape.client.package.reporter import PackageReporter |
837 | ++from landscape.client.package.reporter import PackageReporterConfiguration |
838 | ++from landscape.client.tests.helpers import BrokerServiceHelper |
839 | ++from landscape.client.tests.helpers import LandscapeTest |
840 | + from landscape.lib import bpickle |
841 | + from landscape.lib.apt.package.facade import AptFacade |
842 | +-from landscape.lib.apt.package.store import ( |
843 | +- PackageStore, UnknownHashIDRequest, FakePackageStore) |
844 | +-from landscape.lib.apt.package.testing import ( |
845 | +- AptFacadeHelper, SimpleRepositoryHelper, |
846 | +- HASH1, HASH2, HASH3, PKGNAME1) |
847 | +-from landscape.lib.fs import create_text_file, touch_file |
848 | ++from landscape.lib.apt.package.store import FakePackageStore |
849 | ++from landscape.lib.apt.package.store import PackageStore |
850 | ++from landscape.lib.apt.package.store import UnknownHashIDRequest |
851 | ++from landscape.lib.apt.package.testing import AptFacadeHelper |
852 | ++from landscape.lib.apt.package.testing import HASH1 |
853 | ++from landscape.lib.apt.package.testing import HASH2 |
854 | ++from landscape.lib.apt.package.testing import HASH3 |
855 | ++from landscape.lib.apt.package.testing import PKGNAME1 |
856 | ++from landscape.lib.apt.package.testing import SimpleRepositoryHelper |
857 | + from landscape.lib.fetch import FetchError |
858 | +-from landscape.lib.lsb_release import parse_lsb_release, LSB_RELEASE_FILENAME |
859 | +-from landscape.lib.testing import EnvironSaverHelper, FakeReactor |
860 | +-from landscape.client.package.reporter import ( |
861 | +- PackageReporter, HASH_ID_REQUEST_TIMEOUT, main, find_reporter_command, |
862 | +- PackageReporterConfiguration, FakeGlobalReporter, FakeReporter) |
863 | +-from landscape.client.package import reporter |
864 | +-from landscape.client.tests.helpers import LandscapeTest, BrokerServiceHelper |
865 | +- |
866 | +- |
867 | +-SAMPLE_LSB_RELEASE = "DISTRIB_CODENAME=codename\n" |
868 | ++from landscape.lib.fs import create_text_file |
869 | ++from landscape.lib.fs import touch_file |
870 | ++from landscape.lib.os_release import OS_RELEASE_FILENAME |
871 | ++from landscape.lib.os_release import parse_os_release |
872 | ++from landscape.lib.testing import EnvironSaverHelper |
873 | ++from landscape.lib.testing import FakeReactor |
874 | ++ |
875 | ++ |
876 | ++SAMPLE_OS_RELEASE = """PRETTY_NAME="Ubuntu 22.04.3 LTS" |
877 | ++NAME="Ubuntu" |
878 | ++VERSION_ID="22.04" |
879 | ++VERSION="22.04.3 LTS (Jammy Jellyfish)" |
880 | ++VERSION_CODENAME=codename |
881 | ++ID=ubuntu |
882 | ++ID_LIKE=debian |
883 | ++HOME_URL="https://www.ubuntu.com/" |
884 | ++SUPPORT_URL="https://help.ubuntu.com/" |
885 | ++BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" |
886 | ++PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" |
887 | ++UBUNTU_CODENAME=codename |
888 | ++""" |
889 | + |
890 | + |
891 | + class PackageReporterConfigurationTest(LandscapeTest): |
892 | +@@ -47,7 +73,6 @@ class PackageReporterConfigurationTest(L |
893 | + |
894 | + |
895 | + class PackageReporterAptTest(LandscapeTest): |
896 | +- |
897 | + helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] |
898 | + |
899 | + Facade = AptFacade |
900 | +@@ -125,10 +150,10 @@ class PackageReporterAptTest(LandscapeTe |
901 | + return deferred.addCallback(got_result) |
902 | + |
903 | + def test_set_package_ids_with_unknown_request_id(self): |
904 | +- |
905 | +- self.store.add_task("reporter", |
906 | +- {"type": "package-ids", "ids": [123, 456], |
907 | +- "request-id": 123}) |
908 | ++ self.store.add_task( |
909 | ++ "reporter", |
910 | ++ {"type": "package-ids", "ids": [123, 456], "request-id": 123}, |
911 | ++ ) |
912 | + |
913 | + # Nothing bad should happen. |
914 | + return self.reporter.handle_tasks() |
915 | +@@ -300,7 +325,6 @@ class PackageReporterAptTest(LandscapeTe |
916 | + return_value=succeed(b"hash-ids")) |
917 | + @mock.patch("logging.info", return_value=None) |
918 | + def test_fetch_hash_id_db(self, logging_mock, mock_fetch_async): |
919 | +- |
920 | + # Assume package_hash_id_url is set |
921 | + self.config.data_path = self.makeDir() |
922 | + self.config.package_hash_id_url = "http://fake.url/path/" |
923 | +@@ -311,7 +335,7 @@ class PackageReporterAptTest(LandscapeTe |
924 | + # Fake uuid, codename and arch |
925 | + message_store = self.broker_service.message_store |
926 | + message_store.set_server_uuid("uuid") |
927 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
928 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
929 | + self.facade.set_arch("arch") |
930 | + |
931 | + # Let's say fetch_async is successful |
932 | +@@ -347,7 +371,7 @@ class PackageReporterAptTest(LandscapeTe |
933 | + # Fake uuid, codename and arch |
934 | + message_store = self.broker_service.message_store |
935 | + message_store.set_server_uuid("uuid") |
936 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
937 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
938 | + self.facade.set_arch("arch") |
939 | + |
940 | + # Let's say fetch_async is successful |
941 | +@@ -363,7 +387,6 @@ class PackageReporterAptTest(LandscapeTe |
942 | + |
943 | + @mock.patch("landscape.client.package.reporter.fetch_async") |
944 | + def test_fetch_hash_id_db_does_not_download_twice(self, mock_fetch_async): |
945 | +- |
946 | + # Let's say that the hash=>id database is already there |
947 | + self.config.package_hash_id_url = "http://fake.url/path/" |
948 | + self.config.data_path = self.makeDir() |
949 | +@@ -375,7 +398,7 @@ class PackageReporterAptTest(LandscapeTe |
950 | + # Fake uuid, codename and arch |
951 | + message_store = self.broker_service.message_store |
952 | + message_store.set_server_uuid("uuid") |
953 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
954 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
955 | + self.facade.set_arch("arch") |
956 | + |
957 | + result = self.reporter.fetch_hash_id_db() |
958 | +@@ -408,28 +431,27 @@ class PackageReporterAptTest(LandscapeTe |
959 | + |
960 | + @mock.patch("logging.warning", return_value=None) |
961 | + def test_fetch_hash_id_db_undetermined_codename(self, logging_mock): |
962 | +- |
963 | + # Fake uuid |
964 | + message_store = self.broker_service.message_store |
965 | + message_store.set_server_uuid("uuid") |
966 | + |
967 | + # Undetermined codename |
968 | +- self.reporter.lsb_release_filename = self.makeFile("Foo=bar") |
969 | ++ self.reporter.os_release_filename = self.makeFile("Foo=bar") |
970 | + |
971 | + result = self.reporter.fetch_hash_id_db() |
972 | + |
973 | + logging_mock.assert_called_once_with( |
974 | + "Couldn't determine which hash=>id database to use: " |
975 | +- "missing code-name key in %s" % self.reporter.lsb_release_filename) |
976 | ++ f"missing code-name key in {self.reporter.os_release_filename}", |
977 | ++ ) |
978 | + return result |
979 | + |
980 | + @mock.patch("logging.warning", return_value=None) |
981 | + def test_fetch_hash_id_db_undetermined_arch(self, logging_mock): |
982 | +- |
983 | + # Fake uuid and codename |
984 | + message_store = self.broker_service.message_store |
985 | + message_store.set_server_uuid("uuid") |
986 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
987 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
988 | + |
989 | + # Undetermined arch |
990 | + self.facade.set_arch("") |
991 | +@@ -454,7 +476,7 @@ class PackageReporterAptTest(LandscapeTe |
992 | + # Fake uuid, codename and arch |
993 | + message_store = self.broker_service.message_store |
994 | + message_store.set_server_uuid("uuid") |
995 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
996 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
997 | + self.facade.set_arch("arch") |
998 | + |
999 | + # Check fetch_async is called with the default url |
1000 | +@@ -475,8 +497,10 @@ class PackageReporterAptTest(LandscapeTe |
1001 | + return_value=fail(FetchError("fetch error"))) |
1002 | + @mock.patch("logging.warning", return_value=None) |
1003 | + def test_fetch_hash_id_db_with_download_error( |
1004 | +- self, logging_mock, mock_fetch_async): |
1005 | +- |
1006 | ++ self, |
1007 | ++ logging_mock, |
1008 | ++ mock_fetch_async, |
1009 | ++ ): |
1010 | + # Assume package_hash_id_url is set |
1011 | + self.config.data_path = self.makeDir() |
1012 | + self.config.package_hash_id_url = "http://fake.url/path/" |
1013 | +@@ -484,7 +508,7 @@ class PackageReporterAptTest(LandscapeTe |
1014 | + # Fake uuid, codename and arch |
1015 | + message_store = self.broker_service.message_store |
1016 | + message_store.set_server_uuid("uuid") |
1017 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
1018 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1019 | + self.facade.set_arch("arch") |
1020 | + |
1021 | + # Let's say fetch_async fails |
1022 | +@@ -508,7 +532,6 @@ class PackageReporterAptTest(LandscapeTe |
1023 | + |
1024 | + @mock.patch("logging.warning", return_value=None) |
1025 | + def test_fetch_hash_id_db_with_undetermined_url(self, logging_mock): |
1026 | +- |
1027 | + # We don't know where to fetch the hash=>id database from |
1028 | + self.config.url = None |
1029 | + self.config.package_hash_id_url = None |
1030 | +@@ -516,7 +539,7 @@ class PackageReporterAptTest(LandscapeTe |
1031 | + # Fake uuid, codename and arch |
1032 | + message_store = self.broker_service.message_store |
1033 | + message_store.set_server_uuid("uuid") |
1034 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
1035 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1036 | + self.facade.set_arch("arch") |
1037 | + |
1038 | + result = self.reporter.fetch_hash_id_db() |
1039 | +@@ -547,7 +570,7 @@ class PackageReporterAptTest(LandscapeTe |
1040 | + # Fake uuid, codename and arch |
1041 | + message_store = self.broker_service.message_store |
1042 | + message_store.set_server_uuid("uuid") |
1043 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
1044 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1045 | + self.facade.set_arch("arch") |
1046 | + |
1047 | + # Check fetch_async is called with the default url |
1048 | +@@ -1005,10 +1028,12 @@ class PackageReporterAptTest(LandscapeTe |
1049 | + """Packages versions coming from security are reported as such.""" |
1050 | + message_store = self.broker_service.message_store |
1051 | + message_store.set_accepted_types(["packages"]) |
1052 | +- lsb = parse_lsb_release(LSB_RELEASE_FILENAME) |
1053 | ++ os_release_info = parse_os_release(OS_RELEASE_FILENAME) |
1054 | + release_path = os.path.join(self.repository_dir, "Release") |
1055 | + with open(release_path, "w") as release: |
1056 | +- release.write("Suite: {}-security".format(lsb["code-name"])) |
1057 | ++ release.write( |
1058 | ++ "Suite: {}-security".format(os_release_info["code-name"]), |
1059 | ++ ) |
1060 | + |
1061 | + self.store.set_hash_ids({HASH1: 1, HASH2: 2, HASH3: 3}) |
1062 | + |
1063 | +@@ -1052,10 +1077,12 @@ class PackageReporterAptTest(LandscapeTe |
1064 | + message_store = self.broker_service.message_store |
1065 | + message_store.set_accepted_types(["packages"]) |
1066 | + |
1067 | +- lsb = parse_lsb_release(LSB_RELEASE_FILENAME) |
1068 | ++ os_release_info = parse_os_release(OS_RELEASE_FILENAME) |
1069 | + release_path = os.path.join(self.repository_dir, "Release") |
1070 | + with open(release_path, "w") as release: |
1071 | +- release.write("Suite: {}-backports".format(lsb["code-name"])) |
1072 | ++ release.write( |
1073 | ++ "Suite: {}-backports".format(os_release_info["code-name"]), |
1074 | ++ ) |
1075 | + |
1076 | + self.store.set_hash_ids({HASH1: 1, HASH2: 2, HASH3: 3}) |
1077 | + |
1078 | +@@ -1126,10 +1153,12 @@ class PackageReporterAptTest(LandscapeTe |
1079 | + os.remove(os.path.join(other_backport_dir, "Packages")) |
1080 | + self.facade.add_channel_deb_dir(other_backport_dir) |
1081 | + |
1082 | +- lsb = parse_lsb_release(LSB_RELEASE_FILENAME) |
1083 | ++ os_release_info = parse_os_release(OS_RELEASE_FILENAME) |
1084 | + official_release_path = os.path.join(self.repository_dir, "Release") |
1085 | + with open(official_release_path, "w") as release: |
1086 | +- release.write("Suite: {}-backports".format(lsb["code-name"])) |
1087 | ++ release.write( |
1088 | ++ "Suite: {}-backports".format(os_release_info["code-name"]), |
1089 | ++ ) |
1090 | + unofficial_release_path = os.path.join(other_backport_dir, "Release") |
1091 | + with open(unofficial_release_path, "w") as release: |
1092 | + release.write("Suite: my-personal-backports") |
1093 | +@@ -1341,8 +1370,8 @@ class PackageReporterAptTest(LandscapeTe |
1094 | + deferred = self.reporter.run() |
1095 | + self.reactor.advance(0) |
1096 | + with mock.patch( |
1097 | +- "landscape.client.package.taskhandler.parse_lsb_release", |
1098 | +- side_effect=lambda _: {"code-name": "codename"} |
1099 | ++ "landscape.client.package.taskhandler.parse_os_release", |
1100 | ++ side_effect=lambda _: {"code-name": "codename"}, |
1101 | + ): |
1102 | + yield deferred |
1103 | + |
1104 | +@@ -2025,7 +2054,6 @@ class PackageReporterAptTest(LandscapeTe |
1105 | + |
1106 | + |
1107 | + class GlobalPackageReporterAptTest(LandscapeTest): |
1108 | +- |
1109 | + helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] |
1110 | + |
1111 | + def setUp(self): |
1112 | +@@ -2075,7 +2103,6 @@ class GlobalPackageReporterAptTest(Lands |
1113 | + |
1114 | + |
1115 | + class FakePackageReporterTest(LandscapeTest): |
1116 | +- |
1117 | + helpers = [EnvironSaverHelper, BrokerServiceHelper] |
1118 | + |
1119 | + def setUp(self): |
1120 | +Index: landscape-client/landscape/client/package/tests/test_taskhandler.py |
1121 | +=================================================================== |
1122 | +--- landscape-client.orig/landscape/client/package/tests/test_taskhandler.py |
1123 | ++++ landscape-client/landscape/client/package/tests/test_taskhandler.py |
1124 | +@@ -1,23 +1,31 @@ |
1125 | + import os |
1126 | +-from subprocess import CalledProcessError |
1127 | +- |
1128 | +-from mock import patch, Mock, ANY |
1129 | +- |
1130 | +-from twisted.internet.defer import Deferred, fail, succeed |
1131 | ++from unittest.mock import ANY |
1132 | ++from unittest.mock import Mock |
1133 | ++from unittest.mock import patch |
1134 | ++ |
1135 | ++from twisted.internet.defer import Deferred |
1136 | ++from twisted.internet.defer import fail |
1137 | ++from twisted.internet.defer import succeed |
1138 | + |
1139 | ++from landscape.client.broker.amp import RemoteBrokerConnector |
1140 | ++from landscape.client.package.taskhandler import LazyRemoteBroker |
1141 | ++from landscape.client.package.taskhandler import PackageTaskHandler |
1142 | ++from landscape.client.package.taskhandler import ( |
1143 | ++ PackageTaskHandlerConfiguration, |
1144 | ++) |
1145 | ++from landscape.client.package.taskhandler import run_task_handler |
1146 | ++from landscape.client.tests.helpers import BrokerServiceHelper |
1147 | ++from landscape.client.tests.helpers import LandscapeTest |
1148 | + from landscape.lib.apt.package.facade import AptFacade |
1149 | +-from landscape.lib.apt.package.store import HashIdStore, PackageStore |
1150 | ++from landscape.lib.apt.package.store import HashIdStore |
1151 | ++from landscape.lib.apt.package.store import PackageStore |
1152 | + from landscape.lib.apt.package.testing import AptFacadeHelper |
1153 | + from landscape.lib.lock import lock_path |
1154 | +-from landscape.lib.testing import EnvironSaverHelper, FakeReactor |
1155 | +-from landscape.client.broker.amp import RemoteBrokerConnector |
1156 | +-from landscape.client.package.taskhandler import ( |
1157 | +- PackageTaskHandlerConfiguration, PackageTaskHandler, run_task_handler, |
1158 | +- LazyRemoteBroker) |
1159 | +-from landscape.client.tests.helpers import LandscapeTest, BrokerServiceHelper |
1160 | ++from landscape.lib.testing import EnvironSaverHelper |
1161 | ++from landscape.lib.testing import FakeReactor |
1162 | + |
1163 | + |
1164 | +-SAMPLE_LSB_RELEASE = "DISTRIB_CODENAME=codename\n" |
1165 | ++SAMPLE_OS_RELEASE = "VERSION_CODENAME=codename\n" |
1166 | + |
1167 | + |
1168 | + class PackageTaskHandlerConfigurationTest(LandscapeTest): |
1169 | +@@ -34,7 +42,6 @@ class PackageTaskHandlerConfigurationTes |
1170 | + |
1171 | + |
1172 | + class PackageTaskHandlerTest(LandscapeTest): |
1173 | +- |
1174 | + helpers = [AptFacadeHelper, EnvironSaverHelper, BrokerServiceHelper] |
1175 | + |
1176 | + def setUp(self): |
1177 | +@@ -46,7 +53,6 @@ class PackageTaskHandlerTest(LandscapeTe |
1178 | + self.store, self.facade, self.remote, self.config, self.reactor) |
1179 | + |
1180 | + def test_use_hash_id_db(self): |
1181 | +- |
1182 | + # We don't have this hash=>id mapping |
1183 | + self.assertEqual(self.store.get_hash_id(b"hash"), None) |
1184 | + |
1185 | +@@ -60,7 +66,7 @@ class PackageTaskHandlerTest(LandscapeTe |
1186 | + # Fake uuid, codename and arch |
1187 | + message_store = self.broker_service.message_store |
1188 | + message_store.set_server_uuid("uuid") |
1189 | +- self.handler.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
1190 | ++ self.handler.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1191 | + self.facade.set_arch("arch") |
1192 | + |
1193 | + # Attach the hash=>id database to our store |
1194 | +@@ -75,13 +81,12 @@ class PackageTaskHandlerTest(LandscapeTe |
1195 | + |
1196 | + @patch("logging.warning") |
1197 | + def test_use_hash_id_db_undetermined_codename(self, logging_mock): |
1198 | +- |
1199 | + # Fake uuid |
1200 | + message_store = self.broker_service.message_store |
1201 | + message_store.set_server_uuid("uuid") |
1202 | + |
1203 | + # Undetermined codename |
1204 | +- self.handler.lsb_release_filename = self.makeFile("Foo=bar") |
1205 | ++ self.handler.os_release_filename = self.makeFile("Foo=bar") |
1206 | + |
1207 | + # Go! |
1208 | + result = self.handler.use_hash_id_db() |
1209 | +@@ -89,30 +94,29 @@ class PackageTaskHandlerTest(LandscapeTe |
1210 | + # The failure should be properly logged |
1211 | + logging_mock.assert_called_with( |
1212 | + "Couldn't determine which hash=>id database to use: " |
1213 | +- "missing code-name key in %s" % self.handler.lsb_release_filename) |
1214 | ++ f"missing code-name key in {self.handler.os_release_filename}", |
1215 | ++ ) |
1216 | + |
1217 | + return result |
1218 | + |
1219 | + @patch("logging.warning") |
1220 | +- def test_use_hash_id_db_wit_non_existing_lsb_release(self, logging_mock): |
1221 | +- |
1222 | ++ def test_use_hash_id_db_with_non_existing_os_release(self, logging_mock): |
1223 | + # Fake uuid |
1224 | + message_store = self.broker_service.message_store |
1225 | + message_store.set_server_uuid("uuid") |
1226 | + |
1227 | +- # Undetermined codename |
1228 | +- self.handler.lsb_release_filename = self.makeFile() |
1229 | ++ # Undetermined os release filename |
1230 | ++ self.handler.os_release_filename = "" |
1231 | + |
1232 | + # Go! |
1233 | +- with patch("landscape.lib.lsb_release.check_output") as co_mock: |
1234 | +- co_mock.side_effect = CalledProcessError(127, "") |
1235 | +- result = self.handler.use_hash_id_db() |
1236 | ++ result = self.handler.use_hash_id_db() |
1237 | + |
1238 | + # The failure should be properly logged |
1239 | + logging_mock.assert_called_with( |
1240 | + "Couldn't determine which hash=>id database to use: " |
1241 | +- "[Errno 2] No such file or directory: '%s'" % |
1242 | +- self.handler.lsb_release_filename) |
1243 | ++ "[Errno 2] No such file or directory: " |
1244 | ++ f"'{self.handler.os_release_filename}'", |
1245 | ++ ) |
1246 | + |
1247 | + return result |
1248 | + |
1249 | +@@ -165,11 +169,10 @@ class PackageTaskHandlerTest(LandscapeTe |
1250 | + |
1251 | + @patch("logging.warning") |
1252 | + def test_use_hash_id_db_undetermined_arch(self, logging_mock): |
1253 | +- |
1254 | + # Fake uuid and codename |
1255 | + message_store = self.broker_service.message_store |
1256 | + message_store.set_server_uuid("uuid") |
1257 | +- self.handler.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
1258 | ++ self.handler.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1259 | + |
1260 | + # Undetermined arch |
1261 | + self.facade.set_arch(None) |
1262 | +@@ -185,14 +188,13 @@ class PackageTaskHandlerTest(LandscapeTe |
1263 | + return result |
1264 | + |
1265 | + def test_use_hash_id_db_database_not_found(self): |
1266 | +- |
1267 | + # Clean path, we don't have an appropriate hash=>id database |
1268 | + self.config.data_path = self.makeDir() |
1269 | + |
1270 | + # Fake uuid, codename and arch |
1271 | + message_store = self.broker_service.message_store |
1272 | + message_store.set_server_uuid("uuid") |
1273 | +- self.handler.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
1274 | ++ self.handler.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1275 | + self.facade.set_arch("arch") |
1276 | + |
1277 | + # Let's try |
1278 | +@@ -207,7 +209,6 @@ class PackageTaskHandlerTest(LandscapeTe |
1279 | + |
1280 | + @patch("logging.warning") |
1281 | + def test_use_hash_id_with_invalid_database(self, logging_mock): |
1282 | +- |
1283 | + # Let's say the appropriate database is actually garbage |
1284 | + self.config.data_path = self.makeDir() |
1285 | + os.makedirs(os.path.join(self.config.data_path, "package", "hash-id")) |
1286 | +@@ -218,7 +219,7 @@ class PackageTaskHandlerTest(LandscapeTe |
1287 | + # Fake uuid, codename and arch |
1288 | + message_store = self.broker_service.message_store |
1289 | + message_store.set_server_uuid("uuid") |
1290 | +- self.handler.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
1291 | ++ self.handler.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1292 | + self.facade.set_arch("arch") |
1293 | + |
1294 | + # Try to attach it |
1295 | +@@ -362,7 +363,6 @@ class PackageTaskHandlerTest(LandscapeTe |
1296 | + reactor_mock.run.side_effect = lambda: call_when_running[0]() |
1297 | + |
1298 | + def assert_task_handler(ignored): |
1299 | +- |
1300 | + store, facade, broker, config, reactor = handler_args |
1301 | + |
1302 | + # Verify the arguments passed to the reporter constructor. |
1303 | +@@ -405,7 +405,6 @@ class PackageTaskHandlerTest(LandscapeTe |
1304 | + return result.addCallback(assert_task_handler) |
1305 | + |
1306 | + def test_run_task_handler_when_already_locked(self): |
1307 | +- |
1308 | + lock_path(os.path.join(self.data_path, "package", "default.lock")) |
1309 | + |
1310 | + try: |
1311 | +@@ -453,7 +452,6 @@ class PackageTaskHandlerTest(LandscapeTe |
1312 | + |
1313 | + |
1314 | + class LazyRemoteBrokerTest(LandscapeTest): |
1315 | +- |
1316 | + helpers = [BrokerServiceHelper] |
1317 | + |
1318 | + def test_wb_is_lazy(self): |
1319 | +Index: landscape-client/landscape/lib/lsb_release.py |
1320 | +=================================================================== |
1321 | +--- landscape-client.orig/landscape/lib/lsb_release.py |
1322 | ++++ /dev/null |
1323 | +@@ -1,59 +0,0 @@ |
1324 | +-"""Get information from /usr/bin/lsb_release.""" |
1325 | +-import os |
1326 | +-from subprocess import CalledProcessError, check_output |
1327 | +- |
1328 | +-LSB_RELEASE = "/usr/bin/lsb_release" |
1329 | +-LSB_RELEASE_FILENAME = "/etc/lsb_release" |
1330 | +-LSB_RELEASE_FILE_KEYS = { |
1331 | +- "DISTRIB_ID": "distributor-id", |
1332 | +- "DISTRIB_DESCRIPTION": "description", |
1333 | +- "DISTRIB_RELEASE": "release", |
1334 | +- "DISTRIB_CODENAME": "code-name", |
1335 | +-} |
1336 | +- |
1337 | +- |
1338 | +-def parse_lsb_release(lsb_release_filename=None): |
1339 | +- """ |
1340 | +- Returns a C{dict} holding information about the system LSB release. |
1341 | +- Reads from C{lsb_release_filename} if it exists, else calls |
1342 | +- C{LSB_RELEASE} |
1343 | +- """ |
1344 | +- if lsb_release_filename and os.path.exists(lsb_release_filename): |
1345 | +- return parse_lsb_release_file(lsb_release_filename) |
1346 | +- |
1347 | +- with open(os.devnull, 'w') as FNULL: |
1348 | +- try: |
1349 | +- lsb_info = check_output([LSB_RELEASE, "-as"], stderr=FNULL) |
1350 | +- except (CalledProcessError, FileNotFoundError): |
1351 | +- # Fall back to reading file, even if it doesn't exist. |
1352 | +- return parse_lsb_release_file(lsb_release_filename) |
1353 | +- else: |
1354 | +- dist, desc, release, code_name, _ = lsb_info.decode().split("\n") |
1355 | +- |
1356 | +- return { |
1357 | +- "distributor-id": dist, |
1358 | +- "release": release, |
1359 | +- "code-name": code_name, |
1360 | +- "description": desc, |
1361 | +- } |
1362 | +- |
1363 | +- |
1364 | +-def parse_lsb_release_file(filename): |
1365 | +- """ |
1366 | +- Returns a C{dict} holding information about the system LSB release |
1367 | +- by attempting to parse C{filename}. |
1368 | +- |
1369 | +- @raises: A FileNotFoundError if C{filename} does not exist. |
1370 | +- """ |
1371 | +- info = {} |
1372 | +- |
1373 | +- with open(filename) as fd: |
1374 | +- for line in fd: |
1375 | +- key, value = line.split("=") |
1376 | +- |
1377 | +- if key in LSB_RELEASE_FILE_KEYS: |
1378 | +- key = LSB_RELEASE_FILE_KEYS[key.strip()] |
1379 | +- value = value.strip().strip('"') |
1380 | +- info[key] = value |
1381 | +- |
1382 | +- return info |
1383 | +Index: landscape-client/landscape/lib/os_release.py |
1384 | +=================================================================== |
1385 | +--- /dev/null |
1386 | ++++ landscape-client/landscape/lib/os_release.py |
1387 | +@@ -0,0 +1,43 @@ |
1388 | ++"""Get information from os-release.""" |
1389 | ++import os |
1390 | ++ |
1391 | ++OS_RELEASE_FILENAME = "/etc/os-release" |
1392 | ++OS_RELEASE_FILENAME_FALLBACK = "/usr/lib/os-release" |
1393 | ++OS_RELEASE_FILE_KEYS = { |
1394 | ++ "NAME": "distributor-id", |
1395 | ++ "PRETTY_NAME": "description", |
1396 | ++ "VERSION_ID": "release", |
1397 | ++ "VERSION_CODENAME": "code-name", |
1398 | ++} |
1399 | ++ |
1400 | ++ |
1401 | ++def parse_os_release(os_release_filename=None): |
1402 | ++ """ |
1403 | ++ Returns a C{dict} holding information about the system LSB release |
1404 | ++ by attempting to parse C{os_release_filename} if specified. If no |
1405 | ++ filename is provided /etc/os-release will be used or |
1406 | ++ /usr/lib/os-release as a fallback as indicated in os-release |
1407 | ++ at Freedesktop.org |
1408 | ++ |
1409 | ++ @raises: A FileNotFoundError if C{filename} does not exist. |
1410 | ++ """ |
1411 | ++ info = {} |
1412 | ++ |
1413 | ++ if os_release_filename is None: |
1414 | ++ os_release_filename = OS_RELEASE_FILENAME |
1415 | ++ if not os.path.exists(os_release_filename) or not os.access( |
1416 | ++ os_release_filename, |
1417 | ++ os.R_OK, |
1418 | ++ ): |
1419 | ++ os_release_filename = OS_RELEASE_FILENAME_FALLBACK |
1420 | ++ |
1421 | ++ with open(os_release_filename) as fd: |
1422 | ++ for line in fd: |
1423 | ++ key, value = line.split("=") |
1424 | ++ |
1425 | ++ if key in OS_RELEASE_FILE_KEYS: |
1426 | ++ key = OS_RELEASE_FILE_KEYS[key.strip()] |
1427 | ++ value = value.strip().strip('"') |
1428 | ++ info[key] = value |
1429 | ++ |
1430 | ++ return info |
1431 | +Index: landscape-client/landscape/lib/tests/test_lsb_release.py |
1432 | +=================================================================== |
1433 | +--- landscape-client.orig/landscape/lib/tests/test_lsb_release.py |
1434 | ++++ /dev/null |
1435 | +@@ -1,75 +0,0 @@ |
1436 | +-import unittest |
1437 | +-from subprocess import CalledProcessError |
1438 | +-from unittest import mock |
1439 | +- |
1440 | +-from landscape.lib import testing |
1441 | +-from landscape.lib.lsb_release import parse_lsb_release |
1442 | +- |
1443 | +- |
1444 | +-class LsbReleaseTest(testing.FSTestCase, unittest.TestCase): |
1445 | +- |
1446 | +- def test_parse_lsb_release(self): |
1447 | +- with mock.patch("landscape.lib.lsb_release.check_output") as co_mock: |
1448 | +- co_mock.return_value = (b"Ubuntu\nUbuntu 22.04.1 LTS\n22.04\njammy" |
1449 | +- b"\n") |
1450 | +- lsb_release = parse_lsb_release() |
1451 | +- |
1452 | +- self.assertEqual(lsb_release, |
1453 | +- {"distributor-id": "Ubuntu", |
1454 | +- "description": "Ubuntu 22.04.1 LTS", |
1455 | +- "release": "22.04", |
1456 | +- "code-name": "jammy"}) |
1457 | +- |
1458 | +- def test_parse_lsb_release_debian(self): |
1459 | +- with mock.patch("landscape.lib.lsb_release.check_output") as co_mock: |
1460 | +- co_mock.return_value = (b"Debian\nDebian GNU/Linux 11 (bullseye)\n" |
1461 | +- b"11\nbullseye\n") |
1462 | +- lsb_release = parse_lsb_release() |
1463 | +- |
1464 | +- self.assertEqual(lsb_release, |
1465 | +- {"distributor-id": "Debian", |
1466 | +- "description": "Debian GNU/Linux 11 (bullseye)", |
1467 | +- "release": "11", |
1468 | +- "code-name": "bullseye"}) |
1469 | +- |
1470 | +- def test_parse_lsb_release_file(self): |
1471 | +- """ |
1472 | +- L{parse_lsb_release} returns a C{dict} holding information from |
1473 | +- the given LSB release file. |
1474 | +- """ |
1475 | +- lsb_release_filename = self.makeFile("DISTRIB_ID=Ubuntu\n" |
1476 | +- "DISTRIB_RELEASE=6.06\n" |
1477 | +- "DISTRIB_CODENAME=dapper\n" |
1478 | +- "DISTRIB_DESCRIPTION=" |
1479 | +- "\"Ubuntu 6.06.1 LTS\"\n") |
1480 | +- |
1481 | +- with mock.patch("landscape.lib.lsb_release.check_output") as co_mock: |
1482 | +- co_mock.side_effect = CalledProcessError(127, "") |
1483 | +- lsb_release = parse_lsb_release(lsb_release_filename) |
1484 | +- |
1485 | +- self.assertEqual(lsb_release, |
1486 | +- {"distributor-id": "Ubuntu", |
1487 | +- "description": "Ubuntu 6.06.1 LTS", |
1488 | +- "release": "6.06", |
1489 | +- "code-name": "dapper"}) |
1490 | +- |
1491 | +- def test_parse_lsb_release_file_with_missing_or_extra_fields(self): |
1492 | +- """ |
1493 | +- L{parse_lsb_release} ignores lines not matching the map of |
1494 | +- known keys, and returns only keys with an actual value. |
1495 | +- """ |
1496 | +- lsb_release_filename = self.makeFile("DISTRIB_ID=Ubuntu\n" |
1497 | +- "FOO=Bar\n") |
1498 | +- |
1499 | +- with mock.patch("landscape.lib.lsb_release.check_output") as co_mock: |
1500 | +- co_mock.side_effect = CalledProcessError(127, "") |
1501 | +- lsb_release = parse_lsb_release(lsb_release_filename) |
1502 | +- |
1503 | +- self.assertEqual(lsb_release, {"distributor-id": "Ubuntu"}) |
1504 | +- |
1505 | +- def test_parse_lsb_release_file_not_found(self): |
1506 | +- with mock.patch("landscape.lib.lsb_release.check_output") as co_mock: |
1507 | +- co_mock.side_effect = CalledProcessError(127, "") |
1508 | +- |
1509 | +- self.assertRaises(FileNotFoundError, parse_lsb_release, |
1510 | +- "TheresNoWayThisFileExists") |
1511 | +Index: landscape-client/landscape/lib/tests/test_os_release.py |
1512 | +=================================================================== |
1513 | +--- /dev/null |
1514 | ++++ landscape-client/landscape/lib/tests/test_os_release.py |
1515 | +@@ -0,0 +1,137 @@ |
1516 | ++import unittest |
1517 | ++from unittest import mock |
1518 | ++ |
1519 | ++from landscape.lib import testing |
1520 | ++from landscape.lib.os_release import parse_os_release |
1521 | ++ |
1522 | ++SAMPLE_OS_RELEASE = """PRETTY_NAME="Ubuntu 22.04.3 LTS" |
1523 | ++NAME="Ubuntu" |
1524 | ++VERSION_ID="22.04" |
1525 | ++VERSION="22.04.3 LTS (Jammy Jellyfish)" |
1526 | ++VERSION_CODENAME=codename |
1527 | ++ID=ubuntu |
1528 | ++ID_LIKE=debian |
1529 | ++HOME_URL="https://www.ubuntu.com/" |
1530 | ++SUPPORT_URL="https://help.ubuntu.com/" |
1531 | ++BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" |
1532 | ++PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" |
1533 | ++UBUNTU_CODENAME=codename |
1534 | ++""" |
1535 | ++ |
1536 | ++ |
1537 | ++class OsReleaseTest(testing.FSTestCase, unittest.TestCase): |
1538 | ++ def test_parse_os_release(self): |
1539 | ++ """ |
1540 | ++ L{parse_os_release} ignores lines not matching the map of |
1541 | ++ known keys, and returns only keys with an actual value. By |
1542 | ++ default it reads from OS_RELEASE_FILENAME if no other path |
1543 | ++ is provided. |
1544 | ++ """ |
1545 | ++ |
1546 | ++ os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1547 | ++ |
1548 | ++ with mock.patch( |
1549 | ++ "landscape.lib.os_release.OS_RELEASE_FILENAME", |
1550 | ++ new=os_release_filename, |
1551 | ++ ): |
1552 | ++ os_release = parse_os_release() |
1553 | ++ |
1554 | ++ self.assertEqual( |
1555 | ++ os_release, |
1556 | ++ { |
1557 | ++ "code-name": "codename", |
1558 | ++ "description": "Ubuntu 22.04.3 LTS", |
1559 | ++ "distributor-id": "Ubuntu", |
1560 | ++ "release": "22.04", |
1561 | ++ }, |
1562 | ++ ) |
1563 | ++ |
1564 | ++ def test_parse_os_release_no_etc(self): |
1565 | ++ """ |
1566 | ++ L{parse_os_release} ignores lines not matching the map of |
1567 | ++ known keys, and returns only keys with an actual value. By |
1568 | ++ default it reads from OS_RELEASE_FILENAME if no other path |
1569 | ++ is provided and it should read from SAMPLE_OS_RELEASE_FALLBACK |
1570 | ++ path if there OS_RELEASE_FILENAME doesn't exists. |
1571 | ++ """ |
1572 | ++ |
1573 | ++ os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1574 | ++ |
1575 | ++ with mock.patch("os.path.exists") as co_mock: |
1576 | ++ co_mock.return_value = False |
1577 | ++ |
1578 | ++ with mock.patch( |
1579 | ++ "landscape.lib.os_release.OS_RELEASE_FILENAME_FALLBACK", |
1580 | ++ new=os_release_filename, |
1581 | ++ ): |
1582 | ++ os_release = parse_os_release() |
1583 | ++ |
1584 | ++ self.assertEqual( |
1585 | ++ os_release, |
1586 | ++ { |
1587 | ++ "code-name": "codename", |
1588 | ++ "description": "Ubuntu 22.04.3 LTS", |
1589 | ++ "distributor-id": "Ubuntu", |
1590 | ++ "release": "22.04", |
1591 | ++ }, |
1592 | ++ ) |
1593 | ++ |
1594 | ++ def test_parse_os_release_no_perms(self): |
1595 | ++ """ |
1596 | ++ L{parse_os_release} ignores lines not matching the map of |
1597 | ++ known keys, and returns only keys with an actual value. By |
1598 | ++ default it reads from OS_RELEASE_FILENAME if no other path |
1599 | ++ is provided and it should read from SAMPLE_OS_RELEASE_FALLBACK |
1600 | ++ path if there OS_RELEASE_FILENAME is not readable. |
1601 | ++ """ |
1602 | ++ |
1603 | ++ os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1604 | ++ |
1605 | ++ with mock.patch("os.access") as co_mock: |
1606 | ++ co_mock.return_value = False |
1607 | ++ |
1608 | ++ with mock.patch( |
1609 | ++ "landscape.lib.os_release.OS_RELEASE_FILENAME_FALLBACK", |
1610 | ++ new=os_release_filename, |
1611 | ++ ): |
1612 | ++ os_release = parse_os_release() |
1613 | ++ |
1614 | ++ self.assertEqual( |
1615 | ++ os_release, |
1616 | ++ { |
1617 | ++ "code-name": "codename", |
1618 | ++ "description": "Ubuntu 22.04.3 LTS", |
1619 | ++ "distributor-id": "Ubuntu", |
1620 | ++ "release": "22.04", |
1621 | ++ }, |
1622 | ++ ) |
1623 | ++ |
1624 | ++ def test_parse_os_release_with_file(self): |
1625 | ++ """ |
1626 | ++ L{parse_os_release} returns a C{dict} holding information from |
1627 | ++ the given OS release file. |
1628 | ++ """ |
1629 | ++ os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1630 | ++ os_release = parse_os_release(os_release_filename) |
1631 | ++ |
1632 | ++ self.assertEqual( |
1633 | ++ os_release, |
1634 | ++ { |
1635 | ++ "distributor-id": "Ubuntu", |
1636 | ++ "description": "Ubuntu 22.04.3 LTS", |
1637 | ++ "release": "22.04", |
1638 | ++ "code-name": "codename", |
1639 | ++ }, |
1640 | ++ ) |
1641 | ++ |
1642 | ++ def test_parse_os_release_with_file_not_found(self): |
1643 | ++ """ |
1644 | ++ L{parse_os_release} should fail with FileNotFound |
1645 | ++ if given OS release file doesn't exists. |
1646 | ++ """ |
1647 | ++ |
1648 | ++ self.assertRaises( |
1649 | ++ FileNotFoundError, |
1650 | ++ parse_os_release, |
1651 | ++ "TheresNoWayThisFileExists", |
1652 | ++ ) |
1653 | diff --git a/debian/patches/series b/debian/patches/series |
1654 | index 6c61034..8b5cb58 100644 |
1655 | --- a/debian/patches/series |
1656 | +++ b/debian/patches/series |
1657 | @@ -1,2 +1,4 @@ |
1658 | 0001-start-service-during-config.patch |
1659 | add-non-filtered-interfaces-to-the-api-response.patch |
1660 | +package-reporter-high-cpu.patch |
1661 | +parse-lsb-output.patch |
Simple d/changelog fix, but a bigger question inline.