Merge ~vultaire/charm-sysconfig:lp1841809 into charm-sysconfig:master
- Git
- lp:~vultaire/charm-sysconfig
- lp1841809
- Merge into master
Status: | Merged |
---|---|
Approved by: | Jeremy Lounder |
Approved revision: | d732035d8aff8c3a74d715fa9e4f88a435f0422c |
Merged at revision: | cb5240074d70796e3c06b257756b00013a0ffda4 |
Proposed branch: | ~vultaire/charm-sysconfig:lp1841809 |
Merge into: | charm-sysconfig:master |
Diff against target: |
473 lines (+171/-83) 6 files modified
src/config.yaml (+6/-0) src/lib/lib_sysconfig.py (+48/-18) src/reactive/sysconfig.py (+9/-1) src/templates/etc.systemd.resolved.conf.j2 (+27/-0) src/tests/functional/test_deploy.py (+40/-30) src/tests/unit/test_lib.py (+41/-34) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jeremy Lounder (community) | Approve | ||
Alvaro Uria (community) | Approve | ||
Review via email: mp+378630@code.launchpad.net |
Commit message
Adding resolved-cache-mode config option
Description of the change
* Added the feature
* Added tests for the feature
* Removed 'cosmic' option from test_deploy.py since it adds significant test overhead time (10 minutes on certain tests), due to there not being a cosmic charm available to deploy with.
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Alvaro Uria (aluria) wrote : | # |
* typo: Default value on config.yaml
* nitpick: code style
* Behavior around updating the resolved.conf file (systemd-resolved service should be restarted instead of notifying that the unit needs a reboot)
Paul Goins (vultaire) wrote : | # |
Noting my responses. Will make changes.
Paul Goins (vultaire) : | # |
Paul Goins (vultaire) wrote : | # |
@aluria: Care to give this another look? I think I've addressed everything.
Alvaro Uria (aluria) wrote : | # |
Hey Paul. I've added a comment inline, but I will repeat it here.
* checksum verification can be achieved via any_file_changed [1], helper from the reactive framework, A list of file(s) needs to be passed.
* "render" already checks if new content is going to be written, or it will skip the write
* As a suggestion of a diff approach, set_flag(
And think the above will make more sense in the comment inline.
Other than this, the change (and tests) look great.
1. https:/
Paul Goins (vultaire) wrote : | # |
Most comments addressed. Did not use set_flag(...) since cpufrequtils also handles a service restart in the same way (in the lib module rather than via a flag-based reactive hook); if we change it for resolved, I think we should change it for cpufrequtils as well - and that starts to work its way out of scope in my opinion.
Alvaro Uria (aluria) wrote : | # |
Hey Paul, Thank you! I've added a minor suggestion for readability purpose, but I won't block it for merging :)
Alvaro Uria (aluria) wrote : | # |
Hey Paul. I suggested the use of a list for [ONE_FILE], but failed to check the change. Good catch. I also failed to mention that "import hashlib" may not be needed anymore. Other than that, all looks good to me.
Jeremy Lounder (jldev) : | # |
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Change successfully merged at revision cb5240074d70796
Preview Diff
1 | diff --git a/src/config.yaml b/src/config.yaml | |||
2 | index bd60238..4b93f3c 100644 | |||
3 | --- a/src/config.yaml | |||
4 | +++ b/src/config.yaml | |||
5 | @@ -122,3 +122,9 @@ options: | |||
6 | 122 | Recommended option when Bios control power is set to the OS. | 122 | Recommended option when Bios control power is set to the OS. |
7 | 123 | - 'performance' | 123 | - 'performance' |
8 | 124 | - 'powersave' | 124 | - 'powersave' |
9 | 125 | resolved-cache-mode: | ||
10 | 126 | type: string | ||
11 | 127 | default: "" | ||
12 | 128 | description: | | ||
13 | 129 | Sets the "Cache" configuration value in /etc/systemd/resolved.conf. Valid | ||
14 | 130 | values are '' (system default), 'yes', 'no', and 'no-negative'. | ||
15 | diff --git a/src/lib/lib_sysconfig.py b/src/lib/lib_sysconfig.py | |||
16 | index 51bf049..73e1311 100644 | |||
17 | --- a/src/lib/lib_sysconfig.py | |||
18 | +++ b/src/lib/lib_sysconfig.py | |||
19 | @@ -2,7 +2,6 @@ | |||
20 | 2 | 2 | ||
21 | 3 | Manage grub, systemd, coufrequtils and kernel version configuration. | 3 | Manage grub, systemd, coufrequtils and kernel version configuration. |
22 | 4 | """ | 4 | """ |
23 | 5 | |||
24 | 6 | import os | 5 | import os |
25 | 7 | import subprocess | 6 | import subprocess |
26 | 8 | from datetime import datetime, timedelta, timezone | 7 | from datetime import datetime, timedelta, timezone |
27 | @@ -11,15 +10,18 @@ from charmhelpers.contrib.openstack.utils import config_flags_parser | |||
28 | 11 | from charmhelpers.core import hookenv, host, unitdata | 10 | from charmhelpers.core import hookenv, host, unitdata |
29 | 12 | from charmhelpers.core.templating import render | 11 | from charmhelpers.core.templating import render |
30 | 13 | from charmhelpers.fetch import apt_install, apt_update | 12 | from charmhelpers.fetch import apt_install, apt_update |
31 | 13 | from charms.reactive.helpers import any_file_changed | ||
32 | 14 | 14 | ||
33 | 15 | GRUB_DEFAULT = 'Advanced options for Ubuntu>Ubuntu, with Linux {}' | 15 | GRUB_DEFAULT = 'Advanced options for Ubuntu>Ubuntu, with Linux {}' |
34 | 16 | CPUFREQUTILS_TMPL = 'cpufrequtils.j2' | 16 | CPUFREQUTILS_TMPL = 'cpufrequtils.j2' |
35 | 17 | GRUB_CONF_TMPL = 'grub.j2' | 17 | GRUB_CONF_TMPL = 'grub.j2' |
36 | 18 | SYSTEMD_SYSTEM_TMPL = 'etc.systemd.system.conf.j2' | 18 | SYSTEMD_SYSTEM_TMPL = 'etc.systemd.system.conf.j2' |
37 | 19 | SYSTEMD_RESOLVED_TMPL = 'etc.systemd.resolved.conf.j2' | ||
38 | 19 | 20 | ||
39 | 20 | CPUFREQUTILS = '/etc/default/cpufrequtils' | 21 | CPUFREQUTILS = '/etc/default/cpufrequtils' |
40 | 21 | GRUB_CONF = '/etc/default/grub.d/90-sysconfig.cfg' | 22 | GRUB_CONF = '/etc/default/grub.d/90-sysconfig.cfg' |
41 | 22 | SYSTEMD_SYSTEM = '/etc/systemd/system.conf' | 23 | SYSTEMD_SYSTEM = '/etc/systemd/system.conf' |
42 | 24 | SYSTEMD_RESOLVED = '/etc/systemd/resolved.conf' | ||
43 | 23 | KERNEL = 'kernel' | 25 | KERNEL = 'kernel' |
44 | 24 | 26 | ||
45 | 25 | 27 | ||
46 | @@ -155,25 +157,30 @@ class SysConfigHelper: | |||
47 | 155 | 157 | ||
48 | 156 | @property | 158 | @property |
49 | 157 | def systemd_config_flags(self): | 159 | def systemd_config_flags(self): |
51 | 158 | """Return grub-config-flags config option.""" | 160 | """Return systemd-config-flags config option.""" |
52 | 159 | return parse_config_flags(self.charm_config['systemd-config-flags']) | 161 | return parse_config_flags(self.charm_config['systemd-config-flags']) |
53 | 160 | 162 | ||
54 | 161 | @property | 163 | @property |
55 | 162 | def kernel_version(self): | 164 | def kernel_version(self): |
57 | 163 | """Return grub-config-flags config option.""" | 165 | """Return kernel-version config option.""" |
58 | 164 | return self.charm_config['kernel-version'] | 166 | return self.charm_config['kernel-version'] |
59 | 165 | 167 | ||
60 | 166 | @property | 168 | @property |
61 | 167 | def update_grub(self): | 169 | def update_grub(self): |
63 | 168 | """Return grub-config-flags config option.""" | 170 | """Return update-grub config option.""" |
64 | 169 | return self.charm_config['update-grub'] | 171 | return self.charm_config['update-grub'] |
65 | 170 | 172 | ||
66 | 171 | @property | 173 | @property |
67 | 172 | def governor(self): | 174 | def governor(self): |
69 | 173 | """Return grub-config-flags config option.""" | 175 | """Return governor config option.""" |
70 | 174 | return self.charm_config['governor'] | 176 | return self.charm_config['governor'] |
71 | 175 | 177 | ||
72 | 176 | @property | 178 | @property |
73 | 179 | def resolved_cache_mode(self): | ||
74 | 180 | """Return resolved-cache-mode config option.""" | ||
75 | 181 | return self.charm_config['resolved-cache-mode'] | ||
76 | 182 | |||
77 | 183 | @property | ||
78 | 177 | def config_flags(self): | 184 | def config_flags(self): |
79 | 178 | """Return parsed config-flags into dict. | 185 | """Return parsed config-flags into dict. |
80 | 179 | 186 | ||
81 | @@ -187,9 +194,14 @@ class SysConfigHelper: | |||
82 | 187 | 194 | ||
83 | 188 | def _render_boot_resource(self, source, target, context): | 195 | def _render_boot_resource(self, source, target, context): |
84 | 189 | """Render the template and set the resource as changed.""" | 196 | """Render the template and set the resource as changed.""" |
86 | 190 | render(source=source, templates_dir='templates', target=target, context=context) | 197 | self._render_resource(source, target, context) |
87 | 191 | self.boot_resources.set_resource(target) | 198 | self.boot_resources.set_resource(target) |
88 | 192 | 199 | ||
89 | 200 | @staticmethod | ||
90 | 201 | def _render_resource(source, target, context): | ||
91 | 202 | """Render the template.""" | ||
92 | 203 | render(source=source, templates_dir='templates', target=target, context=context) | ||
93 | 204 | |||
94 | 193 | def _is_kernel_already_running(self): | 205 | def _is_kernel_already_running(self): |
95 | 194 | """Check if the kernel version required by charm config is equal to kernel running.""" | 206 | """Check if the kernel version required by charm config is equal to kernel running.""" |
96 | 195 | configured = self.kernel_version | 207 | configured = self.kernel_version |
97 | @@ -208,18 +220,15 @@ class SysConfigHelper: | |||
98 | 208 | """Validate config parameters.""" | 220 | """Validate config parameters.""" |
99 | 209 | valid = True | 221 | valid = True |
100 | 210 | 222 | ||
113 | 211 | if self.reservation not in ['off', 'isolcpus', 'affinity']: | 223 | for config_key, value, valid_values in ( |
114 | 212 | hookenv.log('reservation not valid. Possible values: ["off", "isolcpus", "affinity"]', hookenv.DEBUG) | 224 | ('reservation', self.reservation, ['off', 'isolcpus', 'affinity']), |
115 | 213 | valid = False | 225 | ('raid-autodetection', self.raid_autodetection, ['', 'noautodetect', 'partitionable']), |
116 | 214 | 226 | ('governor', self.governor, ['', 'powersave', 'performance']), | |
117 | 215 | if self.raid_autodetection not in ['', 'noautodetect', 'partitionable']: | 227 | ('resolved-cache-mode', self.resolved_cache_mode, ['', 'yes', 'no', 'no-negative']), |
118 | 216 | hookenv.log('raid-autodetection not valid. ' | 228 | ): |
119 | 217 | 'Possible values: ["off", "noautodetect", "partitionable"]', hookenv.DEBUG) | 229 | if value not in valid_values: |
120 | 218 | valid = False | 230 | hookenv.log('{} not valid. Possible values: {}'.format(config_key, repr(valid_values)), hookenv.DEBUG) |
121 | 219 | 231 | valid = False | |
110 | 220 | if self.governor not in ['', 'powersave', 'performance']: | ||
111 | 221 | hookenv.log('governor not valid. Possible values: ["", "powersave", "performance"]', hookenv.DEBUG) | ||
112 | 222 | valid = False | ||
122 | 223 | 232 | ||
123 | 224 | return valid | 233 | return valid |
124 | 225 | 234 | ||
125 | @@ -272,6 +281,14 @@ class SysConfigHelper: | |||
126 | 272 | self._render_boot_resource(SYSTEMD_SYSTEM_TMPL, SYSTEMD_SYSTEM, context) | 281 | self._render_boot_resource(SYSTEMD_SYSTEM_TMPL, SYSTEMD_SYSTEM, context) |
127 | 273 | hookenv.log('systemd configuration updated') | 282 | hookenv.log('systemd configuration updated') |
128 | 274 | 283 | ||
129 | 284 | def update_systemd_resolved(self): | ||
130 | 285 | """Update /etc/systemd/resolved.conf according to charm configuration.""" | ||
131 | 286 | context = {} | ||
132 | 287 | if self.resolved_cache_mode: | ||
133 | 288 | context['cache'] = self.resolved_cache_mode | ||
134 | 289 | self._update_systemd_resolved(context) | ||
135 | 290 | hookenv.log('systemd-resolved configuration updated') | ||
136 | 291 | |||
137 | 275 | def install_configured_kernel(self): | 292 | def install_configured_kernel(self): |
138 | 276 | """Install kernel as given by the kernel-version config option. | 293 | """Install kernel as given by the kernel-version config option. |
139 | 277 | 294 | ||
140 | @@ -341,6 +358,19 @@ class SysConfigHelper: | |||
141 | 341 | hookenv.DEBUG | 358 | hookenv.DEBUG |
142 | 342 | ) | 359 | ) |
143 | 343 | 360 | ||
144 | 361 | def remove_resolved_configuration(self): | ||
145 | 362 | """Remove systemd's resolved configuration. | ||
146 | 363 | |||
147 | 364 | Will render resolved config with defaults. | ||
148 | 365 | """ | ||
149 | 366 | self._update_systemd_resolved({}) | ||
150 | 367 | hookenv.log('deleted resolved configuration at '.format(SYSTEMD_RESOLVED), hookenv.DEBUG) | ||
151 | 368 | |||
152 | 369 | def _update_systemd_resolved(self, context): | ||
153 | 370 | self._render_resource(SYSTEMD_RESOLVED_TMPL, SYSTEMD_RESOLVED, context) | ||
154 | 371 | if any_file_changed([SYSTEMD_RESOLVED]): | ||
155 | 372 | host.service_restart('systemd-resolved') | ||
156 | 373 | |||
157 | 344 | def remove_cpufreq_configuration(self): | 374 | def remove_cpufreq_configuration(self): |
158 | 345 | """Remove cpufrequtils configuration. | 375 | """Remove cpufrequtils configuration. |
159 | 346 | 376 | ||
160 | diff --git a/src/reactive/sysconfig.py b/src/reactive/sysconfig.py | |||
161 | index eba0916..6c70fba 100644 | |||
162 | --- a/src/reactive/sysconfig.py | |||
163 | +++ b/src/reactive/sysconfig.py | |||
164 | @@ -28,7 +28,7 @@ from charms.reactive import ( | |||
165 | 28 | when_not, | 28 | when_not, |
166 | 29 | ) | 29 | ) |
167 | 30 | 30 | ||
169 | 31 | from lib_sysconfig import CPUFREQUTILS, GRUB_CONF, KERNEL, SYSTEMD_SYSTEM, SysConfigHelper | 31 | from lib_sysconfig import CPUFREQUTILS, GRUB_CONF, KERNEL, SYSTEMD_SYSTEM, SYSTEMD_RESOLVED, SysConfigHelper |
170 | 32 | 32 | ||
171 | 33 | 33 | ||
172 | 34 | @when_none('sysconfig.installed', 'sysconfig.unsupported') | 34 | @when_none('sysconfig.installed', 'sysconfig.unsupported') |
173 | @@ -54,6 +54,7 @@ def install_sysconfig(): | |||
174 | 54 | syshelper.update_cpufreq() | 54 | syshelper.update_cpufreq() |
175 | 55 | syshelper.update_grub_file() | 55 | syshelper.update_grub_file() |
176 | 56 | syshelper.update_systemd_system_file() | 56 | syshelper.update_systemd_system_file() |
177 | 57 | syshelper.update_systemd_resolved() | ||
178 | 57 | set_flag('sysconfig.installed') | 58 | set_flag('sysconfig.installed') |
179 | 58 | update_status() | 59 | update_status() |
180 | 59 | 60 | ||
181 | @@ -103,6 +104,12 @@ def config_changed(): | |||
182 | 103 | )) or helpers.any_file_changed([SYSTEMD_SYSTEM]): | 104 | )) or helpers.any_file_changed([SYSTEMD_SYSTEM]): |
183 | 104 | syshelper.update_systemd_system_file() | 105 | syshelper.update_systemd_system_file() |
184 | 105 | 106 | ||
185 | 107 | # systemd resolved | ||
186 | 108 | if any(syshelper.charm_config.changed(flag) for flag in ( | ||
187 | 109 | 'resolved-cache-mode', | ||
188 | 110 | )) or helpers.any_file_changed([SYSTEMD_RESOLVED]): | ||
189 | 111 | syshelper.update_systemd_resolved() | ||
190 | 112 | |||
191 | 106 | update_status() | 113 | update_status() |
192 | 107 | 114 | ||
193 | 108 | 115 | ||
194 | @@ -142,5 +149,6 @@ def remove_configuration(): | |||
195 | 142 | syshelper.remove_cpufreq_configuration() | 149 | syshelper.remove_cpufreq_configuration() |
196 | 143 | syshelper.remove_grub_configuration() | 150 | syshelper.remove_grub_configuration() |
197 | 144 | syshelper.remove_systemd_configuration() | 151 | syshelper.remove_systemd_configuration() |
198 | 152 | syshelper.remove_resolved_configuration() | ||
199 | 145 | clear_flag('sysconfig.installed') | 153 | clear_flag('sysconfig.installed') |
200 | 146 | clear_flag('sysconfig.unsupported') | 154 | clear_flag('sysconfig.unsupported') |
201 | diff --git a/src/templates/etc.systemd.resolved.conf.j2 b/src/templates/etc.systemd.resolved.conf.j2 | |||
202 | 147 | new file mode 100644 | 155 | new file mode 100644 |
203 | index 0000000..ab8832b | |||
204 | --- /dev/null | |||
205 | +++ b/src/templates/etc.systemd.resolved.conf.j2 | |||
206 | @@ -0,0 +1,27 @@ | |||
207 | 1 | # This file is part of systemd. | ||
208 | 2 | # | ||
209 | 3 | # systemd is free software; you can redistribute it and/or modify it | ||
210 | 4 | # under the terms of the GNU Lesser General Public License as published by | ||
211 | 5 | # the Free Software Foundation; either version 2.1 of the License, or | ||
212 | 6 | # (at your option) any later version. | ||
213 | 7 | # | ||
214 | 8 | # Entries in this file show the compile time defaults. | ||
215 | 9 | # You can change settings by editing this file. | ||
216 | 10 | # Defaults can be restored by simply deleting this file. | ||
217 | 11 | # | ||
218 | 12 | # See resolved.conf(5) for details | ||
219 | 13 | |||
220 | 14 | [Resolve] | ||
221 | 15 | #DNS= | ||
222 | 16 | #FallbackDNS= | ||
223 | 17 | #Domains= | ||
224 | 18 | #LLMNR=no | ||
225 | 19 | #MulticastDNS=no | ||
226 | 20 | #DNSSEC=no | ||
227 | 21 | #DNSOverTLS=no | ||
228 | 22 | #Cache=yes | ||
229 | 23 | {% if cache -%} | ||
230 | 24 | Cache={{ cache }} | ||
231 | 25 | {% endif -%} | ||
232 | 26 | #DNSStubListener=yes | ||
233 | 27 | #ReadEtcHosts=yes | ||
234 | diff --git a/src/tests/functional/test_deploy.py b/src/tests/functional/test_deploy.py | |||
235 | index 968288b..2900e06 100644 | |||
236 | --- a/src/tests/functional/test_deploy.py | |||
237 | +++ b/src/tests/functional/test_deploy.py | |||
238 | @@ -2,9 +2,11 @@ | |||
239 | 2 | 2 | ||
240 | 3 | import asyncio | 3 | import asyncio |
241 | 4 | import os | 4 | import os |
242 | 5 | import re | ||
243 | 5 | import subprocess | 6 | import subprocess |
244 | 6 | 7 | ||
245 | 7 | import pytest | 8 | import pytest |
246 | 9 | import websockets | ||
247 | 8 | 10 | ||
248 | 9 | 11 | ||
249 | 10 | # Treat all tests as coroutines | 12 | # Treat all tests as coroutines |
250 | @@ -14,12 +16,12 @@ charm_build_dir = os.getenv('CHARM_BUILD_DIR', '..').rstrip('/') | |||
251 | 14 | 16 | ||
252 | 15 | series = ['xenial', | 17 | series = ['xenial', |
253 | 16 | 'bionic', | 18 | 'bionic', |
254 | 17 | pytest.param('cosmic', marks=pytest.mark.xfail(reason='canary')), | ||
255 | 18 | ] | 19 | ] |
256 | 19 | 20 | ||
257 | 20 | sources = [('local', '{}/builds/sysconfig'.format(charm_build_dir))] | 21 | sources = [('local', '{}/builds/sysconfig'.format(charm_build_dir))] |
258 | 21 | 22 | ||
259 | 22 | TIMEOUT = 600 | 23 | TIMEOUT = 600 |
260 | 24 | MODEL_ACTIVE_TIMEOUT = 10 | ||
261 | 23 | GRUB_DEFAULT = 'Advanced options for Ubuntu>Ubuntu, with Linux {}' | 25 | GRUB_DEFAULT = 'Advanced options for Ubuntu>Ubuntu, with Linux {}' |
262 | 24 | PRINCIPAL_APP_NAME = 'ubuntu-{}' | 26 | PRINCIPAL_APP_NAME = 'ubuntu-{}' |
263 | 25 | 27 | ||
264 | @@ -269,14 +271,19 @@ async def test_wrong_reservation(app, model): | |||
265 | 269 | ) | 271 | ) |
266 | 270 | 272 | ||
267 | 271 | 273 | ||
270 | 272 | async def test_wrong_raid_autodetection(app, model): | 274 | @pytest.mark.parametrize('key,bad_value,good_value', [ |
271 | 273 | """Tests wrong raid-autodetection value is used. | 275 | ('raid-autodetection', 'changeme', ''), |
272 | 276 | ('governor', 'changeme', ''), | ||
273 | 277 | ('resolved-cache-mode', 'changeme', ''), | ||
274 | 278 | ]) | ||
275 | 279 | async def test_invalid_configuration_parameters(app, model, key, bad_value, good_value): | ||
276 | 280 | """Tests wrong config value is used. | ||
277 | 274 | 281 | ||
278 | 275 | Expect application is blocked until correct value is set. | 282 | Expect application is blocked until correct value is set. |
279 | 276 | """ | 283 | """ |
280 | 277 | await app.set_config( | 284 | await app.set_config( |
281 | 278 | { | 285 | { |
283 | 279 | 'raid-autodetection': 'changeme' | 286 | key: bad_value |
284 | 280 | } | 287 | } |
285 | 281 | ) | 288 | ) |
286 | 282 | await model.block_until( | 289 | await model.block_until( |
287 | @@ -289,7 +296,7 @@ async def test_wrong_raid_autodetection(app, model): | |||
288 | 289 | 296 | ||
289 | 290 | await app.set_config( | 297 | await app.set_config( |
290 | 291 | { | 298 | { |
292 | 292 | 'raid-autodetection': '' | 299 | key: good_value |
293 | 293 | } | 300 | } |
294 | 294 | ) | 301 | ) |
295 | 295 | await model.block_until( | 302 | await model.block_until( |
296 | @@ -298,33 +305,32 @@ async def test_wrong_raid_autodetection(app, model): | |||
297 | 298 | ) | 305 | ) |
298 | 299 | 306 | ||
299 | 300 | 307 | ||
302 | 301 | async def test_wrong_governor(app, model): | 308 | @pytest.mark.parametrize('cache_setting', [ |
303 | 302 | """Tests wrong governor value is used. | 309 | 'yes', |
304 | 310 | 'no', | ||
305 | 311 | 'no-negative', | ||
306 | 312 | ]) | ||
307 | 313 | async def test_set_resolved_cache(app, model, jujutools, cache_setting): | ||
308 | 314 | """Tests resolved cache settings.""" | ||
309 | 315 | def is_model_settled(): | ||
310 | 316 | return app.units[0].workload_status == 'active' and app.units[0].agent_status == 'idle' | ||
311 | 303 | 317 | ||
326 | 304 | Expect application is blocked until correct value is set. | 318 | await model.block_until(is_model_settled, timeout=TIMEOUT) |
313 | 305 | """ | ||
314 | 306 | await app.set_config( | ||
315 | 307 | { | ||
316 | 308 | 'governor': 'changeme' | ||
317 | 309 | } | ||
318 | 310 | ) | ||
319 | 311 | await model.block_until( | ||
320 | 312 | lambda: app.status == 'blocked', | ||
321 | 313 | timeout=TIMEOUT | ||
322 | 314 | ) | ||
323 | 315 | assert app.status == 'blocked' | ||
324 | 316 | unit = app.units[0] | ||
325 | 317 | assert 'configuration parameters not valid.' in unit.workload_status_message | ||
327 | 318 | 319 | ||
337 | 319 | await app.set_config( | 320 | await app.set_config({'resolved-cache-mode': cache_setting}) |
338 | 320 | { | 321 | # NOTE: app.set_config() doesn't seem to wait for the model to go to a |
339 | 321 | 'governor': '' | 322 | # non-active/idle state. |
340 | 322 | } | 323 | try: |
341 | 323 | ) | 324 | await model.block_until(lambda: not is_model_settled(), timeout=MODEL_ACTIVE_TIMEOUT) |
342 | 324 | await model.block_until( | 325 | except websockets.ConnectionClosed: |
343 | 325 | lambda: app.status == 'active', | 326 | # It's possible (although unlikely) that we missed the charm transitioning from |
344 | 326 | timeout=TIMEOUT | 327 | # idle to active and back. |
345 | 327 | ) | 328 | pass |
346 | 329 | |||
347 | 330 | await model.block_until(is_model_settled, timeout=TIMEOUT) | ||
348 | 331 | |||
349 | 332 | resolved_conf_content = await jujutools.file_contents('/etc/systemd/resolved.conf', app.units[0]) | ||
350 | 333 | assert re.search('^Cache={}$'.format(cache_setting), resolved_conf_content, re.MULTILINE) | ||
351 | 328 | 334 | ||
352 | 329 | 335 | ||
353 | 330 | async def test_uninstall(app, model, jujutools, series): | 336 | async def test_uninstall(app, model, jujutools, series): |
354 | @@ -365,6 +371,10 @@ async def test_uninstall(app, model, jujutools, series): | |||
355 | 365 | systemd_content = await jujutools.file_contents(systemd_path, unit) | 371 | systemd_content = await jujutools.file_contents(systemd_path, unit) |
356 | 366 | assert 'CPUAffinity=1,2,3,4' not in systemd_content | 372 | assert 'CPUAffinity=1,2,3,4' not in systemd_content |
357 | 367 | 373 | ||
358 | 374 | resolved_path = '/etc/systemd/resolved.conf' | ||
359 | 375 | resolved_content = await jujutools.file_contents(resolved_path, unit) | ||
360 | 376 | assert not re.search('^Cache=', resolved_content, re.MULTILINE) | ||
361 | 377 | |||
362 | 368 | cpufreq_path = '/etc/default/cpufrequtils' | 378 | cpufreq_path = '/etc/default/cpufrequtils' |
363 | 369 | cpufreq_content = await jujutools.file_contents(cpufreq_path, unit) | 379 | cpufreq_content = await jujutools.file_contents(cpufreq_path, unit) |
364 | 370 | assert 'GOVERNOR' not in cpufreq_content | 380 | assert 'GOVERNOR' not in cpufreq_content |
365 | diff --git a/src/tests/unit/test_lib.py b/src/tests/unit/test_lib.py | |||
366 | index b7ac651..be25eb4 100644 | |||
367 | --- a/src/tests/unit/test_lib.py | |||
368 | +++ b/src/tests/unit/test_lib.py | |||
369 | @@ -1,13 +1,14 @@ | |||
370 | 1 | #!/usr/bin/python3 | 1 | #!/usr/bin/python3 |
371 | 2 | """Unit tests for SysConfigHelper and BootResourceState classes.""" | 2 | """Unit tests for SysConfigHelper and BootResourceState classes.""" |
374 | 3 | 3 | import os | |
373 | 4 | |||
375 | 5 | import subprocess | 4 | import subprocess |
376 | 5 | import tempfile | ||
377 | 6 | from datetime import datetime, timezone | 6 | from datetime import datetime, timezone |
378 | 7 | 7 | ||
379 | 8 | import lib_sysconfig | 8 | import lib_sysconfig |
380 | 9 | 9 | ||
381 | 10 | import mock | 10 | import mock |
382 | 11 | import pytest | ||
383 | 11 | 12 | ||
384 | 12 | 13 | ||
385 | 13 | class TestBootResourceState: | 14 | class TestBootResourceState: |
386 | @@ -479,45 +480,22 @@ class TestLib: | |||
387 | 479 | ) | 480 | ) |
388 | 480 | restart.assert_called() | 481 | restart.assert_called() |
389 | 481 | 482 | ||
390 | 483 | @pytest.mark.parametrize("invalid_config_key", [ | ||
391 | 484 | "reservation", "raid-autodetection", "governor", "resolved-cache-mode"]) | ||
392 | 482 | @mock.patch("lib_sysconfig.hookenv.config") | 485 | @mock.patch("lib_sysconfig.hookenv.config") |
423 | 483 | def test_wrong_reservation(self, config): | 486 | def test_wrong_config(self, config, invalid_config_key): |
424 | 484 | """Test wrong reservation value. | 487 | """Test wrong configuration value. |
395 | 485 | |||
396 | 486 | Expect that is_config_valid() return false | ||
397 | 487 | """ | ||
398 | 488 | config.return_value = { | ||
399 | 489 | "reservation": "wrong", | ||
400 | 490 | "raid-autodetection": "", | ||
401 | 491 | "governor": "" | ||
402 | 492 | } | ||
403 | 493 | sysh = lib_sysconfig.SysConfigHelper() | ||
404 | 494 | assert not sysh.is_config_valid() | ||
405 | 495 | |||
406 | 496 | @mock.patch("lib_sysconfig.hookenv.config") | ||
407 | 497 | def test_wrong_raid(self, config): | ||
408 | 498 | """Test wrong raid autodetection value. | ||
409 | 499 | |||
410 | 500 | Expect that is_config_valid() return false | ||
411 | 501 | """ | ||
412 | 502 | config.return_value = { | ||
413 | 503 | "reservation": "off", | ||
414 | 504 | "raid-autodetection": "wrong", | ||
415 | 505 | "governor": "" | ||
416 | 506 | } | ||
417 | 507 | sysh = lib_sysconfig.SysConfigHelper() | ||
418 | 508 | assert not sysh.is_config_valid() | ||
419 | 509 | |||
420 | 510 | @mock.patch("lib_sysconfig.hookenv.config") | ||
421 | 511 | def test_wrong_governor(self, config): | ||
422 | 512 | """Test wrong governor value. | ||
425 | 513 | 488 | ||
426 | 514 | Expect that is_config_valid() return false | 489 | Expect that is_config_valid() return false |
427 | 515 | """ | 490 | """ |
429 | 516 | config.return_value = { | 491 | return_value = { |
430 | 517 | "reservation": "off", | 492 | "reservation": "off", |
431 | 518 | "raid-autodetection": "", | 493 | "raid-autodetection": "", |
433 | 519 | "governor": "wrong" | 494 | "governor": "", |
434 | 495 | "resolved-cache-mode": "", | ||
435 | 496 | invalid_config_key: 'wrong', # Will override the selected key with an invalid value | ||
436 | 520 | } | 497 | } |
437 | 498 | config.return_value = return_value | ||
438 | 521 | sysh = lib_sysconfig.SysConfigHelper() | 499 | sysh = lib_sysconfig.SysConfigHelper() |
439 | 522 | assert not sysh.is_config_valid() | 500 | assert not sysh.is_config_valid() |
440 | 523 | 501 | ||
441 | @@ -530,3 +508,32 @@ class TestLib: | |||
442 | 530 | sysh = lib_sysconfig.SysConfigHelper() | 508 | sysh = lib_sysconfig.SysConfigHelper() |
443 | 531 | 509 | ||
444 | 532 | assert sysh.enable_container | 510 | assert sysh.enable_container |
445 | 511 | |||
446 | 512 | @mock.patch("lib_sysconfig.any_file_changed") | ||
447 | 513 | @mock.patch("lib_sysconfig.hookenv.config") | ||
448 | 514 | @mock.patch("lib_sysconfig.host.service_restart") | ||
449 | 515 | @mock.patch("lib_sysconfig.render") | ||
450 | 516 | def test_update_resolved_file_unchanged(self, render, restart, config, file_changed): | ||
451 | 517 | file_changed.return_value = True | ||
452 | 518 | self._test_update_resolved_common(render, config) | ||
453 | 519 | restart.assert_not_called() | ||
454 | 520 | |||
455 | 521 | @mock.patch("lib_sysconfig.any_file_changed") | ||
456 | 522 | @mock.patch("lib_sysconfig.hookenv.config") | ||
457 | 523 | @mock.patch("lib_sysconfig.host.service_restart") | ||
458 | 524 | @mock.patch("lib_sysconfig.render") | ||
459 | 525 | def test_update_resolved_file_changed(self, render, restart, config, file_changed): | ||
460 | 526 | file_changed.return_value = True | ||
461 | 527 | self._test_update_resolved_common(render, config) | ||
462 | 528 | restart.assert_called() | ||
463 | 529 | |||
464 | 530 | def _test_update_resolved_common(self, render, config): | ||
465 | 531 | config.return_value = {"resolved-cache-mode": "no-negative"} | ||
466 | 532 | sysh = lib_sysconfig.SysConfigHelper() | ||
467 | 533 | sysh.update_systemd_resolved() | ||
468 | 534 | render.assert_called_with( | ||
469 | 535 | source=lib_sysconfig.SYSTEMD_RESOLVED_TMPL, | ||
470 | 536 | target=lib_sysconfig.SYSTEMD_RESOLVED, | ||
471 | 537 | templates_dir="templates", | ||
472 | 538 | context={"cache": "no-negative"}, | ||
473 | 539 | ) |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.