@@ -29,118 +29,170 @@ def __init__(self,
29
29
changeapplier = None ,
30
30
config_wrapper = None ,
31
31
patch_wrapper = None ):
32
- self .logger = genericUpdaterLogging .get_logger (title = "Patch Applier" )
32
+ self .logger = genericUpdaterLogging .get_logger (title = "Patch Applier" , print_all_to_console = True )
33
33
self .config_wrapper = config_wrapper if config_wrapper is not None else ConfigWrapper ()
34
34
self .patch_wrapper = patch_wrapper if patch_wrapper is not None else PatchWrapper ()
35
35
self .patchsorter = patchsorter if patchsorter is not None else PatchSorter (self .config_wrapper , self .patch_wrapper )
36
36
self .changeapplier = changeapplier if changeapplier is not None else ChangeApplier ()
37
37
38
38
def apply (self , patch ):
39
- print_to_console = True
40
- self .logger .log_notice ("Patch application starting." , print_to_console )
41
- self .logger .log_notice (f"Patch: { patch } " , print_to_console )
39
+ self .logger .log_notice ("Patch application starting." )
40
+ self .logger .log_notice (f"Patch: { patch } " )
42
41
43
42
# validate patch is only updating tables with yang models
44
- self .logger .log_notice ("Validating patch is not making changes to tables without YANG models." , print_to_console )
43
+ self .logger .log_notice ("Validating patch is not making changes to tables without YANG models." )
45
44
if not (self .patch_wrapper .validate_config_db_patch_has_yang_models (patch )):
46
45
raise ValueError (f"Given patch is not valid because it has changes to tables without YANG models" )
47
46
48
47
# Get old config
49
- self .logger .log_notice ("Getting current config db." , print_to_console )
48
+ self .logger .log_notice ("Getting current config db." )
50
49
old_config = self .config_wrapper .get_config_db_as_json ()
51
50
52
51
# Generate target config
53
- self .logger .log_notice ("Simulating the target full config after applying the patch." , print_to_console )
52
+ self .logger .log_notice ("Simulating the target full config after applying the patch." )
54
53
target_config = self .patch_wrapper .simulate_patch (patch , old_config )
55
54
56
55
# Validate target config
57
- self .logger .log_notice ("Validating target config according to YANG models." , print_to_console )
56
+ self .logger .log_notice ("Validating target config according to YANG models." )
58
57
if not (self .config_wrapper .validate_config_db_config (target_config )):
59
58
raise ValueError (f"Given patch is not valid because it will result in an invalid config" )
60
59
61
60
# Generate list of changes to apply
62
- self .logger .log_notice ("Sorting patch updates." , print_to_console )
61
+ self .logger .log_notice ("Sorting patch updates." )
63
62
changes = self .patchsorter .sort (patch )
64
63
changes_len = len (changes )
65
64
self .logger .log_notice (f"The patch was sorted into { changes_len } " \
66
- f"change{ 's' if changes_len != 1 else '' } { ':' if changes_len > 0 else '.' } " ,
67
- print_to_console )
65
+ f"change{ 's' if changes_len != 1 else '' } { ':' if changes_len > 0 else '.' } " )
68
66
for change in changes :
69
- self .logger .log_notice (f" * { change } " , print_to_console )
67
+ self .logger .log_notice (f" * { change } " )
70
68
71
69
# Apply changes in order
72
- self .logger .log_notice ("Applying changes in order." , print_to_console )
70
+ self .logger .log_notice ("Applying changes in order." )
73
71
for change in changes :
74
72
self .changeapplier .apply (change )
75
73
76
74
# Validate config updated successfully
77
- self .logger .log_notice ("Verifying patch updates are reflected on ConfigDB." , print_to_console )
75
+ self .logger .log_notice ("Verifying patch updates are reflected on ConfigDB." )
78
76
new_config = self .config_wrapper .get_config_db_as_json ()
79
77
if not (self .patch_wrapper .verify_same_json (target_config , new_config )):
80
78
raise GenericConfigUpdaterError (f"After applying patch to config, there are still some parts not updated" )
81
79
82
- self .logger .log_notice ("Patch application completed." , print_to_console )
80
+ self .logger .log_notice ("Patch application completed." )
83
81
84
82
class ConfigReplacer :
85
83
def __init__ (self , patch_applier = None , config_wrapper = None , patch_wrapper = None ):
84
+ self .logger = genericUpdaterLogging .get_logger (title = "Config Replacer" , print_all_to_console = True )
86
85
self .patch_applier = patch_applier if patch_applier is not None else PatchApplier ()
87
86
self .config_wrapper = config_wrapper if config_wrapper is not None else ConfigWrapper ()
88
87
self .patch_wrapper = patch_wrapper if patch_wrapper is not None else PatchWrapper ()
89
88
90
89
def replace (self , target_config ):
90
+ self .logger .log_notice ("Config replacement starting." )
91
+ self .logger .log_notice (f"Target config length: { len (json .dumps (target_config ))} ." )
92
+
93
+ self .logger .log_notice ("Validating target config according to YANG models." )
91
94
if not (self .config_wrapper .validate_config_db_config (target_config )):
92
95
raise ValueError (f"The given target config is not valid" )
93
96
97
+ self .logger .log_notice ("Getting current config db." )
94
98
old_config = self .config_wrapper .get_config_db_as_json ()
99
+
100
+ self .logger .log_notice ("Generating patch between target config and current config db." )
95
101
patch = self .patch_wrapper .generate_patch (old_config , target_config )
102
+ self .logger .log_debug (f"Generated patch: { patch } ." ) # debug since the patch will printed again in 'patch_applier.apply'
96
103
104
+ self .logger .log_notice ("Applying patch using 'Patch Applier'." )
97
105
self .patch_applier .apply (patch )
98
106
107
+ self .logger .log_notice ("Verifying config replacement is reflected on ConfigDB." )
99
108
new_config = self .config_wrapper .get_config_db_as_json ()
100
109
if not (self .patch_wrapper .verify_same_json (target_config , new_config )):
101
110
raise GenericConfigUpdaterError (f"After replacing config, there is still some parts not updated" )
102
111
112
+ self .logger .log_notice ("Config replacement completed." )
113
+
103
114
class FileSystemConfigRollbacker :
104
115
def __init__ (self ,
105
116
checkpoints_dir = CHECKPOINTS_DIR ,
106
117
config_replacer = None ,
107
118
config_wrapper = None ):
119
+ self .logger = genericUpdaterLogging .get_logger (title = "Config Rollbacker" , print_all_to_console = True )
108
120
self .checkpoints_dir = checkpoints_dir
109
121
self .config_replacer = config_replacer if config_replacer is not None else ConfigReplacer ()
110
122
self .config_wrapper = config_wrapper if config_wrapper is not None else ConfigWrapper ()
111
123
112
124
def rollback (self , checkpoint_name ):
125
+ self .logger .log_notice ("Config rollbacking starting." )
126
+ self .logger .log_notice (f"Checkpoint name: { checkpoint_name } ." )
127
+
128
+ self .logger .log_notice (f"Verifying '{ checkpoint_name } ' exists." )
113
129
if not self ._check_checkpoint_exists (checkpoint_name ):
114
130
raise ValueError (f"Checkpoint '{ checkpoint_name } ' does not exist" )
115
131
132
+ self .logger .log_notice (f"Loading checkpoint into memory." )
116
133
target_config = self ._get_checkpoint_content (checkpoint_name )
117
134
135
+ self .logger .log_notice (f"Replacing config using 'Config Replacer'." )
118
136
self .config_replacer .replace (target_config )
119
137
138
+ self .logger .log_notice ("Config rollbacking completed." )
139
+
120
140
def checkpoint (self , checkpoint_name ):
141
+ self .logger .log_notice ("Config checkpoint starting." )
142
+ self .logger .log_notice (f"Checkpoint name: { checkpoint_name } ." )
143
+
144
+ self .logger .log_notice ("Getting current config db." )
121
145
json_content = self .config_wrapper .get_config_db_as_json ()
122
146
147
+ # if current config are not valid, we might not be able to rollback to it. So fail early by not taking checkpoint at all.
148
+ self .logger .log_notice ("Validating current config according to YANG models." )
123
149
if not self .config_wrapper .validate_config_db_config (json_content ):
124
150
raise ValueError (f"Running configs on the device are not valid." )
125
151
152
+ self .logger .log_notice ("Getting checkpoint full-path." )
126
153
path = self ._get_checkpoint_full_path (checkpoint_name )
127
154
155
+ self .logger .log_notice ("Ensuring checkpoint directory exist." )
128
156
self ._ensure_checkpoints_dir_exists ()
129
157
158
+ self .logger .log_notice (f"Saving config db content to { path } ." )
130
159
self ._save_json_file (path , json_content )
131
160
161
+ self .logger .log_notice ("Config checkpoint completed." )
162
+
132
163
def list_checkpoints (self ):
164
+ self .logger .log_info ("Listing checkpoints starting." )
165
+
166
+ self .logger .log_info (f"Verifying checkpoints directory '{ self .checkpoints_dir } ' exists." )
133
167
if not self ._checkpoints_dir_exist ():
168
+ self .logger .log_info ("Checkpoints directory is empty, returning empty checkpoints list." )
134
169
return []
135
170
136
- return self ._get_checkpoint_names ()
171
+ self .logger .log_info ("Getting checkpoints in checkpoints directory." )
172
+ checkpoint_names = self ._get_checkpoint_names ()
173
+
174
+ checkpoints_len = len (checkpoint_names )
175
+ self .logger .log_info (f"Found { checkpoints_len } checkpoint{ 's' if checkpoints_len != 1 else '' } { ':' if checkpoints_len > 0 else '.' } " )
176
+ for checkpoint_name in checkpoint_names :
177
+ self .logger .log_info (f" * { checkpoint_name } " )
178
+
179
+ self .logger .log_info ("Listing checkpoints completed." )
180
+
181
+ return checkpoint_names
137
182
138
183
def delete_checkpoint (self , checkpoint_name ):
184
+ self .logger .log_notice ("Deleting checkpoint starting." )
185
+ self .logger .log_notice (f"Checkpoint name: { checkpoint_name } ." )
186
+
187
+ self .logger .log_notice (f"Checking checkpoint exists." )
139
188
if not self ._check_checkpoint_exists (checkpoint_name ):
140
189
raise ValueError (f"Checkpoint '{ checkpoint_name } ' does not exist" )
141
190
191
+ self .logger .log_notice (f"Deleting checkpoint." )
142
192
self ._delete_checkpoint (checkpoint_name )
143
193
194
+ self .logger .log_notice ("Deleting checkpoint completed." )
195
+
144
196
def _ensure_checkpoints_dir_exists (self ):
145
197
os .makedirs (self .checkpoints_dir , exist_ok = True )
146
198
0 commit comments