Merge ~jansdhillon/ubuntu/+source/landscape-client:lp2099283-package-reporter-fixes-focal into ubuntu/+source/landscape-client:ubuntu/focal-devel
- Git
- lp:~jansdhillon/ubuntu/+source/landscape-client
- lp2099283-package-reporter-fixes-focal
- Merge into ubuntu/focal-devel
Proposed by
Jan-Yaeger Dhillon
Status: | Needs review |
---|---|
Proposed branch: | ~jansdhillon/ubuntu/+source/landscape-client:lp2099283-package-reporter-fixes-focal |
Merge into: | ubuntu/+source/landscape-client:ubuntu/focal-devel |
Diff against target: |
1578 lines (+1550/-0) 4 files modified
debian/changelog (+7/-0) debian/patches/package-reporter-high-cpu.patch (+110/-0) debian/patches/parse-lsb-output.patch (+1431/-0) debian/patches/series (+2/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
David McLain | Pending | ||
Ubuntu Sponsors | Pending | ||
Canonical Server Reporter | Pending | ||
git-ubuntu import | Pending | ||
Review via email:
|
Commit message
Description of the change
To post a comment you must log in.
- f3a75a7... by jansdhillon <email address hidden>
-
fix lp question number to avoid confusion with a separate bug
- bef4e42... by jansdhillon <email address hidden>
-
update LP numbers in patches/changelog
Unmerged commits
- bef4e42... by jansdhillon <email address hidden>
-
update LP numbers in patches/changelog
- f3a75a7... by jansdhillon <email address hidden>
-
fix lp question number to avoid confusion with a separate bug
- 934a18a... 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 86246ae..ce8c1af 100644 |
3 | --- a/debian/changelog |
4 | +++ b/debian/changelog |
5 | @@ -1,3 +1,10 @@ |
6 | +landscape-client (23.02-0ubuntu1~20.04.6) focal; 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:42 -0700 |
12 | + |
13 | landscape-client (23.02-0ubuntu1~20.04.5) focal; urgency=medium |
14 | |
15 | * d/p/0002-fix-locale-error.path: revert previous fix for #1827857, as |
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..b852e19 |
135 | --- /dev/null |
136 | +++ b/debian/patches/parse-lsb-output.patch |
137 | @@ -0,0 +1,1431 @@ |
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 | +@@ -11,19 +11,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 | +@@ -116,7 +125,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 | +@@ -161,11 +169,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 | +@@ -264,12 +270,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 | +@@ -412,7 +419,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 | +@@ -633,9 +639,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 | +@@ -46,7 +46,6 @@ class PackageReporterConfigurationTest(L |
809 | + |
810 | + |
811 | + class PackageReporterAptTest(LandscapeTest): |
812 | +- |
813 | + helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] |
814 | + |
815 | + Facade = AptFacade |
816 | +@@ -124,10 +123,10 @@ class PackageReporterAptTest(LandscapeTe |
817 | + return deferred.addCallback(got_result) |
818 | + |
819 | + def test_set_package_ids_with_unknown_request_id(self): |
820 | +- |
821 | +- self.store.add_task("reporter", |
822 | +- {"type": "package-ids", "ids": [123, 456], |
823 | +- "request-id": 123}) |
824 | ++ self.store.add_task( |
825 | ++ "reporter", |
826 | ++ {"type": "package-ids", "ids": [123, 456], "request-id": 123}, |
827 | ++ ) |
828 | + |
829 | + # Nothing bad should happen. |
830 | + return self.reporter.handle_tasks() |
831 | +@@ -299,7 +298,6 @@ class PackageReporterAptTest(LandscapeTe |
832 | + return_value=succeed(b"hash-ids")) |
833 | + @mock.patch("logging.info", return_value=None) |
834 | + def test_fetch_hash_id_db(self, logging_mock, mock_fetch_async): |
835 | +- |
836 | + # Assume package_hash_id_url is set |
837 | + self.config.data_path = self.makeDir() |
838 | + self.config.package_hash_id_url = "http://fake.url/path/" |
839 | +@@ -310,7 +308,7 @@ class PackageReporterAptTest(LandscapeTe |
840 | + # Fake uuid, codename and arch |
841 | + message_store = self.broker_service.message_store |
842 | + message_store.set_server_uuid("uuid") |
843 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
844 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
845 | + self.facade.set_arch("arch") |
846 | + |
847 | + # Let's say fetch_async is successful |
848 | +@@ -346,7 +344,7 @@ class PackageReporterAptTest(LandscapeTe |
849 | + # Fake uuid, codename and arch |
850 | + message_store = self.broker_service.message_store |
851 | + message_store.set_server_uuid("uuid") |
852 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
853 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
854 | + self.facade.set_arch("arch") |
855 | + |
856 | + # Let's say fetch_async is successful |
857 | +@@ -362,7 +360,6 @@ class PackageReporterAptTest(LandscapeTe |
858 | + |
859 | + @mock.patch("landscape.client.package.reporter.fetch_async") |
860 | + def test_fetch_hash_id_db_does_not_download_twice(self, mock_fetch_async): |
861 | +- |
862 | + # Let's say that the hash=>id database is already there |
863 | + self.config.package_hash_id_url = "http://fake.url/path/" |
864 | + self.config.data_path = self.makeDir() |
865 | +@@ -374,7 +371,7 @@ class PackageReporterAptTest(LandscapeTe |
866 | + # Fake uuid, codename and arch |
867 | + message_store = self.broker_service.message_store |
868 | + message_store.set_server_uuid("uuid") |
869 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
870 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
871 | + self.facade.set_arch("arch") |
872 | + |
873 | + result = self.reporter.fetch_hash_id_db() |
874 | +@@ -407,28 +404,27 @@ class PackageReporterAptTest(LandscapeTe |
875 | + |
876 | + @mock.patch("logging.warning", return_value=None) |
877 | + def test_fetch_hash_id_db_undetermined_codename(self, logging_mock): |
878 | +- |
879 | + # Fake uuid |
880 | + message_store = self.broker_service.message_store |
881 | + message_store.set_server_uuid("uuid") |
882 | + |
883 | + # Undetermined codename |
884 | +- self.reporter.lsb_release_filename = self.makeFile("Foo=bar") |
885 | ++ self.reporter.os_release_filename = self.makeFile("Foo=bar") |
886 | + |
887 | + result = self.reporter.fetch_hash_id_db() |
888 | + |
889 | + logging_mock.assert_called_once_with( |
890 | + "Couldn't determine which hash=>id database to use: " |
891 | +- "missing code-name key in %s" % self.reporter.lsb_release_filename) |
892 | ++ f"missing code-name key in {self.reporter.os_release_filename}", |
893 | ++ ) |
894 | + return result |
895 | + |
896 | + @mock.patch("logging.warning", return_value=None) |
897 | + def test_fetch_hash_id_db_undetermined_arch(self, logging_mock): |
898 | +- |
899 | + # Fake uuid and codename |
900 | + message_store = self.broker_service.message_store |
901 | + message_store.set_server_uuid("uuid") |
902 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
903 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
904 | + |
905 | + # Undetermined arch |
906 | + self.facade.set_arch("") |
907 | +@@ -453,7 +449,7 @@ class PackageReporterAptTest(LandscapeTe |
908 | + # Fake uuid, codename and arch |
909 | + message_store = self.broker_service.message_store |
910 | + message_store.set_server_uuid("uuid") |
911 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
912 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
913 | + self.facade.set_arch("arch") |
914 | + |
915 | + # Check fetch_async is called with the default url |
916 | +@@ -474,8 +470,10 @@ class PackageReporterAptTest(LandscapeTe |
917 | + return_value=fail(FetchError("fetch error"))) |
918 | + @mock.patch("logging.warning", return_value=None) |
919 | + def test_fetch_hash_id_db_with_download_error( |
920 | +- self, logging_mock, mock_fetch_async): |
921 | +- |
922 | ++ self, |
923 | ++ logging_mock, |
924 | ++ mock_fetch_async, |
925 | ++ ): |
926 | + # Assume package_hash_id_url is set |
927 | + self.config.data_path = self.makeDir() |
928 | + self.config.package_hash_id_url = "http://fake.url/path/" |
929 | +@@ -483,7 +481,7 @@ class PackageReporterAptTest(LandscapeTe |
930 | + # Fake uuid, codename and arch |
931 | + message_store = self.broker_service.message_store |
932 | + message_store.set_server_uuid("uuid") |
933 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
934 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
935 | + self.facade.set_arch("arch") |
936 | + |
937 | + # Let's say fetch_async fails |
938 | +@@ -507,7 +505,6 @@ class PackageReporterAptTest(LandscapeTe |
939 | + |
940 | + @mock.patch("logging.warning", return_value=None) |
941 | + def test_fetch_hash_id_db_with_undetermined_url(self, logging_mock): |
942 | +- |
943 | + # We don't know where to fetch the hash=>id database from |
944 | + self.config.url = None |
945 | + self.config.package_hash_id_url = None |
946 | +@@ -515,7 +512,7 @@ class PackageReporterAptTest(LandscapeTe |
947 | + # Fake uuid, codename and arch |
948 | + message_store = self.broker_service.message_store |
949 | + message_store.set_server_uuid("uuid") |
950 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
951 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
952 | + self.facade.set_arch("arch") |
953 | + |
954 | + result = self.reporter.fetch_hash_id_db() |
955 | +@@ -546,7 +543,7 @@ class PackageReporterAptTest(LandscapeTe |
956 | + # Fake uuid, codename and arch |
957 | + message_store = self.broker_service.message_store |
958 | + message_store.set_server_uuid("uuid") |
959 | +- self.reporter.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
960 | ++ self.reporter.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
961 | + self.facade.set_arch("arch") |
962 | + |
963 | + # Check fetch_async is called with the default url |
964 | +@@ -1004,10 +1001,12 @@ class PackageReporterAptTest(LandscapeTe |
965 | + """Packages versions coming from security are reported as such.""" |
966 | + message_store = self.broker_service.message_store |
967 | + message_store.set_accepted_types(["packages"]) |
968 | +- lsb = parse_lsb_release(LSB_RELEASE_FILENAME) |
969 | ++ os_release_info = parse_os_release(OS_RELEASE_FILENAME) |
970 | + release_path = os.path.join(self.repository_dir, "Release") |
971 | + with open(release_path, "w") as release: |
972 | +- release.write("Suite: {}-security".format(lsb["code-name"])) |
973 | ++ release.write( |
974 | ++ "Suite: {}-security".format(os_release_info["code-name"]), |
975 | ++ ) |
976 | + |
977 | + self.store.set_hash_ids({HASH1: 1, HASH2: 2, HASH3: 3}) |
978 | + |
979 | +@@ -1051,10 +1050,12 @@ class PackageReporterAptTest(LandscapeTe |
980 | + message_store = self.broker_service.message_store |
981 | + message_store.set_accepted_types(["packages"]) |
982 | + |
983 | +- lsb = parse_lsb_release(LSB_RELEASE_FILENAME) |
984 | ++ os_release_info = parse_os_release(OS_RELEASE_FILENAME) |
985 | + release_path = os.path.join(self.repository_dir, "Release") |
986 | + with open(release_path, "w") as release: |
987 | +- release.write("Suite: {}-backports".format(lsb["code-name"])) |
988 | ++ release.write( |
989 | ++ "Suite: {}-backports".format(os_release_info["code-name"]), |
990 | ++ ) |
991 | + |
992 | + self.store.set_hash_ids({HASH1: 1, HASH2: 2, HASH3: 3}) |
993 | + |
994 | +@@ -1125,10 +1126,12 @@ class PackageReporterAptTest(LandscapeTe |
995 | + os.remove(os.path.join(other_backport_dir, "Packages")) |
996 | + self.facade.add_channel_deb_dir(other_backport_dir) |
997 | + |
998 | +- lsb = parse_lsb_release(LSB_RELEASE_FILENAME) |
999 | ++ os_release_info = parse_os_release(OS_RELEASE_FILENAME) |
1000 | + official_release_path = os.path.join(self.repository_dir, "Release") |
1001 | + with open(official_release_path, "w") as release: |
1002 | +- release.write("Suite: {}-backports".format(lsb["code-name"])) |
1003 | ++ release.write( |
1004 | ++ "Suite: {}-backports".format(os_release_info["code-name"]), |
1005 | ++ ) |
1006 | + unofficial_release_path = os.path.join(other_backport_dir, "Release") |
1007 | + with open(unofficial_release_path, "w") as release: |
1008 | + release.write("Suite: my-personal-backports") |
1009 | +@@ -1315,8 +1318,8 @@ class PackageReporterAptTest(LandscapeTe |
1010 | + deferred = self.reporter.run() |
1011 | + self.reactor.advance(0) |
1012 | + with mock.patch( |
1013 | +- "landscape.client.package.taskhandler.parse_lsb_release", |
1014 | +- side_effect=lambda _: {"code-name": "codename"} |
1015 | ++ "landscape.client.package.taskhandler.parse_os_release", |
1016 | ++ side_effect=lambda _: {"code-name": "codename"}, |
1017 | + ): |
1018 | + yield deferred |
1019 | + |
1020 | +@@ -1999,7 +2002,6 @@ class PackageReporterAptTest(LandscapeTe |
1021 | + |
1022 | + |
1023 | + class GlobalPackageReporterAptTest(LandscapeTest): |
1024 | +- |
1025 | + helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] |
1026 | + |
1027 | + def setUp(self): |
1028 | +@@ -2049,7 +2051,6 @@ class GlobalPackageReporterAptTest(Lands |
1029 | + |
1030 | + |
1031 | + class FakePackageReporterTest(LandscapeTest): |
1032 | +- |
1033 | + helpers = [EnvironSaverHelper, BrokerServiceHelper] |
1034 | + |
1035 | + def setUp(self): |
1036 | +Index: landscape-client/landscape/client/package/tests/test_taskhandler.py |
1037 | +=================================================================== |
1038 | +--- landscape-client.orig/landscape/client/package/tests/test_taskhandler.py |
1039 | ++++ landscape-client/landscape/client/package/tests/test_taskhandler.py |
1040 | +@@ -1,23 +1,31 @@ |
1041 | + import os |
1042 | +-from subprocess import CalledProcessError |
1043 | +- |
1044 | +-from mock import patch, Mock, ANY |
1045 | +- |
1046 | +-from twisted.internet.defer import Deferred, fail, succeed |
1047 | ++from unittest.mock import ANY |
1048 | ++from unittest.mock import Mock |
1049 | ++from unittest.mock import patch |
1050 | ++ |
1051 | ++from twisted.internet.defer import Deferred |
1052 | ++from twisted.internet.defer import fail |
1053 | ++from twisted.internet.defer import succeed |
1054 | + |
1055 | ++from landscape.client.broker.amp import RemoteBrokerConnector |
1056 | ++from landscape.client.package.taskhandler import LazyRemoteBroker |
1057 | ++from landscape.client.package.taskhandler import PackageTaskHandler |
1058 | ++from landscape.client.package.taskhandler import ( |
1059 | ++ PackageTaskHandlerConfiguration, |
1060 | ++) |
1061 | ++from landscape.client.package.taskhandler import run_task_handler |
1062 | ++from landscape.client.tests.helpers import BrokerServiceHelper |
1063 | ++from landscape.client.tests.helpers import LandscapeTest |
1064 | + from landscape.lib.apt.package.facade import AptFacade |
1065 | +-from landscape.lib.apt.package.store import HashIdStore, PackageStore |
1066 | ++from landscape.lib.apt.package.store import HashIdStore |
1067 | ++from landscape.lib.apt.package.store import PackageStore |
1068 | + from landscape.lib.apt.package.testing import AptFacadeHelper |
1069 | + from landscape.lib.lock import lock_path |
1070 | +-from landscape.lib.testing import EnvironSaverHelper, FakeReactor |
1071 | +-from landscape.client.broker.amp import RemoteBrokerConnector |
1072 | +-from landscape.client.package.taskhandler import ( |
1073 | +- PackageTaskHandlerConfiguration, PackageTaskHandler, run_task_handler, |
1074 | +- LazyRemoteBroker) |
1075 | +-from landscape.client.tests.helpers import LandscapeTest, BrokerServiceHelper |
1076 | ++from landscape.lib.testing import EnvironSaverHelper |
1077 | ++from landscape.lib.testing import FakeReactor |
1078 | + |
1079 | + |
1080 | +-SAMPLE_LSB_RELEASE = "DISTRIB_CODENAME=codename\n" |
1081 | ++SAMPLE_OS_RELEASE = "VERSION_CODENAME=codename\n" |
1082 | + |
1083 | + |
1084 | + class PackageTaskHandlerConfigurationTest(LandscapeTest): |
1085 | +@@ -34,7 +42,6 @@ class PackageTaskHandlerConfigurationTes |
1086 | + |
1087 | + |
1088 | + class PackageTaskHandlerTest(LandscapeTest): |
1089 | +- |
1090 | + helpers = [AptFacadeHelper, EnvironSaverHelper, BrokerServiceHelper] |
1091 | + |
1092 | + def setUp(self): |
1093 | +@@ -46,7 +53,6 @@ class PackageTaskHandlerTest(LandscapeTe |
1094 | + self.store, self.facade, self.remote, self.config, self.reactor) |
1095 | + |
1096 | + def test_use_hash_id_db(self): |
1097 | +- |
1098 | + # We don't have this hash=>id mapping |
1099 | + self.assertEqual(self.store.get_hash_id(b"hash"), None) |
1100 | + |
1101 | +@@ -60,7 +66,7 @@ class PackageTaskHandlerTest(LandscapeTe |
1102 | + # Fake uuid, codename and arch |
1103 | + message_store = self.broker_service.message_store |
1104 | + message_store.set_server_uuid("uuid") |
1105 | +- self.handler.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
1106 | ++ self.handler.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1107 | + self.facade.set_arch("arch") |
1108 | + |
1109 | + # Attach the hash=>id database to our store |
1110 | +@@ -75,13 +81,12 @@ class PackageTaskHandlerTest(LandscapeTe |
1111 | + |
1112 | + @patch("logging.warning") |
1113 | + def test_use_hash_id_db_undetermined_codename(self, logging_mock): |
1114 | +- |
1115 | + # Fake uuid |
1116 | + message_store = self.broker_service.message_store |
1117 | + message_store.set_server_uuid("uuid") |
1118 | + |
1119 | + # Undetermined codename |
1120 | +- self.handler.lsb_release_filename = self.makeFile("Foo=bar") |
1121 | ++ self.handler.os_release_filename = self.makeFile("Foo=bar") |
1122 | + |
1123 | + # Go! |
1124 | + result = self.handler.use_hash_id_db() |
1125 | +@@ -89,30 +94,29 @@ class PackageTaskHandlerTest(LandscapeTe |
1126 | + # The failure should be properly logged |
1127 | + logging_mock.assert_called_with( |
1128 | + "Couldn't determine which hash=>id database to use: " |
1129 | +- "missing code-name key in %s" % self.handler.lsb_release_filename) |
1130 | ++ f"missing code-name key in {self.handler.os_release_filename}", |
1131 | ++ ) |
1132 | + |
1133 | + return result |
1134 | + |
1135 | + @patch("logging.warning") |
1136 | +- def test_use_hash_id_db_wit_non_existing_lsb_release(self, logging_mock): |
1137 | +- |
1138 | ++ def test_use_hash_id_db_with_non_existing_os_release(self, logging_mock): |
1139 | + # Fake uuid |
1140 | + message_store = self.broker_service.message_store |
1141 | + message_store.set_server_uuid("uuid") |
1142 | + |
1143 | +- # Undetermined codename |
1144 | +- self.handler.lsb_release_filename = self.makeFile() |
1145 | ++ # Undetermined os release filename |
1146 | ++ self.handler.os_release_filename = "" |
1147 | + |
1148 | + # Go! |
1149 | +- with patch("landscape.lib.lsb_release.check_output") as co_mock: |
1150 | +- co_mock.side_effect = CalledProcessError(127, "") |
1151 | +- result = self.handler.use_hash_id_db() |
1152 | ++ result = self.handler.use_hash_id_db() |
1153 | + |
1154 | + # The failure should be properly logged |
1155 | + logging_mock.assert_called_with( |
1156 | + "Couldn't determine which hash=>id database to use: " |
1157 | +- "[Errno 2] No such file or directory: '%s'" % |
1158 | +- self.handler.lsb_release_filename) |
1159 | ++ "[Errno 2] No such file or directory: " |
1160 | ++ f"'{self.handler.os_release_filename}'", |
1161 | ++ ) |
1162 | + |
1163 | + return result |
1164 | + |
1165 | +@@ -165,11 +169,10 @@ class PackageTaskHandlerTest(LandscapeTe |
1166 | + |
1167 | + @patch("logging.warning") |
1168 | + def test_use_hash_id_db_undetermined_arch(self, logging_mock): |
1169 | +- |
1170 | + # Fake uuid and codename |
1171 | + message_store = self.broker_service.message_store |
1172 | + message_store.set_server_uuid("uuid") |
1173 | +- self.handler.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
1174 | ++ self.handler.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1175 | + |
1176 | + # Undetermined arch |
1177 | + self.facade.set_arch(None) |
1178 | +@@ -185,14 +188,13 @@ class PackageTaskHandlerTest(LandscapeTe |
1179 | + return result |
1180 | + |
1181 | + def test_use_hash_id_db_database_not_found(self): |
1182 | +- |
1183 | + # Clean path, we don't have an appropriate hash=>id database |
1184 | + self.config.data_path = self.makeDir() |
1185 | + |
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 | + # Let's try |
1194 | +@@ -207,7 +209,6 @@ class PackageTaskHandlerTest(LandscapeTe |
1195 | + |
1196 | + @patch("logging.warning") |
1197 | + def test_use_hash_id_with_invalid_database(self, logging_mock): |
1198 | +- |
1199 | + # Let's say the appropriate database is actually garbage |
1200 | + self.config.data_path = self.makeDir() |
1201 | + os.makedirs(os.path.join(self.config.data_path, "package", "hash-id")) |
1202 | +@@ -218,7 +219,7 @@ class PackageTaskHandlerTest(LandscapeTe |
1203 | + # Fake uuid, codename and arch |
1204 | + message_store = self.broker_service.message_store |
1205 | + message_store.set_server_uuid("uuid") |
1206 | +- self.handler.lsb_release_filename = self.makeFile(SAMPLE_LSB_RELEASE) |
1207 | ++ self.handler.os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1208 | + self.facade.set_arch("arch") |
1209 | + |
1210 | + # Try to attach it |
1211 | +@@ -362,7 +363,6 @@ class PackageTaskHandlerTest(LandscapeTe |
1212 | + reactor_mock.run.side_effect = lambda: call_when_running[0]() |
1213 | + |
1214 | + def assert_task_handler(ignored): |
1215 | +- |
1216 | + store, facade, broker, config, reactor = handler_args |
1217 | + |
1218 | + # Verify the arguments passed to the reporter constructor. |
1219 | +@@ -405,7 +405,6 @@ class PackageTaskHandlerTest(LandscapeTe |
1220 | + return result.addCallback(assert_task_handler) |
1221 | + |
1222 | + def test_run_task_handler_when_already_locked(self): |
1223 | +- |
1224 | + lock_path(os.path.join(self.data_path, "package", "default.lock")) |
1225 | + |
1226 | + try: |
1227 | +@@ -453,7 +452,6 @@ class PackageTaskHandlerTest(LandscapeTe |
1228 | + |
1229 | + |
1230 | + class LazyRemoteBrokerTest(LandscapeTest): |
1231 | +- |
1232 | + helpers = [BrokerServiceHelper] |
1233 | + |
1234 | + def test_wb_is_lazy(self): |
1235 | +Index: landscape-client/landscape/lib/lsb_release.py |
1236 | +=================================================================== |
1237 | +--- landscape-client.orig/landscape/lib/lsb_release.py |
1238 | ++++ /dev/null |
1239 | +@@ -1,59 +0,0 @@ |
1240 | +-"""Get information from /usr/bin/lsb_release.""" |
1241 | +-import os |
1242 | +-from subprocess import CalledProcessError, check_output |
1243 | +- |
1244 | +-LSB_RELEASE = "/usr/bin/lsb_release" |
1245 | +-LSB_RELEASE_FILENAME = "/etc/lsb_release" |
1246 | +-LSB_RELEASE_FILE_KEYS = { |
1247 | +- "DISTRIB_ID": "distributor-id", |
1248 | +- "DISTRIB_DESCRIPTION": "description", |
1249 | +- "DISTRIB_RELEASE": "release", |
1250 | +- "DISTRIB_CODENAME": "code-name", |
1251 | +-} |
1252 | +- |
1253 | +- |
1254 | +-def parse_lsb_release(lsb_release_filename=None): |
1255 | +- """ |
1256 | +- Returns a C{dict} holding information about the system LSB release. |
1257 | +- Reads from C{lsb_release_filename} if it exists, else calls |
1258 | +- C{LSB_RELEASE} |
1259 | +- """ |
1260 | +- if lsb_release_filename and os.path.exists(lsb_release_filename): |
1261 | +- return parse_lsb_release_file(lsb_release_filename) |
1262 | +- |
1263 | +- with open(os.devnull, 'w') as FNULL: |
1264 | +- try: |
1265 | +- lsb_info = check_output([LSB_RELEASE, "-as"], stderr=FNULL) |
1266 | +- except (CalledProcessError, FileNotFoundError): |
1267 | +- # Fall back to reading file, even if it doesn't exist. |
1268 | +- return parse_lsb_release_file(lsb_release_filename) |
1269 | +- else: |
1270 | +- dist, desc, release, code_name, _ = lsb_info.decode().split("\n") |
1271 | +- |
1272 | +- return { |
1273 | +- "distributor-id": dist, |
1274 | +- "release": release, |
1275 | +- "code-name": code_name, |
1276 | +- "description": desc, |
1277 | +- } |
1278 | +- |
1279 | +- |
1280 | +-def parse_lsb_release_file(filename): |
1281 | +- """ |
1282 | +- Returns a C{dict} holding information about the system LSB release |
1283 | +- by attempting to parse C{filename}. |
1284 | +- |
1285 | +- @raises: A FileNotFoundError if C{filename} does not exist. |
1286 | +- """ |
1287 | +- info = {} |
1288 | +- |
1289 | +- with open(filename) as fd: |
1290 | +- for line in fd: |
1291 | +- key, value = line.split("=") |
1292 | +- |
1293 | +- if key in LSB_RELEASE_FILE_KEYS: |
1294 | +- key = LSB_RELEASE_FILE_KEYS[key.strip()] |
1295 | +- value = value.strip().strip('"') |
1296 | +- info[key] = value |
1297 | +- |
1298 | +- return info |
1299 | +Index: landscape-client/landscape/lib/os_release.py |
1300 | +=================================================================== |
1301 | +--- /dev/null |
1302 | ++++ landscape-client/landscape/lib/os_release.py |
1303 | +@@ -0,0 +1,43 @@ |
1304 | ++"""Get information from os-release.""" |
1305 | ++import os |
1306 | ++ |
1307 | ++OS_RELEASE_FILENAME = "/etc/os-release" |
1308 | ++OS_RELEASE_FILENAME_FALLBACK = "/usr/lib/os-release" |
1309 | ++OS_RELEASE_FILE_KEYS = { |
1310 | ++ "NAME": "distributor-id", |
1311 | ++ "PRETTY_NAME": "description", |
1312 | ++ "VERSION_ID": "release", |
1313 | ++ "VERSION_CODENAME": "code-name", |
1314 | ++} |
1315 | ++ |
1316 | ++ |
1317 | ++def parse_os_release(os_release_filename=None): |
1318 | ++ """ |
1319 | ++ Returns a C{dict} holding information about the system LSB release |
1320 | ++ by attempting to parse C{os_release_filename} if specified. If no |
1321 | ++ filename is provided /etc/os-release will be used or |
1322 | ++ /usr/lib/os-release as a fallback as indicated in os-release |
1323 | ++ at Freedesktop.org |
1324 | ++ |
1325 | ++ @raises: A FileNotFoundError if C{filename} does not exist. |
1326 | ++ """ |
1327 | ++ info = {} |
1328 | ++ |
1329 | ++ if os_release_filename is None: |
1330 | ++ os_release_filename = OS_RELEASE_FILENAME |
1331 | ++ if not os.path.exists(os_release_filename) or not os.access( |
1332 | ++ os_release_filename, |
1333 | ++ os.R_OK, |
1334 | ++ ): |
1335 | ++ os_release_filename = OS_RELEASE_FILENAME_FALLBACK |
1336 | ++ |
1337 | ++ with open(os_release_filename) as fd: |
1338 | ++ for line in fd: |
1339 | ++ key, value = line.split("=") |
1340 | ++ |
1341 | ++ if key in OS_RELEASE_FILE_KEYS: |
1342 | ++ key = OS_RELEASE_FILE_KEYS[key.strip()] |
1343 | ++ value = value.strip().strip('"') |
1344 | ++ info[key] = value |
1345 | ++ |
1346 | ++ return info |
1347 | +Index: landscape-client/landscape/lib/tests/test_lsb_release.py |
1348 | +=================================================================== |
1349 | +--- landscape-client.orig/landscape/lib/tests/test_lsb_release.py |
1350 | ++++ /dev/null |
1351 | +@@ -1,75 +0,0 @@ |
1352 | +-import unittest |
1353 | +-from subprocess import CalledProcessError |
1354 | +-from unittest import mock |
1355 | +- |
1356 | +-from landscape.lib import testing |
1357 | +-from landscape.lib.lsb_release import parse_lsb_release |
1358 | +- |
1359 | +- |
1360 | +-class LsbReleaseTest(testing.FSTestCase, unittest.TestCase): |
1361 | +- |
1362 | +- def test_parse_lsb_release(self): |
1363 | +- with mock.patch("landscape.lib.lsb_release.check_output") as co_mock: |
1364 | +- co_mock.return_value = (b"Ubuntu\nUbuntu 22.04.1 LTS\n22.04\njammy" |
1365 | +- b"\n") |
1366 | +- lsb_release = parse_lsb_release() |
1367 | +- |
1368 | +- self.assertEqual(lsb_release, |
1369 | +- {"distributor-id": "Ubuntu", |
1370 | +- "description": "Ubuntu 22.04.1 LTS", |
1371 | +- "release": "22.04", |
1372 | +- "code-name": "jammy"}) |
1373 | +- |
1374 | +- def test_parse_lsb_release_debian(self): |
1375 | +- with mock.patch("landscape.lib.lsb_release.check_output") as co_mock: |
1376 | +- co_mock.return_value = (b"Debian\nDebian GNU/Linux 11 (bullseye)\n" |
1377 | +- b"11\nbullseye\n") |
1378 | +- lsb_release = parse_lsb_release() |
1379 | +- |
1380 | +- self.assertEqual(lsb_release, |
1381 | +- {"distributor-id": "Debian", |
1382 | +- "description": "Debian GNU/Linux 11 (bullseye)", |
1383 | +- "release": "11", |
1384 | +- "code-name": "bullseye"}) |
1385 | +- |
1386 | +- def test_parse_lsb_release_file(self): |
1387 | +- """ |
1388 | +- L{parse_lsb_release} returns a C{dict} holding information from |
1389 | +- the given LSB release file. |
1390 | +- """ |
1391 | +- lsb_release_filename = self.makeFile("DISTRIB_ID=Ubuntu\n" |
1392 | +- "DISTRIB_RELEASE=6.06\n" |
1393 | +- "DISTRIB_CODENAME=dapper\n" |
1394 | +- "DISTRIB_DESCRIPTION=" |
1395 | +- "\"Ubuntu 6.06.1 LTS\"\n") |
1396 | +- |
1397 | +- with mock.patch("landscape.lib.lsb_release.check_output") as co_mock: |
1398 | +- co_mock.side_effect = CalledProcessError(127, "") |
1399 | +- lsb_release = parse_lsb_release(lsb_release_filename) |
1400 | +- |
1401 | +- self.assertEqual(lsb_release, |
1402 | +- {"distributor-id": "Ubuntu", |
1403 | +- "description": "Ubuntu 6.06.1 LTS", |
1404 | +- "release": "6.06", |
1405 | +- "code-name": "dapper"}) |
1406 | +- |
1407 | +- def test_parse_lsb_release_file_with_missing_or_extra_fields(self): |
1408 | +- """ |
1409 | +- L{parse_lsb_release} ignores lines not matching the map of |
1410 | +- known keys, and returns only keys with an actual value. |
1411 | +- """ |
1412 | +- lsb_release_filename = self.makeFile("DISTRIB_ID=Ubuntu\n" |
1413 | +- "FOO=Bar\n") |
1414 | +- |
1415 | +- with mock.patch("landscape.lib.lsb_release.check_output") as co_mock: |
1416 | +- co_mock.side_effect = CalledProcessError(127, "") |
1417 | +- lsb_release = parse_lsb_release(lsb_release_filename) |
1418 | +- |
1419 | +- self.assertEqual(lsb_release, {"distributor-id": "Ubuntu"}) |
1420 | +- |
1421 | +- def test_parse_lsb_release_file_not_found(self): |
1422 | +- with mock.patch("landscape.lib.lsb_release.check_output") as co_mock: |
1423 | +- co_mock.side_effect = CalledProcessError(127, "") |
1424 | +- |
1425 | +- self.assertRaises(FileNotFoundError, parse_lsb_release, |
1426 | +- "TheresNoWayThisFileExists") |
1427 | +Index: landscape-client/landscape/lib/tests/test_os_release.py |
1428 | +=================================================================== |
1429 | +--- /dev/null |
1430 | ++++ landscape-client/landscape/lib/tests/test_os_release.py |
1431 | +@@ -0,0 +1,137 @@ |
1432 | ++import unittest |
1433 | ++from unittest import mock |
1434 | ++ |
1435 | ++from landscape.lib import testing |
1436 | ++from landscape.lib.os_release import parse_os_release |
1437 | ++ |
1438 | ++SAMPLE_OS_RELEASE = """PRETTY_NAME="Ubuntu 22.04.3 LTS" |
1439 | ++NAME="Ubuntu" |
1440 | ++VERSION_ID="22.04" |
1441 | ++VERSION="22.04.3 LTS (Jammy Jellyfish)" |
1442 | ++VERSION_CODENAME=codename |
1443 | ++ID=ubuntu |
1444 | ++ID_LIKE=debian |
1445 | ++HOME_URL="https://www.ubuntu.com/" |
1446 | ++SUPPORT_URL="https://help.ubuntu.com/" |
1447 | ++BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" |
1448 | ++PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" |
1449 | ++UBUNTU_CODENAME=codename |
1450 | ++""" |
1451 | ++ |
1452 | ++ |
1453 | ++class OsReleaseTest(testing.FSTestCase, unittest.TestCase): |
1454 | ++ def test_parse_os_release(self): |
1455 | ++ """ |
1456 | ++ L{parse_os_release} ignores lines not matching the map of |
1457 | ++ known keys, and returns only keys with an actual value. By |
1458 | ++ default it reads from OS_RELEASE_FILENAME if no other path |
1459 | ++ is provided. |
1460 | ++ """ |
1461 | ++ |
1462 | ++ os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1463 | ++ |
1464 | ++ with mock.patch( |
1465 | ++ "landscape.lib.os_release.OS_RELEASE_FILENAME", |
1466 | ++ new=os_release_filename, |
1467 | ++ ): |
1468 | ++ os_release = parse_os_release() |
1469 | ++ |
1470 | ++ self.assertEqual( |
1471 | ++ os_release, |
1472 | ++ { |
1473 | ++ "code-name": "codename", |
1474 | ++ "description": "Ubuntu 22.04.3 LTS", |
1475 | ++ "distributor-id": "Ubuntu", |
1476 | ++ "release": "22.04", |
1477 | ++ }, |
1478 | ++ ) |
1479 | ++ |
1480 | ++ def test_parse_os_release_no_etc(self): |
1481 | ++ """ |
1482 | ++ L{parse_os_release} ignores lines not matching the map of |
1483 | ++ known keys, and returns only keys with an actual value. By |
1484 | ++ default it reads from OS_RELEASE_FILENAME if no other path |
1485 | ++ is provided and it should read from SAMPLE_OS_RELEASE_FALLBACK |
1486 | ++ path if there OS_RELEASE_FILENAME doesn't exists. |
1487 | ++ """ |
1488 | ++ |
1489 | ++ os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1490 | ++ |
1491 | ++ with mock.patch("os.path.exists") as co_mock: |
1492 | ++ co_mock.return_value = False |
1493 | ++ |
1494 | ++ with mock.patch( |
1495 | ++ "landscape.lib.os_release.OS_RELEASE_FILENAME_FALLBACK", |
1496 | ++ new=os_release_filename, |
1497 | ++ ): |
1498 | ++ os_release = parse_os_release() |
1499 | ++ |
1500 | ++ self.assertEqual( |
1501 | ++ os_release, |
1502 | ++ { |
1503 | ++ "code-name": "codename", |
1504 | ++ "description": "Ubuntu 22.04.3 LTS", |
1505 | ++ "distributor-id": "Ubuntu", |
1506 | ++ "release": "22.04", |
1507 | ++ }, |
1508 | ++ ) |
1509 | ++ |
1510 | ++ def test_parse_os_release_no_perms(self): |
1511 | ++ """ |
1512 | ++ L{parse_os_release} ignores lines not matching the map of |
1513 | ++ known keys, and returns only keys with an actual value. By |
1514 | ++ default it reads from OS_RELEASE_FILENAME if no other path |
1515 | ++ is provided and it should read from SAMPLE_OS_RELEASE_FALLBACK |
1516 | ++ path if there OS_RELEASE_FILENAME is not readable. |
1517 | ++ """ |
1518 | ++ |
1519 | ++ os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1520 | ++ |
1521 | ++ with mock.patch("os.access") as co_mock: |
1522 | ++ co_mock.return_value = False |
1523 | ++ |
1524 | ++ with mock.patch( |
1525 | ++ "landscape.lib.os_release.OS_RELEASE_FILENAME_FALLBACK", |
1526 | ++ new=os_release_filename, |
1527 | ++ ): |
1528 | ++ os_release = parse_os_release() |
1529 | ++ |
1530 | ++ self.assertEqual( |
1531 | ++ os_release, |
1532 | ++ { |
1533 | ++ "code-name": "codename", |
1534 | ++ "description": "Ubuntu 22.04.3 LTS", |
1535 | ++ "distributor-id": "Ubuntu", |
1536 | ++ "release": "22.04", |
1537 | ++ }, |
1538 | ++ ) |
1539 | ++ |
1540 | ++ def test_parse_os_release_with_file(self): |
1541 | ++ """ |
1542 | ++ L{parse_os_release} returns a C{dict} holding information from |
1543 | ++ the given OS release file. |
1544 | ++ """ |
1545 | ++ os_release_filename = self.makeFile(SAMPLE_OS_RELEASE) |
1546 | ++ os_release = parse_os_release(os_release_filename) |
1547 | ++ |
1548 | ++ self.assertEqual( |
1549 | ++ os_release, |
1550 | ++ { |
1551 | ++ "distributor-id": "Ubuntu", |
1552 | ++ "description": "Ubuntu 22.04.3 LTS", |
1553 | ++ "release": "22.04", |
1554 | ++ "code-name": "codename", |
1555 | ++ }, |
1556 | ++ ) |
1557 | ++ |
1558 | ++ def test_parse_os_release_with_file_not_found(self): |
1559 | ++ """ |
1560 | ++ L{parse_os_release} should fail with FileNotFound |
1561 | ++ if given OS release file doesn't exists. |
1562 | ++ """ |
1563 | ++ |
1564 | ++ self.assertRaises( |
1565 | ++ FileNotFoundError, |
1566 | ++ parse_os_release, |
1567 | ++ "TheresNoWayThisFileExists", |
1568 | ++ ) |
1569 | diff --git a/debian/patches/series b/debian/patches/series |
1570 | index 1d2050f..f0e339b 100644 |
1571 | --- a/debian/patches/series |
1572 | +++ b/debian/patches/series |
1573 | @@ -1,3 +1,5 @@ |
1574 | 0001-start-service-during-config.patch |
1575 | add-non-filtered-interfaces-to-the-api-response.patch |
1576 | 0002-fix-locale-error.patch |
1577 | +package-reporter-high-cpu.patch |
1578 | +parse-lsb-output.patch |