|
5 | 5 | import importlib |
6 | 6 | import os |
7 | 7 | import tempfile |
| 8 | +import time |
8 | 9 | from collections import defaultdict |
9 | 10 | from swsscommon.swsscommon import ConfigDBConnector |
10 | 11 | from sonic_py_common import multi_asic |
11 | 12 | from .gu_common import GenericConfigUpdaterError, genericUpdaterLogging |
12 | | -from .gu_common import get_config_db_as_json |
| 13 | +from .gu_common import JsonChange |
13 | 14 |
|
14 | 15 | SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) |
15 | 16 | UPDATER_CONF_FILE = f"{SCRIPT_DIR}/gcu_services_validator.conf.json" |
@@ -64,8 +65,8 @@ class DryRunChangeApplier: |
64 | 65 | def __init__(self, config_wrapper): |
65 | 66 | self.config_wrapper = config_wrapper |
66 | 67 |
|
67 | | - def apply(self, change): |
68 | | - self.config_wrapper.apply_change_to_config_db(change) |
| 68 | + def apply(self, current_configdb: dict, change: JsonChange) -> dict: |
| 69 | + return self.config_wrapper.apply_change_to_config_db(current_configdb, change) |
69 | 70 |
|
70 | 71 | def remove_backend_tables_from_config(self, data): |
71 | 72 | return data |
@@ -137,25 +138,45 @@ def _report_mismatch(self, run_data, upd_data): |
137 | 138 | log_error("run_data vs expected_data: {}".format( |
138 | 139 | str(jsondiff.diff(run_data, upd_data))[0:40])) |
139 | 140 |
|
140 | | - def apply(self, change): |
141 | | - run_data = get_config_db_as_json(self.scope) |
142 | | - upd_data = prune_empty_table(change.apply(copy.deepcopy(run_data))) |
| 141 | + def apply(self, current_configdb: dict, change: JsonChange) -> dict: |
| 142 | + run_data = current_configdb |
| 143 | + upd_data = prune_empty_table(change.apply(run_data, in_place=False)) |
143 | 144 | upd_keys = defaultdict(dict) |
144 | 145 |
|
145 | 146 | for tbl in sorted(set(run_data.keys()).union(set(upd_data.keys()))): |
146 | 147 | self._upd_data(tbl, run_data.get(tbl, {}), upd_data.get(tbl, {}), upd_keys) |
147 | 148 |
|
148 | 149 | ret = self._services_validate(run_data, upd_data, upd_keys) |
149 | | - if not ret: |
150 | | - run_data = get_config_db_as_json(self.scope) |
151 | | - self.remove_backend_tables_from_config(upd_data) |
152 | | - self.remove_backend_tables_from_config(run_data) |
153 | | - if upd_data != run_data: |
154 | | - self._report_mismatch(run_data, upd_data) |
155 | | - ret = -1 |
156 | | - if ret: |
| 150 | + # The above function returns 0 on success as it uses shell return codes |
| 151 | + if ret != 0: |
157 | 152 | log_error("Failed to apply Json change") |
158 | | - return ret |
| 153 | + |
| 154 | + # There was a sanity check in this position originally that appeared |
| 155 | + # to be development-time code to ensure things were operating correctly. |
| 156 | + # It would retrieve the configdb from Redis and perform transformation |
| 157 | + # and comparison. Its not possible for the configuration to not be what |
| 158 | + # we expect since we have a known state we are mutating with a lock. |
| 159 | + # That said we are leaving in the final configuration comparison in |
| 160 | + # PatchApplier "just in case". |
| 161 | + # |
| 162 | + # However, this code did hide a pretty nasty race condition since there |
| 163 | + # is no feedback loop for when config_db changes are actually consumed. |
| 164 | + # This check would consume high CPU and would take a good amount of |
| 165 | + # time (0.5s - 1s). |
| 166 | + # |
| 167 | + # The below sleep is functionally equivalent in terms of preventing the |
| 168 | + # race condition (without the high CPU that might cause other control |
| 169 | + # plane issues), but is of course not the proper fix. |
| 170 | + # |
| 171 | + # An upstream SONiC issue will be opened for the race condition, and |
| 172 | + # until resolved leaving this comment in place for future reference. |
| 173 | + time.sleep(1) |
| 174 | + |
| 175 | + # Interestingly this function returns the updated data and doesn't |
| 176 | + # propagate an error. Maybe it should? Or are exceptions thrown |
| 177 | + # from _upd_data on failure? We seem to intentionally only log on |
| 178 | + # _services_validate() |
| 179 | + return upd_data |
159 | 180 |
|
160 | 181 | def remove_backend_tables_from_config(self, data): |
161 | 182 | for key in self.backend_tables: |
|
0 commit comments