|
15 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 |
|
17 | 17 | import os |
| 18 | +import re |
18 | 19 |
|
19 | 20 | from sys import exit |
20 | 21 |
|
@@ -173,6 +174,13 @@ def get_config(config=None): |
173 | 174 |
|
174 | 175 | ethernet['flowtable_interfaces'] = get_flowtable_interfaces(conf) |
175 | 176 |
|
| 177 | + ethernet['vpp'] = conf.get_config_dict( |
| 178 | + ['vpp'], |
| 179 | + key_mangling=('-', '_'), |
| 180 | + get_first_key=True, |
| 181 | + no_tag_node_value_mangle=True, |
| 182 | + ) |
| 183 | + |
176 | 184 | # Protocols static arp dependency |
177 | 185 | if 'static_arp' in ethernet: |
178 | 186 | set_dependents('static_arp', conf) |
@@ -307,8 +315,59 @@ def verify_flowtable(ethernet: dict): |
307 | 315 | if vifcname in ethernet['flowtable_interfaces']: |
308 | 316 | raise ConfigError(f'Cannot delete interface "{vifcname}", still referenced on a flowtable') |
309 | 317 |
|
| 318 | +def verify_vpp_remove_vif(ethernet: dict): |
| 319 | + """Ensure that VIF interfaces being removed are not used by VPP features""" |
| 320 | + vpp_paths_pattern = re.compile( |
| 321 | + # Known paths that already use VLAN interfaces |
| 322 | + r'(nat\.cgnat\.interface\.inside)|' |
| 323 | + r'(nat\.cgnat\.interface\.outside)|' |
| 324 | + r'(nat44\.interface\.inside)|' |
| 325 | + r'(nat44\.interface\.outside)|' |
| 326 | + # Potential paths for VLAN interfaces |
| 327 | + r'(nat44\.address_pool\.translation\.interface)|' |
| 328 | + r'(nat44\.address_pool\.twice_nat\.interface)|' |
| 329 | + r'(nat44\.exclude\.rule\.(\d)+\.external_interface)|' |
| 330 | + r'(interfaces\.bonding\.bond(\d)+\.member\.interface)|' |
| 331 | + r'(interfaces\.bridge\.br(\d)+\.member\.interface)|' |
| 332 | + r'(interfaces\.xconnect\.xcon(\d)+\.member\.interface)|' |
| 333 | + r'(acl\.ip\.interface)|' |
| 334 | + r'(acl\.macip\.interface)' |
| 335 | + ) |
| 336 | + ifname = ethernet['ifname'] |
| 337 | + |
| 338 | + vlan_names = [ |
| 339 | + f'{ifname}.{vif_id}' |
| 340 | + for vif_group in ['vif_remove', 'vif_s_remove'] |
| 341 | + for vif_id in ethernet.get(vif_group, []) |
| 342 | + ] |
| 343 | + |
| 344 | + if not vlan_names: |
| 345 | + return |
| 346 | + |
| 347 | + vpp_flat = dict_to_paths_values(ethernet.get('vpp', {})) |
| 348 | + |
| 349 | + candidate_keys = [] |
| 350 | + for key, value in vpp_flat.items(): |
| 351 | + # Normalize values to list for consistent processing |
| 352 | + values = value if isinstance(value, list) else [value] |
| 353 | + if any(vlan in values for vlan in vlan_names): |
| 354 | + candidate_keys.append((key, values)) |
| 355 | + |
| 356 | + if not candidate_keys: |
| 357 | + return |
| 358 | + |
| 359 | + for key, values in candidate_keys: |
| 360 | + if vpp_paths_pattern.fullmatch(key): |
| 361 | + used_vlans = [v for v in vlan_names if v in values] |
| 362 | + if used_vlans: |
| 363 | + raise ConfigError( |
| 364 | + f'Cannot delete interface "{used_vlans[0]}", ' |
| 365 | + f'it is still in use by "vpp {key.replace(".", " ")}"' |
| 366 | + ) |
| 367 | + |
310 | 368 | def verify(ethernet): |
311 | 369 | verify_flowtable(ethernet) |
| 370 | + verify_vpp_remove_vif(ethernet) |
312 | 371 |
|
313 | 372 | if 'deleted' in ethernet: |
314 | 373 | return None |
|
0 commit comments