Merge ~gabrielcocenza/juju-lint:bug/1988851 into juju-lint:master

Proposed by Gabriel Cocenza
Status: Merged
Approved by: Eric Chen
Approved revision: d520551fb9eb9060f7c7b00ce6688bfe9aa61958
Merged at revision: 3684a5c1f7e315832720e859f0e4a78c757e7d9a
Proposed branch: ~gabrielcocenza/juju-lint:bug/1988851
Merge into: juju-lint:master
Prerequisite: ~gabrielcocenza/juju-lint:bug/1979542
Diff against target: 1242 lines (+633/-141)
5 files modified
contrib/includes/networking/ovn.yaml (+14/-6)
contrib/includes/networking/ovs.yaml (+10/-2)
contrib/includes/openstack.yaml (+0/-5)
jujulint/lint.py (+142/-48)
tests/unit/test_jujulint.py (+467/-80)
Reviewer Review Type Date Requested Status
Eric Chen Approve
🤖 prod-jenkaas-bootstack continuous-integration Approve
Nobuto Murata (community) Approve
Review via email: mp+429591@code.launchpad.net

Commit message

check path_mtu values depending on the networking solution (ovs, ovn)

- added the possibility of adding custom messages and log level for
  config checks (isset, eq, neq, gte and search)

Closes-Bug: #1988851

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
Eric Chen (eric-chen) wrote :

Do we need the function to adding custom messages in this PR? it seems an additional feature to me.

review: Needs Information
Revision history for this message
Eric Chen (eric-chen) wrote :

Leave similar quiestion inline

review: Needs Information
Revision history for this message
Gabriel Cocenza (gabrielcocenza) wrote :

Yes, it's actually two new features: ability of controlling log-level and custom message on every application config.

I think it's a good versatility to have this option of customizing messages and control the log-level on every application configuration. Let's get the neutron-api configuration of path-mtu for example:

- If we can't insert a custom message, the user would receive an error message saying:
Application neutron-api has config for path-mtu which is bigger than 1550: 2000
This standard message doesn't say much what actually this value can cause

The problem with this is:
- having a value bigger than 1550 it's not considered "wrong" depending on the networking solution, so it makes more sense send a warn message explaining what can happen if the value is bigger.

I can see this customization been useful on every "operator". Maybe using isset to False in a configuration is opinionated and not necessarily wrong.

pastebin of tests (lint, unit, func) https://pastebin.canonical.com/p/QZ6BT8HZxW/

Revision history for this message
Nobuto Murata (nobuto) :
review: Needs Fixing
Revision history for this message
Gabriel Cocenza (gabrielcocenza) wrote :

Thanks for your review Nobuto.

I've updated the patch including your suggestions.
Here[0] is the logs of unit, lint and functional tests.

[0] https://pastebin.canonical.com/p/tbCSh5Hs8t/

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Approve (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Approve (continuous-integration)
Revision history for this message
Nobuto Murata (nobuto) wrote :

Left a nit-picking comment, other than that it looks good to me.

review: Approve
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Approve (continuous-integration)
Revision history for this message
Eric Chen (eric-chen) :
review: Approve
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision 3684a5c1f7e315832720e859f0e4a78c757e7d9a

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/contrib/includes/networking/ovn.yaml b/contrib/includes/networking/ovn.yaml
2index b4f4140..bba04d5 100644
3--- a/contrib/includes/networking/ovn.yaml
4+++ b/contrib/includes/networking/ovn.yaml
5@@ -1,9 +1,17 @@
6 openstack config networking: &openstack-config-networking
7- ovn-central:
8- ovsdb-server-election-timer:
9- gte: 4
10+ ovn-central:
11+ ovsdb-server-election-timer:
12+ gte: 4
13+ neutron-api:
14+ path-mtu:
15+ gte: 1558
16+ log-level: warning
17+ custom-message: >
18+ Usable MTU for a tenant network is less than 1500.
19+ global-physnet-mtu:
20+ eq: 9000
21
22 openstack mandatory charms networking: &openstack-mandatory-charms-networking
23- - ovn-central
24- - ovn-chassis
25- - neutron-api-plugin-ovn
26+ - ovn-central
27+ - ovn-chassis
28+ - neutron-api-plugin-ovn
29diff --git a/contrib/includes/networking/ovs.yaml b/contrib/includes/networking/ovs.yaml
30index 493195e..4cba606 100644
31--- a/contrib/includes/networking/ovs.yaml
32+++ b/contrib/includes/networking/ovs.yaml
33@@ -1,4 +1,12 @@
34-openstack config networking: &openstack-config-networking {}
35+openstack config networking: &openstack-config-networking
36+ neutron-api:
37+ path-mtu:
38+ gte: 1550
39+ log-level: warning
40+ custom-message: >
41+ Usable MTU for a tenant network is less than 1500.
42+ global-physnet-mtu:
43+ eq: 9000
44
45 openstack mandatory charms networking: &openstack-mandatory-charms-networking
46- - neutron-openvswitch
47+ - neutron-openvswitch
48diff --git a/contrib/includes/openstack.yaml b/contrib/includes/openstack.yaml
49index 01317e2..86af4da 100644
50--- a/contrib/includes/openstack.yaml
51+++ b/contrib/includes/openstack.yaml
52@@ -14,11 +14,6 @@ openstack config base: &openstack-config-base
53 glance:
54 ceph-osd-replication-count:
55 eq: 3
56- neutron-api:
57- path-mtu:
58- eq: 9000
59- global-physnet-mtu:
60- eq: 9000
61 nova-compute:
62 cpu-model:
63 neq: ""
64diff --git a/jujulint/lint.py b/jujulint/lint.py
65index 8bbcff4..b8fa535 100755
66--- a/jujulint/lint.py
67+++ b/jujulint/lint.py
68@@ -40,8 +40,14 @@ from jujulint.model_input import input_handler
69 from jujulint.relations import RelationError, RelationsRulesBootStrap
70
71 VALID_CONFIG_CHECKS = ("isset", "eq", "neq", "gte", "search")
72-
73-# Generic named tuple to represent the binary config operators (eq,neq,gte)
74+VALID_LOG_LEVEL = {
75+ "debug": logging.DEBUG,
76+ "info": logging.INFO,
77+ "warning": logging.WARNING,
78+ "error": logging.ERROR,
79+}
80+
81+# Generic named tuple to represent the binary config operators (eq, neq, gte)
82 ConfigOperator = collections.namedtuple(
83 "ConfigOperator", "name repr check error_template"
84 )
85@@ -212,7 +218,15 @@ class Linter:
86
87 return _int * conv[suffix.lower()]
88
89- def isset(self, name, check_value, rule, config):
90+ def isset(
91+ self,
92+ name,
93+ check_value,
94+ rule,
95+ config,
96+ log_level=logging.ERROR,
97+ custom_message="",
98+ ):
99 """Check if value is set per rule constraints."""
100 if rule in config:
101 if check_value is True:
102@@ -225,7 +239,7 @@ class Linter:
103 )
104 return True
105 actual_value = config[rule]
106- self.handle_error(
107+ self.message_handler(
108 {
109 "id": "config-isset-check-false",
110 "tags": ["config", "isset"],
111@@ -233,10 +247,12 @@ class Linter:
112 "application": name,
113 "rule": rule,
114 "actual_value": actual_value,
115- "message": "Application {} has config for {}: {}.".format(
116+ "message": custom_message
117+ or "Application {} has config for {}: {}.".format(
118 name, rule, actual_value
119 ),
120- }
121+ },
122+ log_level=log_level,
123 )
124 return False
125 elif check_value is False:
126@@ -247,19 +263,29 @@ class Linter:
127 )
128 )
129 return True
130- self.handle_error(
131+ self.message_handler(
132 {
133 "id": "config-isset-check-true",
134 "tags": ["config", "isset"],
135 "description": "Checks for config condition 'isset' true",
136 "application": name,
137 "rule": rule,
138- "message": "Application {} has no config for {}.".format(name, rule),
139- }
140+ "message": custom_message
141+ or "Application {} has no config for {}.".format(name, rule),
142+ },
143+ log_level=log_level,
144 )
145 return False
146
147- def eq(self, app_name, check_value, config_key, app_config):
148+ def eq(
149+ self,
150+ app_name,
151+ check_value,
152+ config_key,
153+ app_config,
154+ log_level=logging.ERROR,
155+ custom_message="",
156+ ):
157 """Check if value matches the provided value or regex, autodetecting regex."""
158 operator = ConfigOperator(
159 name="eq",
160@@ -269,10 +295,24 @@ class Linter:
161 )
162
163 return self.check_config_generic(
164- operator, app_name, check_value, config_key, app_config
165+ operator,
166+ app_name,
167+ check_value,
168+ config_key,
169+ app_config,
170+ log_level,
171+ custom_message,
172 )
173
174- def neq(self, app_name, check_value, config_key, app_config):
175+ def neq(
176+ self,
177+ app_name,
178+ check_value,
179+ config_key,
180+ app_config,
181+ log_level=logging.ERROR,
182+ custom_message="",
183+ ):
184 """Check if value does not match a the config."""
185 operator = ConfigOperator(
186 name="neq",
187@@ -282,10 +322,24 @@ class Linter:
188 )
189
190 return self.check_config_generic(
191- operator, app_name, check_value, config_key, app_config
192+ operator,
193+ app_name,
194+ check_value,
195+ config_key,
196+ app_config,
197+ log_level,
198+ custom_message,
199 )
200
201- def gte(self, app_name, check_value, config_key, app_config):
202+ def gte(
203+ self,
204+ app_name,
205+ check_value,
206+ config_key,
207+ app_config,
208+ log_level=logging.ERROR,
209+ custom_message="",
210+ ):
211 """Check if value is greater than or equal to the check value."""
212
213 def operator_gte_check(check_value, actual_value):
214@@ -302,10 +356,24 @@ class Linter:
215 )
216
217 return self.check_config_generic(
218- operator, app_name, check_value, config_key, app_config
219+ operator,
220+ app_name,
221+ check_value,
222+ config_key,
223+ app_config,
224+ log_level,
225+ custom_message,
226 )
227
228- def search(self, app_name, check_value, config_key, app_config):
229+ def search(
230+ self,
231+ app_name,
232+ check_value,
233+ config_key,
234+ app_config,
235+ log_level=logging.ERROR,
236+ custom_message="",
237+ ):
238 """Scan through the charm config looking for a match using the regex pattern."""
239 if config_key in app_config:
240 actual_value = app_config.get(config_key)
241@@ -319,7 +387,7 @@ class Linter:
242 )
243 )
244 return True
245- self.handle_error(
246+ self.message_handler(
247 {
248 "id": "config-search-check",
249 "tags": ["config", "search"],
250@@ -328,13 +396,15 @@ class Linter:
251 "rule": config_key,
252 "expected_value": check_value,
253 "actual_value": actual_value,
254- "message": "Application {} has an invalid config for '{}': regex {} not found at {}".format(
255+ "message": custom_message
256+ or "Application {} has an invalid config for '{}': regex {} not found at {}".format(
257 app_name,
258 config_key,
259 repr(check_value),
260 repr(actual_value),
261 ),
262- }
263+ },
264+ log_level=log_level,
265 )
266 return False
267
268@@ -349,7 +419,14 @@ class Linter:
269 return False
270
271 def check_config_generic(
272- self, operator, app_name, check_value, config_key, app_config
273+ self,
274+ operator,
275+ app_name,
276+ check_value,
277+ config_key,
278+ app_config,
279+ log_level=logging.ERROR,
280+ custom_message="",
281 ):
282 """Apply the provided config operator to the configuration."""
283 # First check if the config key is present
284@@ -380,7 +457,7 @@ class Linter:
285 )
286 return True
287 else:
288- self.handle_error(
289+ self.message_handler(
290 {
291 "id": "config-{}-check".format(operator.name),
292 "tags": ["config", operator.name],
293@@ -391,13 +468,15 @@ class Linter:
294 "rule": config_key,
295 "expected_value": check_value,
296 "actual_value": actual_value,
297- "message": operator.error_template.format(
298+ "message": custom_message
299+ or operator.error_template.format(
300 app_name,
301 config_key,
302 repr(check_value),
303 repr(actual_value),
304 ),
305- }
306+ },
307+ log_level=log_level,
308 )
309 return False
310
311@@ -428,11 +507,21 @@ class Linter:
312 )
313 continue
314
315+ custom_message = rules[rule].pop("custom-message", "")
316+ log_level = rules[rule].pop("log-level", "error").lower()
317+
318 for check_op, check_value in rules[rule].items():
319 # check_op should be the operator name, e.g. (eq, neq, gte, isset)
320 if check_op in VALID_CONFIG_CHECKS:
321 check_method = getattr(self, check_op)
322- check_method(app_name, check_value, rule, config)
323+ check_method(
324+ app_name,
325+ check_value,
326+ rule,
327+ config,
328+ VALID_LOG_LEVEL.get(log_level, logging.ERROR),
329+ custom_message,
330+ )
331 else:
332 self._log_with_header(
333 "Application {} has unknown check operation for {}: {}.".format(
334@@ -635,7 +724,7 @@ class Linter:
335 for rule in relations_rules:
336 for endpoint, applications in rule.missing_relations.items():
337 if applications:
338- self.handle_error(
339+ self.message_handler(
340 {
341 "id": "missing-relations",
342 "tags": ["relation", "missing"],
343@@ -646,7 +735,7 @@ class Linter:
344 )
345 if rule.not_exist_error:
346 for relation in rule.not_exist_error:
347- self.handle_error(
348+ self.message_handler(
349 {
350 "id": "relation-exist",
351 "tags": ["relation", "exist"],
352@@ -657,7 +746,7 @@ class Linter:
353 )
354
355 if rule.missing_machines:
356- self.handle_error(
357+ self.message_handler(
358 {
359 "id": "missing-machine",
360 "tags": ["missing", "machine"],
361@@ -697,7 +786,7 @@ class Linter:
362 """Check we recognise the charms which are in the model."""
363 for charm in self.model.charms:
364 if charm not in self.lint_rules["known charms"]:
365- self.handle_error(
366+ self.message_handler(
367 {
368 "id": "unrecognised-charm",
369 "tags": ["charm", "unrecognised"],
370@@ -710,11 +799,11 @@ class Linter:
371 for charm in self.lint_rules["operations mandatory"]:
372 error = self.check_charms_ops_mandatory(charm)
373 if error:
374- self.handle_error(error)
375+ self.message_handler(error)
376 if self.cloud_type == "openstack":
377 for charm in self.lint_rules["openstack mandatory"]:
378 if charm not in self.model.charms:
379- self.handle_error(
380+ self.message_handler(
381 {
382 "id": "openstack-charm-missing",
383 "tags": [
384@@ -731,7 +820,7 @@ class Linter:
385 )
386 for charm in self.lint_rules["operations openstack mandatory"]:
387 if charm not in self.model.charms:
388- self.handle_error(
389+ self.message_handler(
390 {
391 "id": "openstack-ops-charm-missing",
392 "tags": [
393@@ -752,7 +841,7 @@ class Linter:
394 elif self.cloud_type == "kubernetes":
395 for charm in self.lint_rules["kubernetes mandatory"]:
396 if charm not in self.model.charms:
397- self.handle_error(
398+ self.message_handler(
399 {
400 "id": "kubernetes-charm-missing",
401 "tags": [
402@@ -769,7 +858,7 @@ class Linter:
403 )
404 for charm in self.lint_rules["operations kubernetes mandatory"]:
405 if charm not in self.model.charms:
406- self.handle_error(
407+ self.message_handler(
408 {
409 "id": "kubernetes-ops-charm-missing",
410 "tags": [
411@@ -895,7 +984,7 @@ class Linter:
412
413 message = "Space binding mismatch: {}".format(mismatch)
414 if error:
415- self.handle_error(
416+ self.message_handler(
417 {
418 "id": "space-binding-mismatch",
419 "tags": ["mismatch", "space", "binding"],
420@@ -912,7 +1001,7 @@ class Linter:
421 if self.model.missing_subs:
422 for sub in self.model.missing_subs:
423 principals = ", ".join(sorted(self.model.missing_subs[sub]))
424- self.handle_error(
425+ self.message_handler(
426 {
427 "id": "ops-subordinate-missing",
428 "tags": ["missing", "ops", "charm", "mandatory", "subordinate"],
429@@ -928,7 +1017,7 @@ class Linter:
430 if self.model.extraneous_subs:
431 for sub in self.model.extraneous_subs:
432 principals = ", ".join(sorted(self.model.extraneous_subs[sub]))
433- self.handle_error(
434+ self.message_handler(
435 {
436 "id": "subordinate-extraneous",
437 "tags": ["extraneous", "charm", "subordinate"],
438@@ -943,7 +1032,7 @@ class Linter:
439 if self.model.duelling_subs:
440 for sub in self.model.duelling_subs:
441 machines = ", ".join(sorted(self.model.duelling_subs[sub]))
442- self.handle_error(
443+ self.message_handler(
444 {
445 "id": "subordinate-duplicate",
446 "tags": ["duplicate", "charm", "subordinate"],
447@@ -962,7 +1051,7 @@ class Linter:
448 az_map = ", ".join(
449 ["{}: {}".format(az, az_counter[az]) for az in sorted(az_counter)]
450 )
451- self.handle_error(
452+ self.message_handler(
453 {
454 "id": "AZ-unbalance",
455 "tags": ["AZ"],
456@@ -987,7 +1076,7 @@ class Linter:
457 self.model.app_to_charm[app] = charm_name
458 self.model.charm_to_app.setdefault(charm_name, set()).add(app)
459 else:
460- self.handle_error(
461+ self.message_handler(
462 {
463 "id": "charm-not-mapped",
464 "tags": ["charm", "mapped", "parsing"],
465@@ -1060,7 +1149,7 @@ class Linter:
466 return
467
468 status_msg = status.get("message")
469- self.handle_error(
470+ self.message_handler(
471 {
472 "id": "status-unexpected",
473 "tags": ["status"],
474@@ -1162,7 +1251,7 @@ class Linter:
475 azs.add(self.model.machines_to_az[machine])
476 num_azs = len(azs)
477 if num_azs != 3:
478- self.handle_error(
479+ self.message_handler(
480 {
481 "id": "AZ-invalid-number",
482 "tags": ["AZ"],
483@@ -1306,15 +1395,20 @@ class Linter:
484 "Model contains no applications, skipping.", level=logging.WARN
485 )
486
487- def collect(self, error):
488+ def collect(self, message):
489 """Collect an error and add it to the collector."""
490- self.output_collector["errors"].append(error)
491+ self.output_collector["errors"].append(message)
492
493- def handle_error(self, error):
494- """Collect an error and add it to the collector."""
495- self._log_with_header(error["message"], level=logging.ERROR)
496- if self.collect_errors:
497- self.collect(error)
498+ def message_handler(self, message, log_level=logging.ERROR):
499+ """Handle message from checks and append error messages to the collector."""
500+ if "message" not in message:
501+ log_level = logging.ERROR
502+ message = {"message": "wrong message_handler format"}
503+
504+ self._log_with_header(message["message"], level=log_level)
505+
506+ if self.collect_errors and log_level == logging.ERROR:
507+ self.collect(message)
508
509 def _process_includes_in_rules(self, yaml_txt):
510 """
511diff --git a/tests/unit/test_jujulint.py b/tests/unit/test_jujulint.py
512index 264b15e..77dbb5e 100644
513--- a/tests/unit/test_jujulint.py
514+++ b/tests/unit/test_jujulint.py
515@@ -8,6 +8,7 @@ import pytest
516 import yaml
517
518 from jujulint import check_spaces, lint, relations
519+from jujulint.lint import VALID_LOG_LEVEL
520
521
522 class TestUtils:
523@@ -536,22 +537,79 @@ applications:
524 assert errors[0]["id"] == "kubernetes-ops-charm-missing"
525 assert errors[0]["charm"] == "ntp"
526
527- def test_config_eq(self, linter, juju_status):
528- """Test the config condition 'eq'."""
529+ @pytest.mark.parametrize(
530+ "custom_message, log_level, generate_error",
531+ [
532+ ("", "", True), # generate error, no custom message
533+ ("custom message", "", True), # generate error with custom message
534+ ("", "foo", True), # generate error, no custom message
535+ (
536+ "custom message",
537+ "warning",
538+ False,
539+ ), # doesn't generate error with custom message
540+ (
541+ "custom message",
542+ "WARNING",
543+ False,
544+ ), # doesn't generate error with custom message
545+ ("", "info", False), # doesn't generate error, no custom message
546+ ("", "debug", False), # doesn't generate error, no custom message
547+ ],
548+ )
549+ def test_config_eq(
550+ self, linter, juju_status, custom_message, log_level, generate_error
551+ ):
552+ """Test the config condition 'eq' with custom messages and log levels."""
553 linter.lint_rules["config"] = {"ubuntu": {"fake-opt": {"eq": False}}}
554 juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": True}
555+ if custom_message:
556+ linter.lint_rules["config"]["ubuntu"]["fake-opt"][
557+ "custom-message"
558+ ] = custom_message
559+ if log_level:
560+ linter.lint_rules["config"]["ubuntu"]["fake-opt"]["log-level"] = log_level
561+
562 linter.do_lint(juju_status)
563
564 errors = linter.output_collector["errors"]
565- assert len(errors) == 1
566- assert errors[0]["id"] == "config-eq-check"
567- assert errors[0]["application"] == "ubuntu"
568- assert errors[0]["rule"] == "fake-opt"
569- assert errors[0]["expected_value"] is False
570- assert errors[0]["actual_value"] is True
571
572- def test_config_eq_suffix_match(self, linter, juju_status):
573- """Test the config condition 'eq'. when suffix matches."""
574+ if log_level.lower() != "error" and log_level.lower() in VALID_LOG_LEVEL:
575+ assert not generate_error
576+ assert len(errors) == 0
577+
578+ else:
579+ assert len(errors) == 1
580+ assert errors[0]["id"] == "config-eq-check"
581+ assert errors[0]["application"] == "ubuntu"
582+ assert errors[0]["rule"] == "fake-opt"
583+ assert errors[0]["expected_value"] is False
584+ assert errors[0]["actual_value"] is True
585+
586+ @pytest.mark.parametrize(
587+ "custom_message, log_level, generate_error",
588+ [
589+ ("", "", True), # generate error, no custom message
590+ ("custom message", "", True), # generate error with custom message
591+ ("", "foo", True), # generate error, no custom message
592+ (
593+ "custom message",
594+ "warning",
595+ False,
596+ ), # doesn't generate error with custom message
597+ (
598+ "custom message",
599+ "WARNING",
600+ False,
601+ ), # doesn't generate error with custom message
602+ ("", "info", False), # doesn't generate error, no custom message
603+ ("", "debug", False), # doesn't generate error, no custom message
604+ ],
605+ )
606+ def test_config_eq_suffix_match(
607+ self, linter, juju_status, custom_message, log_level, generate_error
608+ ):
609+ """Test the config condition 'eq'. when suffix matches with custom messages and log levels."""
610 linter.lint_rules["config"] = {
611 "ubuntu": {"fake-opt": {"eq": False, "suffixes": ["host", "physical"]}}
612 }
613@@ -559,31 +617,86 @@ applications:
614 juju_status["applications"]["ubuntu-host"] = juju_status["applications"].pop(
615 "ubuntu"
616 )
617+ if custom_message:
618+ linter.lint_rules["config"]["ubuntu"]["fake-opt"][
619+ "custom-message"
620+ ] = custom_message
621+ if log_level:
622+ linter.lint_rules["config"]["ubuntu"]["fake-opt"]["log-level"] = log_level
623+
624 linter.do_lint(juju_status)
625
626 errors = linter.output_collector["errors"]
627- assert len(errors) == 1
628- assert errors[0]["id"] == "config-eq-check"
629- assert errors[0]["application"] == "ubuntu-host"
630- assert errors[0]["rule"] == "fake-opt"
631- assert errors[0]["expected_value"] is False
632- assert errors[0]["actual_value"] is True
633
634- def test_config_eq_suffix_match_charm_name(self, linter, juju_status):
635+ if log_level.lower() != "error" and log_level.lower() in VALID_LOG_LEVEL:
636+ assert not generate_error
637+ assert len(errors) == 0
638+ else:
639+ assert len(errors) == 1
640+ assert errors[0]["id"] == "config-eq-check"
641+ assert errors[0]["application"] == "ubuntu-host"
642+ assert errors[0]["rule"] == "fake-opt"
643+ assert errors[0]["expected_value"] is False
644+ assert errors[0]["actual_value"] is True
645+ if custom_message:
646+ assert errors[0]["message"] == custom_message
647+ else:
648+ assert errors[0]["message"] != custom_message
649+
650+ @pytest.mark.parametrize(
651+ "custom_message, log_level, generate_error",
652+ [
653+ ("", "", True), # generate error, no custom message
654+ ("custom message", "", True), # generate error with custom message
655+ ("", "foo", True), # generate error, no custom message
656+ (
657+ "custom message",
658+ "warning",
659+ False,
660+ ), # doesn't generate error with custom message
661+ (
662+ "custom message",
663+ "WARNING",
664+ False,
665+ ), # doesn't generate error with custom message
666+ ("", "info", False), # doesn't generate error, no custom message
667+ ("", "debug", False), # doesn't generate error, no custom message
668+ ],
669+ )
670+ def test_config_eq_suffix_match_charm_name(
671+ self, linter, juju_status, custom_message, log_level, generate_error
672+ ):
673 """Test the config condition 'eq'. when suffix and base charm name."""
674 linter.lint_rules["config"] = {
675 "ubuntu": {"fake-opt": {"eq": False, "suffixes": ["host", "physical"]}}
676 }
677 juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": True}
678+ if custom_message:
679+ linter.lint_rules["config"]["ubuntu"]["fake-opt"][
680+ "custom-message"
681+ ] = custom_message
682+ if log_level:
683+ linter.lint_rules["config"]["ubuntu"]["fake-opt"]["log-level"] = log_level
684+
685 linter.do_lint(juju_status)
686
687 errors = linter.output_collector["errors"]
688- assert len(errors) == 1
689- assert errors[0]["id"] == "config-eq-check"
690- assert errors[0]["application"] == "ubuntu"
691- assert errors[0]["rule"] == "fake-opt"
692- assert errors[0]["expected_value"] is False
693- assert errors[0]["actual_value"] is True
694+
695+ if log_level.lower() != "error" and log_level.lower() in VALID_LOG_LEVEL:
696+ assert not generate_error
697+ assert len(errors) == 0
698+
699+ else:
700+ assert len(errors) == 1
701+ assert errors[0]["id"] == "config-eq-check"
702+ assert errors[0]["application"] == "ubuntu"
703+ assert errors[0]["rule"] == "fake-opt"
704+ assert errors[0]["expected_value"] is False
705+ assert errors[0]["actual_value"] is True
706+ if custom_message:
707+ assert errors[0]["message"] == custom_message
708+ else:
709+ assert errors[0]["message"] != custom_message
710
711 def test_config_eq_suffix_skip(self, linter, juju_status):
712 """Test the config condition 'eq'. when suffix doesn't match."""
713@@ -599,22 +712,61 @@ applications:
714 errors = linter.output_collector["errors"]
715 assert not errors
716
717- def test_config_eq_no_suffix_check_all(self, linter, juju_status):
718+ @pytest.mark.parametrize(
719+ "custom_message, log_level, generate_error",
720+ [
721+ ("", "", True), # generate error, no custom message
722+ ("custom message", "", True), # generate error with custom message
723+ ("", "foo", True), # generate error, no custom message
724+ (
725+ "custom message",
726+ "warning",
727+ False,
728+ ), # doesn't generate error with custom message
729+ (
730+ "custom message",
731+ "WARNING",
732+ False,
733+ ), # doesn't generate error with custom message
734+ ("", "info", False), # doesn't generate error, no custom message
735+ ("", "debug", False), # doesn't generate error, no custom message
736+ ],
737+ )
738+ def test_config_eq_no_suffix_check_all(
739+ self, linter, juju_status, custom_message, log_level, generate_error
740+ ):
741 """Test the config condition 'eq'. when no suffix all should be checked."""
742 linter.lint_rules["config"] = {"ubuntu": {"fake-opt": {"eq": False}}}
743 juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": True}
744 juju_status["applications"]["ubuntu-host"] = juju_status["applications"].pop(
745 "ubuntu"
746 )
747+ if custom_message:
748+ linter.lint_rules["config"]["ubuntu"]["fake-opt"][
749+ "custom-message"
750+ ] = custom_message
751+ if log_level:
752+ linter.lint_rules["config"]["ubuntu"]["fake-opt"]["log-level"] = log_level
753+
754 linter.do_lint(juju_status)
755
756 errors = linter.output_collector["errors"]
757- assert len(errors) == 1
758- assert errors[0]["id"] == "config-eq-check"
759- assert errors[0]["application"] == "ubuntu-host"
760- assert errors[0]["rule"] == "fake-opt"
761- assert errors[0]["expected_value"] is False
762- assert errors[0]["actual_value"] is True
763+
764+ if log_level.lower() != "error" and log_level.lower() in VALID_LOG_LEVEL:
765+ assert not generate_error
766+ assert len(errors) == 0
767+
768+ else:
769+ assert len(errors) == 1
770+ assert errors[0]["id"] == "config-eq-check"
771+ assert errors[0]["application"] == "ubuntu-host"
772+ assert errors[0]["rule"] == "fake-opt"
773+ assert errors[0]["expected_value"] is False
774+ assert errors[0]["actual_value"] is True
775+ if custom_message:
776+ assert errors[0]["message"] == custom_message
777+ else:
778+ assert errors[0]["message"] != custom_message
779
780 def test_config_neq_valid(self, linter, juju_status):
781 """Test the config condition 'neq'."""
782@@ -625,46 +777,164 @@ applications:
783 errors = linter.output_collector["errors"]
784 assert len(errors) == 0
785
786- def test_config_neq_invalid(self, linter, juju_status):
787- """Test the config condition 'neq', valid."""
788+ @pytest.mark.parametrize(
789+ "custom_message, log_level, generate_error",
790+ [
791+ ("", "", True), # generate error, no custom message
792+ ("custom message", "", True), # generate error with custom message
793+ ("", "foo", True), # generate error, no custom message
794+ (
795+ "custom message",
796+ "warning",
797+ False,
798+ ), # doesn't generate error with custom message
799+ (
800+ "custom message",
801+ "WARNING",
802+ False,
803+ ), # doesn't generate error with custom message
804+ ("", "info", False), # doesn't generate error, no custom message
805+ ("", "debug", False), # doesn't generate error, no custom message
806+ ],
807+ )
808+ def test_config_neq_invalid(
809+ self, linter, juju_status, custom_message, log_level, generate_error
810+ ):
811+ """Test the config condition 'neq', invalid with custom messages and log levels."""
812 linter.lint_rules["config"] = {"ubuntu": {"fake-opt": {"neq": ""}}}
813 juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": ""}
814+ if custom_message:
815+ linter.lint_rules["config"]["ubuntu"]["fake-opt"][
816+ "custom-message"
817+ ] = custom_message
818+ if log_level:
819+ linter.lint_rules["config"]["ubuntu"]["fake-opt"]["log-level"] = log_level
820+
821 linter.do_lint(juju_status)
822
823 errors = linter.output_collector["errors"]
824- assert len(errors) == 1
825- assert errors[0]["id"] == "config-neq-check"
826- assert errors[0]["application"] == "ubuntu"
827- assert errors[0]["rule"] == "fake-opt"
828- assert errors[0]["expected_value"] == ""
829- assert errors[0]["actual_value"] == ""
830
831- def test_config_gte(self, linter, juju_status):
832- """Test the config condition 'gte'."""
833+ if log_level.lower() != "error" and log_level.lower() in VALID_LOG_LEVEL:
834+ assert not generate_error
835+ assert len(errors) == 0
836+
837+ else:
838+ assert len(errors) == 1
839+ assert errors[0]["id"] == "config-neq-check"
840+ assert errors[0]["application"] == "ubuntu"
841+ assert errors[0]["rule"] == "fake-opt"
842+ assert errors[0]["expected_value"] == ""
843+ assert errors[0]["actual_value"] == ""
844+ if custom_message:
845+ assert errors[0]["message"] == custom_message
846+ else:
847+ assert errors[0]["message"] != custom_message
848+
849+ @pytest.mark.parametrize(
850+ "custom_message, log_level, generate_error",
851+ [
852+ ("", "", True), # generate error, no custom message
853+ ("custom message", "", True), # generate error with custom message
854+ ("", "foo", True), # generate error, no custom message
855+ (
856+ "custom message",
857+ "warning",
858+ False,
859+ ), # doesn't generate error with custom message
860+ (
861+ "custom message",
862+ "WARNING",
863+ False,
864+ ), # doesn't generate error with custom message
865+ ("", "info", False), # doesn't generate error, no custom message
866+ ("", "debug", False), # doesn't generate error, no custom message
867+ ],
868+ )
869+ def test_config_gte(
870+ self, linter, juju_status, custom_message, log_level, generate_error
871+ ):
872+ """Test the config condition 'gte' with custom messages and log levels."""
873 linter.lint_rules["config"] = {"ubuntu": {"fake-opt": {"gte": 3}}}
874 juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": 0}
875+ if custom_message:
876+ linter.lint_rules["config"]["ubuntu"]["fake-opt"][
877+ "custom-message"
878+ ] = custom_message
879+ if log_level:
880+ linter.lint_rules["config"]["ubuntu"]["fake-opt"]["log-level"] = log_level
881+
882 linter.do_lint(juju_status)
883
884 errors = linter.output_collector["errors"]
885- assert len(errors) == 1
886- assert errors[0]["id"] == "config-gte-check"
887- assert errors[0]["application"] == "ubuntu"
888- assert errors[0]["rule"] == "fake-opt"
889- assert errors[0]["expected_value"] == 3
890- assert errors[0]["actual_value"] == 0
891
892- def test_config_isset_false_fail(self, linter, juju_status):
893- """Test error handling if config condition 'isset'=false is not met."""
894+ if log_level.lower() != "error" and log_level.lower() in VALID_LOG_LEVEL:
895+ assert not generate_error
896+ assert len(errors) == 0
897+
898+ else:
899+ assert generate_error
900+ assert len(errors) == 1
901+ assert errors[0]["id"] == "config-gte-check"
902+ assert errors[0]["application"] == "ubuntu"
903+ assert errors[0]["rule"] == "fake-opt"
904+ assert errors[0]["expected_value"] == 3
905+ assert errors[0]["actual_value"] == 0
906+ if custom_message:
907+ assert errors[0]["message"] == custom_message
908+ else:
909+ assert errors[0]["message"] != custom_message
910+
911+ @pytest.mark.parametrize(
912+ "custom_message, log_level, generate_error",
913+ [
914+ ("", "", True), # generate error, no custom message
915+ ("custom message", "", True), # generate error with custom message
916+ ("", "foo", True), # generate error, no custom message
917+ (
918+ "custom message",
919+ "warning",
920+ False,
921+ ), # doesn't generate error with custom message
922+ (
923+ "custom message",
924+ "WARNING",
925+ False,
926+ ), # doesn't generate error with custom message
927+ ("", "info", False), # doesn't generate error, no custom message
928+ ("", "debug", False), # doesn't generate error, no custom message
929+ ],
930+ )
931+ def test_config_isset_false_fail(
932+ self, linter, juju_status, custom_message, log_level, generate_error
933+ ):
934+ """Test message handling if config condition 'isset'=false is not met."""
935 linter.lint_rules["config"] = {"ubuntu": {"fake-opt": {"isset": False}}}
936 juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": 0}
937+ if custom_message:
938+ linter.lint_rules["config"]["ubuntu"]["fake-opt"][
939+ "custom-message"
940+ ] = custom_message
941+ if log_level:
942+ linter.lint_rules["config"]["ubuntu"]["fake-opt"]["log-level"] = log_level
943+
944 linter.do_lint(juju_status)
945
946 errors = linter.output_collector["errors"]
947- assert len(errors) == 1
948- assert errors[0]["id"] == "config-isset-check-false"
949- assert errors[0]["application"] == "ubuntu"
950- assert errors[0]["rule"] == "fake-opt"
951- assert errors[0]["actual_value"] == 0
952+
953+ if log_level.lower() != "error" and log_level.lower() in VALID_LOG_LEVEL:
954+ assert not generate_error
955+ assert len(errors) == 0
956+
957+ else:
958+ assert len(errors) == 1
959+ assert errors[0]["id"] == "config-isset-check-false"
960+ assert errors[0]["application"] == "ubuntu"
961+ assert errors[0]["rule"] == "fake-opt"
962+ assert errors[0]["actual_value"] == 0
963+ if custom_message:
964+ assert errors[0]["message"] == custom_message
965+ else:
966+ assert errors[0]["message"] != custom_message
967
968 def test_config_isset_false_pass(self, linter, juju_status):
969 """Test handling if config condition 'isset'=false is met."""
970@@ -675,17 +945,56 @@ applications:
971 errors = linter.output_collector["errors"]
972 assert len(errors) == 0
973
974- def test_config_isset_true_fail(self, linter, juju_status):
975- """Test error handling if config condition 'isset'=true is not met."""
976+ @pytest.mark.parametrize(
977+ "custom_message, log_level, generate_error",
978+ [
979+ ("", "", True), # generate error, no custom message
980+ ("custom message", "", True), # generate error with custom message
981+ ("", "foo", True), # generate error, no custom message
982+ (
983+ "custom message",
984+ "warning",
985+ False,
986+ ), # doesn't generate error with custom message
987+ (
988+ "custom message",
989+ "WARNING",
990+ False,
991+ ), # doesn't generate error with custom message
992+ ("", "info", False), # doesn't generate error, no custom message
993+ ("", "debug", False), # doesn't generate error, no custom message
994+ ],
995+ )
996+ def test_config_isset_true_fail(
997+ self, linter, juju_status, custom_message, log_level, generate_error
998+ ):
999+ """Test message handling if config condition 'isset'=true is not met."""
1000 linter.lint_rules["config"] = {"ubuntu": {"fake-opt": {"isset": True}}}
1001 juju_status["applications"]["ubuntu"]["options"] = {}
1002+ if custom_message:
1003+ linter.lint_rules["config"]["ubuntu"]["fake-opt"][
1004+ "custom-message"
1005+ ] = custom_message
1006+ if log_level:
1007+ linter.lint_rules["config"]["ubuntu"]["fake-opt"]["log-level"] = log_level
1008+
1009 linter.do_lint(juju_status)
1010
1011 errors = linter.output_collector["errors"]
1012- assert len(errors) == 1
1013- assert errors[0]["id"] == "config-isset-check-true"
1014- assert errors[0]["application"] == "ubuntu"
1015- assert errors[0]["rule"] == "fake-opt"
1016+
1017+ if log_level.lower() != "error" and log_level.lower() in VALID_LOG_LEVEL:
1018+ assert not generate_error
1019+ assert len(errors) == 0
1020+
1021+ else:
1022+ assert len(errors) == 1
1023+ assert errors[0]["id"] == "config-isset-check-true"
1024+ assert errors[0]["application"] == "ubuntu"
1025+ assert errors[0]["rule"] == "fake-opt"
1026+ if custom_message:
1027+ assert errors[0]["message"] == custom_message
1028+ else:
1029+ assert errors[0]["message"] != custom_message
1030
1031 def test_config_isset_true_pass(self, linter, juju_status):
1032 """Test handling if config condition 'isset'=true is met."""
1033@@ -709,23 +1018,60 @@ applications:
1034 errors = linter.output_collector["errors"]
1035 assert len(errors) == 0
1036
1037- def test_config_search_invalid(self, linter, juju_status):
1038+ @pytest.mark.parametrize(
1039+ "custom_message, log_level, generate_error",
1040+ [
1041+ ("", "", True), # generate error, no custom message
1042+ ("custom message", "", True), # generate error with custom message
1043+ ("", "foo", True), # generate error, no custom message
1044+ (
1045+ "custom message",
1046+ "warning",
1047+ False,
1048+ ), # doesn't generate error with custom message
1049+ (
1050+ "custom message",
1051+ "WARNING",
1052+ False,
1053+ ), # doesn't generate error with custom message
1054+ ("", "info", False), # doesn't generate error, no custom message
1055+ ("", "debug", False), # doesn't generate error, no custom message
1056+ ],
1057+ )
1058+ def test_config_search_invalid(
1059+ self, linter, juju_status, custom_message, log_level, generate_error
1060+ ):
1061 """Test the config condition 'search' when invalid."""
1062 linter.lint_rules["config"] = {
1063- "ubuntu": {"fake_opt": {"search": "\\W\\*, \\W\\*, 25000, 27500"}}
1064+ "ubuntu": {"fake-opt": {"search": "\\W\\*, \\W\\*, 25000, 27500"}}
1065 }
1066 juju_status["applications"]["ubuntu"]["options"] = {
1067- "fake_opt": "[[/, queue1, 10, 20], [\\*, \\*, 10, 20]]"
1068+ "fake-opt": "[[/, queue1, 10, 20], [\\*, \\*, 10, 20]]"
1069 }
1070+ if custom_message:
1071+ linter.lint_rules["config"]["ubuntu"]["fake-opt"][
1072+ "custom-message"
1073+ ] = custom_message
1074+ if log_level:
1075+ linter.lint_rules["config"]["ubuntu"]["fake-opt"]["log-level"] = log_level
1076+
1077 linter.do_lint(juju_status)
1078
1079 errors = linter.output_collector["errors"]
1080- assert len(errors) == 1
1081- assert errors[0]["id"] == "config-search-check"
1082- assert errors[0]["application"] == "ubuntu"
1083- assert errors[0]["rule"] == "fake_opt"
1084- assert errors[0]["expected_value"] == "\\W\\*, \\W\\*, 25000, 27500"
1085- assert errors[0]["actual_value"] == "[[/, queue1, 10, 20], [\\*, \\*, 10, 20]]"
1086+
1087+ if log_level.lower() != "error" and log_level.lower() in VALID_LOG_LEVEL:
1088+ assert not generate_error
1089+ assert len(errors) == 0
1090+
1091+ else:
1092+ assert len(errors) == 1
1093+ assert errors[0]["id"] == "config-search-check"
1094+ assert errors[0]["application"] == "ubuntu"
1095+ assert errors[0]["rule"] == "fake-opt"
1096+ assert errors[0]["expected_value"] == "\\W\\*, \\W\\*, 25000, 27500"
1097+ assert (
1098+ errors[0]["actual_value"] == "[[/, queue1, 10, 20], [\\*, \\*, 10, 20]]"
1099+ )
1100
1101 def test_config_search_missing(self, linter, mocker):
1102 """Test the config search method logs warning if the config option is missing."""
1103@@ -1245,12 +1591,12 @@ applications:
1104 @pytest.mark.parametrize("input_file_type", ["juju-status", "juju-bundle"])
1105 def test_check_relations(self, linter, input_files, mocker, input_file_type):
1106 """Ensure that check_relation pass."""
1107- mock_handle_error = mocker.patch("jujulint.lint.Linter.handle_error")
1108+ mock_message_handler = mocker.patch("jujulint.lint.Linter.message_handler")
1109 linter.lint_rules["relations"] = [
1110 {"charm": "nrpe", "check": [["nrpe:juju-info", "ubuntu:juju-info"]]}
1111 ]
1112 linter.check_relations(input_files[input_file_type])
1113- mock_handle_error.assert_not_called()
1114+ mock_message_handler.assert_not_called()
1115
1116 @pytest.mark.parametrize(
1117 "input_file_type",
1118@@ -1260,11 +1606,11 @@ applications:
1119 ],
1120 )
1121 def test_check_relations_exception_handling(
1122- self, linter, juju_status, mocker, input_file_type, input_files
1123+ self, linter, mocker, input_file_type, input_files
1124 ):
1125 """Ensure that handle error if relation rules are in wrong format."""
1126 mock_log = mocker.patch("jujulint.lint.Linter._log_with_header")
1127- mock_handle_error = mocker.patch("jujulint.lint.Linter.handle_error")
1128+ mock_message_handler = mocker.patch("jujulint.lint.Linter.message_handler")
1129 linter.lint_rules["relations"] = [
1130 {"charm": "ntp", "check": [["ntp", "ubuntu"]]}
1131 ]
1132@@ -1275,17 +1621,17 @@ applications:
1133 expected_exception = relations.RelationError(expected_msg)
1134
1135 linter.check_relations(input_files[input_file_type])
1136- mock_handle_error.assert_not_called()
1137+ mock_message_handler.assert_not_called()
1138 mock_log.assert_has_calls(
1139 [mocker.call(expected_exception.message, level=logging.ERROR)]
1140 )
1141
1142 @pytest.mark.parametrize("input_file_type", ["juju-status", "juju-bundle"])
1143 def test_check_relations_missing_relations(
1144- self, linter, juju_status, mocker, input_file_type, input_files
1145+ self, linter, mocker, input_file_type, input_files
1146 ):
1147 """Ensure that check_relation handle missing relations."""
1148- mock_handle_error = mocker.patch("jujulint.lint.Linter.handle_error")
1149+ mock_message_handler = mocker.patch("jujulint.lint.Linter.message_handler")
1150 # add a relation rule that doesn't happen in the model
1151 linter.lint_rules["relations"] = [
1152 {
1153@@ -1294,7 +1640,7 @@ applications:
1154 }
1155 ]
1156 linter.check_relations(input_files[input_file_type])
1157- mock_handle_error.assert_called_with(
1158+ mock_message_handler.assert_called_with(
1159 {
1160 "id": "missing-relations",
1161 "tags": ["relation", "missing"],
1162@@ -1307,7 +1653,7 @@ applications:
1163 @pytest.mark.parametrize("input_file_type", ["juju-status", "juju-bundle"])
1164 def test_check_relations_exist(self, linter, input_files, mocker, input_file_type):
1165 """Ensure that check_relation handle not exist error."""
1166- mock_handle_error = mocker.patch("jujulint.lint.Linter.handle_error")
1167+ mock_message_handler = mocker.patch("jujulint.lint.Linter.message_handler")
1168 not_exist_relation = [
1169 "nrpe-host:nrpe-external-master",
1170 "elasticsearch:nrpe-external-master",
1171@@ -1317,7 +1663,7 @@ applications:
1172 {"charm": "nrpe", "not-exist": [not_exist_relation]}
1173 ]
1174 linter.check_relations(input_files[input_file_type])
1175- mock_handle_error.assert_called_with(
1176+ mock_message_handler.assert_called_with(
1177 {
1178 "id": "relation-exist",
1179 "tags": ["relation", "exist"],
1180@@ -1338,7 +1684,7 @@ applications:
1181 input_file.machines_data.update(new_machines)
1182 # map file again
1183 input_file.map_file()
1184- mock_handle_error = mocker.patch("jujulint.lint.Linter.handle_error")
1185+ mock_message_handler = mocker.patch("jujulint.lint.Linter.message_handler")
1186 linter.lint_rules["relations"] = [
1187 {
1188 "charm": "nrpe",
1189@@ -1348,7 +1694,7 @@ applications:
1190
1191 expected_missing_machines = ["2", "3"]
1192 linter.check_relations(input_file)
1193- mock_handle_error.assert_called_with(
1194+ mock_message_handler.assert_called_with(
1195 {
1196 "id": "missing-machine",
1197 "tags": ["missing", "machine"],
1198@@ -1358,3 +1704,44 @@ applications:
1199 ),
1200 }
1201 )
1202+
1203+ def test_path_mtu_for_ovs_ovn_rules(self, linter, rules_files):
1204+ """Test that ovs and ovn rules set right value of path-mtu."""
1205+ for rule in [rules_file for rules_file in rules_files if "fcb" in rules_file]:
1206+ linter.filename = rule
1207+ linter.read_rules()
1208+ path_mtu = linter.lint_rules["openstack config"]["neutron-api"]["path-mtu"]
1209+ if "ovs" in rule:
1210+ assert path_mtu["gte"] == 1550
1211+ else:
1212+ assert path_mtu["gte"] == 1558
1213+
1214+ assert path_mtu["log-level"] == "warning"
1215+ assert path_mtu["custom-message"]
1216+
1217+ @pytest.mark.parametrize(
1218+ "message, log_level, error",
1219+ [
1220+ ({}, None, True), # log wrong message_handler format as error message
1221+ ({}, logging.INFO, True), # log wrong message_handler format as error
1222+ ({"message": "my message"}, None, True), # log error message
1223+ ({"message": "my message"}, logging.INFO, False), # log info message
1224+ ({"message": "my message"}, logging.DEBUG, False), # log debug message
1225+ ({"message": "my message"}, logging.WARNING, False), # log warning message
1226+ ({"message": "my message"}, logging.ERROR, True), # log error message
1227+ ],
1228+ )
1229+ def test_message_handler(self, linter, mocker, message, log_level, error):
1230+ """Test message_handler method."""
1231+ logger_mock = mocker.patch.object(linter, "_log_with_header")
1232+ expected_message = message.get("message", "wrong message_handler format")
1233+ if message and not error:
1234+ linter.message_handler(message, log_level)
1235+ logger_mock.assert_has_calls(
1236+ [mocker.call(expected_message, level=log_level)]
1237+ )
1238+ else:
1239+ linter.message_handler(message)
1240+ logger_mock.assert_has_calls(
1241+ [mocker.call(expected_message, level=logging.ERROR)]
1242+ )

Subscribers

People subscribed via source and target branches