@@ -102,10 +102,11 @@ def _generate_esi_warnings() -> None:
102102 text = f'EVPN Ethernet Segment "{ esi_name } " seems to be used only once ({ esi_use [0 ]} )' ,
103103 module = _config_name ,
104104 flag = 'evpn_mh_segment_used_once' ,
105+ more_hints = [ f'This could be due to a typo on Ethernet Segment name on the interface' ],
105106 )
106107 # ESI LAG LACP mismatch
107108 for esi_name , esi_lacp_ids in _esi_stats .esi_lacp_id .items ():
108- if len (esi_lacp_ids ) != 1 :
109+ if len (esi_lacp_ids ) > 1 :
109110 mismatch_list = []
110111 for m in list (esi_lacp_ids ):
111112 found_interfaces = "," .join (_esi_stats .esi_lacp_id_info [esi_name ][m ])
@@ -118,6 +119,73 @@ def _generate_esi_warnings() -> None:
118119 )
119120 return
120121
122+ def _unsupported_device_checks (node : Box ) -> None :
123+ for intf in node .get ('interfaces' ,[]):
124+ if intf .get ('evpn.es' , False ):
125+ log .error (
126+ f'Node { node .name } ({ node .device } ) does not support EVPN Multihoming'
127+ f' (found "evpn.es" attribute on interface { intf .ifname } )' ,
128+ category = log .IncorrectAttr ,
129+ module = _config_name )
130+ return
131+
132+ def _es_interface_attribute_validation (topology : Box , node : Box , intf : Box ) -> bool :
133+ global _config_name
134+ features = devices .get_device_features (node ,topology .defaults )
135+ intf_es = intf .evpn .es
136+ intf_es_data = node .evpn .ethernet_segments [intf_es ]
137+ # Check for interface type support
138+ if intf_es and intf .type not in _es_supported_on :
139+ log .error (
140+ f'EVPN Ethernet Segment is supported only on LAG or "physical" interfaces '
141+ f'(found on: Node { node .name } ({ node .device } ) - interface { intf .ifname } ({ intf .type } ))' ,
142+ category = log .IncorrectAttr ,
143+ module = _config_name )
144+ return False
145+ # Check for explicit lag support
146+ if intf .type == 'lag' and not features .get ('evpn.multihoming.lag' , False ):
147+ log .error (
148+ f'Node { node .name } ({ node .device } ) does not support EVPN Ethernet Segment on LAG interfaces (found on: { intf .ifname } )' ,
149+ category = log .IncorrectAttr ,
150+ module = _config_name )
151+ return False
152+ # Check for other interface support (except lag)
153+ if intf .type in list (set (_es_supported_on ) - set (['lag' ])) and not features .get ('evpn.multihoming.interface' , False ):
154+ log .error (
155+ f'Node { node .name } ({ node .device } ) does not support EVPN Ethernet Segment on "physical" interfaces (found on: { intf .ifname } )' ,
156+ category = log .IncorrectAttr ,
157+ module = _config_name )
158+ return False
159+ # ES data: if none of auto or id value is specified, trigger error
160+ if intf_es_data is None or not (intf_es_data .get ('auto' ,False ) or intf_es_data .get ('id' ,False )):
161+ log .error (
162+ f'Node { node .name } ({ node .device } ) no valid EVPN Ethernet Segment Identifier configuration for { intf_es } (on interface { intf .ifname } )' ,
163+ category = log .IncorrectAttr ,
164+ module = _config_name )
165+ return False
166+ # ESI-LAG **requires** a manually assigned LACP System ID (unless we auto generate it)
167+ if intf .type == 'lag' and not intf .get ('lag.lacp_system_id' ,False ):
168+ log .error (
169+ f'Node { node .name } ({ node .device } ) cannot use ESI-LAG interface without "lacp_system_id" ({ intf .ifname } )' ,
170+ category = log .IncorrectAttr ,
171+ module = _config_name )
172+ return False
173+ # ES data: if auto value, and not lacp system id is defined (or not lag or not supported), trigger an error
174+ if intf_es_data .get ('auto' ,False ):
175+ if intf .type != 'lag' :
176+ log .error (
177+ f'Node { node .name } ({ node .device } ) cannot use auto ESI on non-LAG interface ({ intf .ifname } )' ,
178+ category = log .IncorrectAttr ,
179+ module = _config_name )
180+ return False
181+ if not features .get ('evpn.multihoming.esi_auto' , False ):
182+ log .error (
183+ f'Node { node .name } ({ node .device } ) cannot use auto ESI on interface { intf .ifname } - auto generation not supported.' ,
184+ category = log .IncorrectAttr ,
185+ module = _config_name )
186+ return False
187+ return True
188+
121189def post_transform (topology : Box ) -> None :
122190 global _config_name
123191 global _auto_segments
@@ -127,9 +195,11 @@ def post_transform(topology: Box) -> None:
127195 if not 'evpn' in node .module : continue
128196 features = devices .get_device_features (node ,topology .defaults )
129197 es_supported = 'evpn.multihoming' in features
130- if not es_supported : continue
198+ if not es_supported :
199+ _unsupported_device_checks (node )
200+ continue
131201 # Load Ethernet Segments data and expand it with auto segments
132- es_data = node .get ( ' evpn.ethernet_segments' , {})
202+ es_data = node .evpn .ethernet_segments
133203 es_data .update (_auto_segments )
134204 for intf in node .get ('interfaces' ,[]):
135205 intf_es = intf .get ('evpn.es' , '' )
@@ -144,57 +214,10 @@ def post_transform(topology: Box) -> None:
144214 # If LAG, auto generated ESI-ID and LACP-System-ID are not present, use also auto generated LACP ID
145215 if intf .type == 'lag' and not intf .get ('lag.lacp_system_id' ,False ) and '_lacp_system_id' in es_data [intf_es ]:
146216 intf .lag .lacp_system_id = es_data [intf_es ]._lacp_system_id
147- # Error Validation checks
148- if intf_es and intf .type not in _es_supported_on :
149- log .error (
150- f'EVPN Ethernet Segment is supported only on LAG or "physical" interfaces '
151- f'(found on: Node { node .name } ({ node .device } ) - interface { intf .ifname } ({ intf .type } ))' ,
152- category = log .IncorrectAttr ,
153- module = _config_name )
154- return
155- # Check for explicit lag support
156- if intf .type == 'lag' and not features .get ('evpn.multihoming.lag' , False ):
157- log .error (
158- f'Node { node .name } ({ node .device } ) does not support EVPN Ethernet Segment on LAG interfaces (found on: { intf .ifname } )' ,
159- category = log .IncorrectAttr ,
160- module = _config_name )
161- return
162- # Check for other interface support (except lag)
163- if intf .type in list (set (_es_supported_on ) - set (['lag' ])) and not features .get ('evpn.multihoming.interface' , False ):
164- log .error (
165- f'Node { node .name } ({ node .device } ) does not support EVPN Ethernet Segment on "physical" interfaces (found on: { intf .ifname } )' ,
166- category = log .IncorrectAttr ,
167- module = _config_name )
168- return
217+ # Error Validation checks - exit in case of errors
218+ if not _es_interface_attribute_validation (topology , node , intf ): return
219+ # Update interface attributes
169220 intf_es_data = es_data [intf_es ]
170- # if none of auto or id value is specified, trigger error
171- if intf_es_data is None or not (es_data [intf_es ].get ('auto' ,False ) or es_data [intf_es ].get ('id' ,False )):
172- log .error (
173- f'Node { node .name } ({ node .device } ) no valid EVPN Ethernet Segment Identifier configuration for { intf_es } (on interface { intf .ifname } )' ,
174- category = log .IncorrectAttr ,
175- module = _config_name )
176- return
177- # ESI-LAG **requires** a manually assigned LACP System ID (unless we auto generate it)
178- if intf .type == 'lag' and not intf .get ('lag.lacp_system_id' ,False ):
179- log .error (
180- f'Node { node .name } ({ node .device } ) cannot use ESI-LAG interface without "lacp_system_id" ({ intf .ifname } )' ,
181- category = log .IncorrectAttr ,
182- module = _config_name )
183- return
184- # if auto value, and not lacp system id is defined (or not lag or not supported), trigger an error
185- if es_data [intf_es ].get ('auto' ,False ):
186- if intf .type != 'lag' :
187- log .error (
188- f'Node { node .name } ({ node .device } ) cannot use auto ESI on non-LAG interface ({ intf .ifname } )' ,
189- category = log .IncorrectAttr ,
190- module = _config_name )
191- return
192- if not features .get ('evpn.multihoming.esi_auto' , False ):
193- log .error (
194- f'Node { node .name } ({ node .device } ) cannot use auto ESI on interface { intf .ifname } - auto generation not supported.' ,
195- category = log .IncorrectAttr ,
196- module = _config_name )
197- return
198221 # if interface is _mlag, ESI-LAG have the precedence: pop _mlag
199222 if intf .type == 'lag' and '_mlag' in intf .lag :
200223 intf .lag .pop ('_mlag' )
0 commit comments