Skip to content

Commit a54a091

Browse files
authored
[GCU] Supressing YANG errors from libyang while sorting (#1991)
#### What I did Fixes #2025 Stopping SonicYang from printing errors during config validation or moves generation. If callers uses the `-v` option, they will see the SonicYang logs printed. Also made sure that SonicYang returned error are properly propagated, so the user can see the exact errors. #### How I did it Used the the option `print_log_enabled` while creating `SonicYang` ```python sy = sonic_yang.SonicYang(self.yang_dir, print_log_enabled=sonic_yang_print_log_enabled) ``` #### How to verify it Manually tested the change to the output, and added unit-tests to the gu_common functions that were modified. #### Previous command output (if the output of a command-line utility has changed) Given an invalid patch will remove PORT table, while the PORT table is used. ``` admin@vlab-01:~$ sudo config apply-patch remove_port.json-patch -i /FEATURE Patch Applier: Patch application starting. Patch Applier: Patch: [{"op": "remove", "path": "/PORT"}] Patch Applier: Getting current config db. Patch Applier: Simulating the target full config after applying the patch. Patch Applier: Validating target config does not have empty tables, since they do not show up in ConfigDb. Patch Applier: Sorting patch updates. sonic_yang(6):Note: Below table(s) have no YANG models: CONSOLE_SWITCH, DEVICE_NEIGHBOR_METADATA, DHCP_SERVER, KDUMP, RESTAPI, SNMP, SNMP_COMMUNITY, TELEMETRY libyang[0]: Leafref "/sonic-port:sonic-port/sonic-port:PORT/sonic-port:PORT_LIST/sonic-port:name" of value "Ethernet112" points to a non-existing leaf. (path: /sonic-buffer-pg:sonic-buffer-pg/BUFFER_PG/BUFFER_PG_LIST[port='Ethernet112'][pg_num='0']/port) libyang[0]: Leafref "/sonic-port:sonic-port/sonic-port:PORT/sonic-port:PORT_LIST/sonic-port:name" of value "Ethernet112" points to a non-existing leaf. (path: /sonic-buffer-pg:sonic-buffer-pg/BUFFER_PG/BUFFER_PG_LIST[port='Ethernet112'][pg_num='3-4']/port) libyang[0]: Leafref "/sonic-port:sonic-port/sonic-port:PORT/sonic-port:PORT_LIST/sonic-port:name" of value "Ethernet116" points to a non-existing leaf. (path: /sonic-buffer-pg:sonic-buffer-pg/BUFFER_PG/BUFFER_PG_LIST[port='Ethernet116'][pg_num='0']/port) >>>> 10s of log lines libyang[0]: Leafref "/sonic-port:sonic-port/sonic-port:PORT/sonic-port:PORT_LIST/sonic-port:name" of value "Ethernet96" points to a non-existing leaf. (path: /sonic-port-qos-map:sonic-port-qos-map/PORT_QOS_MAP/PORT_QOS_MAP_LIST[ifname='Ethernet96']/ifname) sonic_yang(3):Data Loading Failed:Leafref "/sonic-port:sonic-port/sonic-port:PORT/sonic-port:PORT_LIST/sonic-port:name" of value "Ethernet96" points to a non-existing leaf. Failed to apply patch Usage: config apply-patch [OPTIONS] PATCH_FILE_PATH Try "config apply-patch -h" for help. Error: Given patch is not valid because it will result in an invalid config admin@vlab-01:~$ ``` #### New command output (if the output of a command-line utility has changed) ``` admin@vlab-01:~$ sudo config apply-patch remove_port.json-patch -i /FEATURE Patch Applier: Patch application starting. Patch Applier: Patch: [{"op": "remove", "path": "/PORT"}] Patch Applier: Getting current config db. Patch Applier: Simulating the target full config after applying the patch. Patch Applier: Validating target config does not have empty tables, since they do not show up in ConfigDb. Patch Applier: Sorting patch updates. Failed to apply patch Usage: config apply-patch [OPTIONS] PATCH_FILE_PATH Try "config apply-patch -h" for help. Error: Given patch will produce invalid config. Error: Data Loading Failed Leafref "/sonic-port:sonic-port/sonic-port:PORT/sonic-port:PORT_LIST/sonic-port:name" of value "Ethernet96" points to a non-existing leaf. admin@vlab-01:~$ ```
1 parent fbfa8bc commit a54a091

File tree

4 files changed

+31
-19
lines changed

4 files changed

+31
-19
lines changed

generic_config_updater/gu_common.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ def validate_sonic_yang_config(self, sonic_yang_as_json):
105105
sy.loadData(config_db_as_json)
106106

107107
sy.validate_data_tree()
108-
return True
108+
return True, None
109109
except sonic_yang.SonicYangException as ex:
110-
return False
110+
return False, ex
111111

112112
def validate_config_db_config(self, config_db_as_json):
113113
sy = self.create_sonic_yang_with_loaded_models()
@@ -118,9 +118,9 @@ def validate_config_db_config(self, config_db_as_json):
118118
sy.loadData(tmp_config_db_as_json)
119119

120120
sy.validate_data_tree()
121-
return True
121+
return True, None
122122
except sonic_yang.SonicYangException as ex:
123-
return False
123+
return False, ex
124124

125125
def crop_tables_without_yang(self, config_db_as_json):
126126
sy = self.create_sonic_yang_with_loaded_models()
@@ -151,7 +151,8 @@ def remove_empty_tables(self, config):
151151
def create_sonic_yang_with_loaded_models(self):
152152
# sonic_yang_with_loaded_models will only be initialized once the first time this method is called
153153
if self.sonic_yang_with_loaded_models is None:
154-
loaded_models_sy = sonic_yang.SonicYang(self.yang_dir)
154+
sonic_yang_print_log_enabled = genericUpdaterLogging.get_verbose()
155+
loaded_models_sy = sonic_yang.SonicYang(self.yang_dir, print_log_enabled=sonic_yang_print_log_enabled)
155156
loaded_models_sy.loadYangModel() # This call takes a long time (100s of ms) because it reads files from disk
156157
self.sonic_yang_with_loaded_models = loaded_models_sy
157158

@@ -829,6 +830,9 @@ def __init__(self):
829830
def set_verbose(self, verbose):
830831
self._verbose = verbose
831832

833+
def get_verbose(self):
834+
return self._verbose
835+
832836
def get_logger(self, title, print_all_to_console=False):
833837
return TitledLogger(SYSLOG_IDENTIFIER, title, self._verbose, print_all_to_console)
834838

generic_config_updater/patch_sorter.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,8 @@ def __init__(self, config_wrapper):
562562

563563
def validate(self, move, diff):
564564
simulated_config = move.apply(diff.current_config)
565-
return self.config_wrapper.validate_config_db_config(simulated_config)
565+
is_valid, error = self.config_wrapper.validate_config_db_config(simulated_config)
566+
return is_valid
566567

567568
# TODO: Add this validation to YANG models instead
568569
class UniqueLanesMoveValidator:
@@ -1543,8 +1544,9 @@ def sort(self, patch, algorithm=Algorithm.DFS):
15431544

15441545
# Validate target config
15451546
self.logger.log_info("Validating target config according to YANG models.")
1546-
if not(self.config_wrapper.validate_config_db_config(target_config)):
1547-
raise ValueError(f"Given patch is not valid because it will result in an invalid config")
1547+
is_valid, error = self.config_wrapper.validate_config_db_config(target_config)
1548+
if not is_valid:
1549+
raise ValueError(f"Given patch will produce invalid config. Error: {error}")
15481550

15491551
# Generate list of changes to apply
15501552
self.logger.log_info("Sorting patch updates.")
@@ -1731,8 +1733,9 @@ def sort(self, patch, algorithm=Algorithm.DFS):
17311733

17321734
# Validate YANG covered target config
17331735
self.logger.log_info("Validating YANG covered target config according to YANG models.")
1734-
if not(self.config_wrapper.validate_config_db_config(target_config_yang)):
1735-
raise ValueError(f"Given patch is not valid because it will result in an invalid config")
1736+
is_valid, error = self.config_wrapper.validate_config_db_config(target_config_yang)
1737+
if not is_valid:
1738+
raise ValueError(f"Given patch will produce invalid config. Error: {error}")
17361739

17371740
# Generating changes associated with non-YANG covered configs
17381741
self.logger.log_info("Sorting non-YANG covered configs patch updates.")

tests/generic_config_updater/gu_common_test.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -144,43 +144,47 @@ def test_validate_sonic_yang_config__valid_config__returns_true(self):
144144
expected = True
145145

146146
# Act
147-
actual = config_wrapper.validate_sonic_yang_config(Files.SONIC_YANG_AS_JSON)
147+
actual, error = config_wrapper.validate_sonic_yang_config(Files.SONIC_YANG_AS_JSON)
148148

149149
# Assert
150150
self.assertEqual(expected, actual)
151+
self.assertIsNone(error)
151152

152153
def test_validate_sonic_yang_config__invvalid_config__returns_false(self):
153154
# Arrange
154155
config_wrapper = gu_common.ConfigWrapper()
155156
expected = False
156157

157158
# Act
158-
actual = config_wrapper.validate_sonic_yang_config(Files.SONIC_YANG_AS_JSON_INVALID)
159+
actual, error = config_wrapper.validate_sonic_yang_config(Files.SONIC_YANG_AS_JSON_INVALID)
159160

160161
# Assert
161162
self.assertEqual(expected, actual)
163+
self.assertIsNotNone(error)
162164

163165
def test_validate_config_db_config__valid_config__returns_true(self):
164166
# Arrange
165167
config_wrapper = gu_common.ConfigWrapper()
166168
expected = True
167169

168170
# Act
169-
actual = config_wrapper.validate_config_db_config(Files.CONFIG_DB_AS_JSON)
171+
actual, error = config_wrapper.validate_config_db_config(Files.CONFIG_DB_AS_JSON)
170172

171173
# Assert
172174
self.assertEqual(expected, actual)
175+
self.assertIsNone(error)
173176

174177
def test_validate_config_db_config__invalid_config__returns_false(self):
175178
# Arrange
176179
config_wrapper = gu_common.ConfigWrapper()
177180
expected = False
178181

179182
# Act
180-
actual = config_wrapper.validate_config_db_config(Files.CONFIG_DB_AS_JSON_INVALID)
183+
actual, error = config_wrapper.validate_config_db_config(Files.CONFIG_DB_AS_JSON_INVALID)
181184

182185
# Assert
183186
self.assertEqual(expected, actual)
187+
self.assertIsNotNone(error)
184188

185189
def test_crop_tables_without_yang__returns_cropped_config_db_as_json(self):
186190
# Arrange

tests/generic_config_updater/patch_sorter_test.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,7 @@ def test_validate__invalid_config_db_after_applying_move__failure(self):
925925
# Arrange
926926
config_wrapper = Mock()
927927
config_wrapper.validate_config_db_config.side_effect = \
928-
create_side_effect_dict({(str(self.any_simulated_config),): False})
928+
create_side_effect_dict({(str(self.any_simulated_config),): (False, None)})
929929
validator = ps.FullConfigMoveValidator(config_wrapper)
930930

931931
# Act and assert
@@ -935,7 +935,7 @@ def test_validate__valid_config_db_after_applying_move__success(self):
935935
# Arrange
936936
config_wrapper = Mock()
937937
config_wrapper.validate_config_db_config.side_effect = \
938-
create_side_effect_dict({(str(self.any_simulated_config),): True})
938+
create_side_effect_dict({(str(self.any_simulated_config),): (True, None)})
939939
validator = ps.FullConfigMoveValidator(config_wrapper)
940940

941941
# Act and assert
@@ -3100,7 +3100,8 @@ def run_single_success_case(self, data, skip_exact_change_list_match):
31003100
simulated_config = current_config
31013101
for change in actual_changes:
31023102
simulated_config = change.apply(simulated_config)
3103-
self.assertTrue(self.config_wrapper.validate_config_db_config(simulated_config))
3103+
is_valid, error = self.config_wrapper.validate_config_db_config(simulated_config)
3104+
self.assertTrue(is_valid, f"Change will produce invalid config. Error: {error}")
31043105
self.assertEqual(target_config, simulated_config)
31053106

31063107
def test_patch_sorter_failure(self):
@@ -3424,7 +3425,7 @@ def __create_patch_sorter(self,
34243425
(str(any_target_config),): (any_target_config_yang, any_target_config_non_yang)})
34253426

34263427
config_wrapper.validate_config_db_config.side_effect = \
3427-
create_side_effect_dict({(str(any_target_config_yang),): valid_yang_covered_config})
3428+
create_side_effect_dict({(str(any_target_config_yang),): (valid_yang_covered_config, None)})
34283429

34293430
patch_wrapper.generate_patch.side_effect = \
34303431
create_side_effect_dict(
@@ -3517,7 +3518,7 @@ def __create_patch_sorter(self,
35173518

35183519
config_wrapper.validate_config_db_config.side_effect = \
35193520
create_side_effect_dict(
3520-
{(str(any_target_config),): valid_config_db})
3521+
{(str(any_target_config),): (valid_config_db, None)})
35213522

35223523

35233524
inner_patch_sorter.sort.side_effect = \

0 commit comments

Comments
 (0)