From 4d732c68e8eb63a890b3a8971f51ca26047279a1 Mon Sep 17 00:00:00 2001 From: Mohamed Ghoneim Date: Thu, 14 Oct 2021 04:30:19 -0700 Subject: [PATCH] [generic_config_updater] Logging (#1864) #### What I did Add some logs to generic_updater #### How I did it #### How to verify it #### Previous command output (if the output of a command-line utility has changed) #### New command output (if the output of a command-line utility has changed) Empty patch ``` admin@vlab-01:~$ sudo config apply-patch empty.json-patch Patch Applier: Patch application starting. Patch Applier: Patch: [] Patch Applier: Validating patch is not making changes to tables without YANG models. Patch Applier: Getting current config db. Patch Applier: Simulating the target full config after applying the patch. Patch Applier: Validating target config according to YANG models. ... [logs from sonic-yang-mgmt framework] Patch Applier: Sorting patch updates. Patch Applier: The patch was sorted into 0 changes. Patch Applier: Applying changes in order. Patch Applier: Verifying patch updates are reflected on ConfigDB. Patch Applier: Patch application completed. Patch applied successfully. admin@vlab-01:~$ ``` Single change patch ``` admin@vlab-01:~$ sudo config apply-patch dhcp_add.json-patch Patch Applier: Patch application starting. Patch Applier: Patch: [{"op": "add", "path": "/VLAN/Vlan1000/dhcp_servers/4", "value": "192.0.0.5"}] Patch Applier: Validating patch is not making changes to tables without YANG models. Patch Applier: Getting current config db. Patch Applier: Simulating the target full config after applying the patch. Patch Applier: Validating target config according to YANG models. ... [logs from sonic-yang-mgmt framework] Patch Applier: The patch was sorted into 1 change: Patch Applier: * [{"op": "add", "path": "/VLAN/Vlan1000/dhcp_servers/4", "value": "192.0.0.5"}] Patch Applier: Applying changes in order. Failed to apply patch Usage: config apply-patch [OPTIONS] PATCH_FILE_PATH Try "config apply-patch -h" for help. Error: ChangeApplier.apply(change) is not implemented yet admin@vlab-01:~$ ``` Multi change patch: ``` admin@vlab-01:~$ sudo config apply-patch update_lanes.json-patch Patch Applier: Patch application starting. Patch Applier: Patch: [{"op": "replace", "path": "/PORT/Ethernet100/lanes", "value": "121,122,123"}] Patch Applier: Validating patch is not making changes to tables without YANG models. Patch Applier: Getting current config db. Patch Applier: Simulating the target full config after applying the patch. Patch Applier: Validating target config according to YANG models. ... [logs from sonic-yang-mgmt framework] Patch Applier: Sorting patch updates. ... [logs from sonic-yang-mgmt framework] Patch Applier: The patch was sorted into 2 changes: Patch Applier: * [{"op": "remove", "path": "/PORT/Ethernet100"}] Patch Applier: * [{"op": "add", "path": "/PORT/Ethernet100", "value": {"alias": "fortyGigE0/100", "description": "fortyGigE0/100", "index": "25", "lanes": "121,122,123", "mtu": "9100", "pfc_asym": "off", "speed": "40000", "tpid": "0x8100"}}] Patch Applier: Applying changes in order. Failed to apply patch Usage: config apply-patch [OPTIONS] PATCH_FILE_PATH Try "config apply-patch -h" for help. Error: ChangeApplier.apply(change) is not implemented yet admin@vlab-01:~$ ``` --- generic_config_updater/generic_updater.py | 28 ++++++++++++++++++----- generic_config_updater/gu_common.py | 25 ++++++++++++++++++++ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/generic_config_updater/generic_updater.py b/generic_config_updater/generic_updater.py index 8fd36ced9140..8205a20b27b1 100644 --- a/generic_config_updater/generic_updater.py +++ b/generic_config_updater/generic_updater.py @@ -2,7 +2,7 @@ import os from enum import Enum from .gu_common import GenericConfigUpdaterError, ConfigWrapper, \ - DryRunConfigWrapper, PatchWrapper + DryRunConfigWrapper, PatchWrapper, genericUpdaterLogging from .patch_sorter import PatchSorter CHECKPOINTS_DIR = "/etc/sonic/checkpoints" @@ -32,38 +32,58 @@ def __init__(self, changeapplier=None, config_wrapper=None, patch_wrapper=None): + self.logger = genericUpdaterLogging.get_logger(title="Patch Applier") self.config_wrapper = config_wrapper if config_wrapper is not None else ConfigWrapper() self.patch_wrapper = patch_wrapper if patch_wrapper is not None else PatchWrapper() self.patchsorter = patchsorter if patchsorter is not None else PatchSorter(self.config_wrapper, self.patch_wrapper) self.changeapplier = changeapplier if changeapplier is not None else ChangeApplier() def apply(self, patch): + print_to_console=True + self.logger.log_notice("Patch application starting.", print_to_console) + self.logger.log_notice(f"Patch: {patch}", print_to_console) + # validate patch is only updating tables with yang models + self.logger.log_notice("Validating patch is not making changes to tables without YANG models.", print_to_console) if not(self.patch_wrapper.validate_config_db_patch_has_yang_models(patch)): raise ValueError(f"Given patch is not valid because it has changes to tables without YANG models") # Get old config + self.logger.log_notice("Getting current config db.", print_to_console) old_config = self.config_wrapper.get_config_db_as_json() # Generate target config + self.logger.log_notice("Simulating the target full config after applying the patch.", print_to_console) target_config = self.patch_wrapper.simulate_patch(patch, old_config) # Validate target config + self.logger.log_notice("Validating target config according to YANG models.", print_to_console) if not(self.config_wrapper.validate_config_db_config(target_config)): raise ValueError(f"Given patch is not valid because it will result in an invalid config") # Generate list of changes to apply + self.logger.log_notice("Sorting patch updates.", print_to_console) changes = self.patchsorter.sort(patch) + changes_len = len(changes) + self.logger.log_notice(f"The patch was sorted into {changes_len} " \ + f"change{'s' if changes_len != 1 else ''}{':' if changes_len > 0 else '.'}", + print_to_console) + for change in changes: + self.logger.log_notice(f" * {change}", print_to_console) # Apply changes in order + self.logger.log_notice("Applying changes in order.", print_to_console) for change in changes: self.changeapplier.apply(change) # Validate config updated successfully + self.logger.log_notice("Verifying patch updates are reflected on ConfigDB.", print_to_console) new_config = self.config_wrapper.get_config_db_as_json() if not(self.patch_wrapper.verify_same_json(target_config, new_config)): raise GenericConfigUpdaterError(f"After applying patch to config, there are still some parts not updated") + self.logger.log_notice("Patch application completed.", print_to_console) + class ConfigReplacer: def __init__(self, patch_applier=None, config_wrapper=None, patch_wrapper=None): self.patch_applier = patch_applier if patch_applier is not None else PatchApplier() @@ -293,11 +313,7 @@ def create_config_rollbacker(self, verbose, dry_run=False): return config_rollbacker def init_verbose_logging(self, verbose): - # TODO: implement verbose logging - # Usually logs have levels such as: error, warning, info, debug. - # By default all log levels should show up to the user, except debug. - # By allowing verbose logging, debug msgs will also be shown to the user. - pass + genericUpdaterLogging.set_verbose(verbose) def get_config_wrapper(self, dry_run): if dry_run: diff --git a/generic_config_updater/gu_common.py b/generic_config_updater/gu_common.py index 66d9b0d7d9bc..be1f6e5db71b 100644 --- a/generic_config_updater/gu_common.py +++ b/generic_config_updater/gu_common.py @@ -6,9 +6,11 @@ import yang as ly import copy import re +from sonic_py_common import logger from enum import Enum YANG_DIR = "/usr/local/yang-models" +SYSLOG_IDENTIFIER = "GenericConfigUpdater" class GenericConfigUpdaterError(Exception): pass @@ -691,3 +693,26 @@ def _get_model(self, model, name): return submodel return None + +class TitledLogger(logger.Logger): + def __init__(self, syslog_identifier, title, verbose): + super().__init__(syslog_identifier) + self._title = title + if verbose: + self.set_min_log_priority_debug() + + def log(self, priority, msg, also_print_to_console=False): + combined_msg = f"{self._title}: {msg}" + super().log(priority, combined_msg, also_print_to_console) + +class GenericUpdaterLogging: + def __init__(self): + self.set_verbose(False) + + def set_verbose(self, verbose): + self._verbose = verbose + + def get_logger(self, title): + return TitledLogger(SYSLOG_IDENTIFIER, title, self._verbose) + +genericUpdaterLogging = GenericUpdaterLogging()