11
11
12
12
from neutron_understack import config
13
13
from neutron_understack import utils
14
+ from neutron_understack import vlan_group_name_convention
14
15
from neutron_understack .nautobot import Nautobot
15
16
from neutron_understack .trunk import UnderStackTrunkDriver
16
17
from neutron_understack .undersync import Undersync
17
18
from neutron_understack .vlan_manager import VlanManager
18
19
19
20
LOG = logging .getLogger (__name__ )
20
21
22
+ # We take interest in port binding events for Networks of these types:
23
+ RELEVANT_NETWORK_TYPES = [
24
+ p_const .TYPE_VXLAN ,
25
+ p_const .TYPE_VLAN ,
26
+ ]
21
27
22
28
config .register_ml2_type_understack_opts (cfg .CONF )
23
29
config .register_ml2_understack_opts (cfg .CONF )
@@ -284,22 +290,35 @@ def _configure_switchport_on_bind(self, context: PortContext) -> None:
284
290
)
285
291
286
292
def bind_port (self , context : PortContext ) -> None :
293
+ """Bind port hook.
294
+
295
+ Example segment: {
296
+ 'id': '308b7fd5-e3bc-4b64-b491-910480e4c3e4',
297
+ 'network_type': 'vxlan',
298
+ 'physical_network': None,
299
+ 'segmentation_id': 200025,
300
+ 'network_id': '6af047b2-65ff-48dc-aa57-9647119c4883'
301
+ }
302
+ """
287
303
for segment in context .network .network_segments :
288
- if self . check_segment ( segment ) :
304
+ if segment [ api . NETWORK_TYPE ] in RELEVANT_NETWORK_TYPES :
289
305
context .set_binding (
290
306
segment [api .ID ],
291
307
portbindings .VIF_TYPE_OTHER ,
292
308
{},
293
309
status = p_const .PORT_STATUS_ACTIVE ,
294
310
)
295
311
LOG .debug ("Bound segment: %s" , segment )
312
+ self ._find_or_create_network_segment (context )
296
313
self ._configure_switchport_on_bind (context )
297
- return
314
+
315
+ return # bind one segment only
298
316
else :
299
317
LOG .debug (
300
- "Refusing to bind port for segment ID %(id)s, "
318
+ "Ignoring bind_port for segment ID %(id)s, "
301
319
"segment %(seg)s, phys net %(physnet)s, and "
302
- "network type %(nettype)s" ,
320
+ "network type %(nettype)s because network_type "
321
+ "is not VXLAN or VLAN type" ,
303
322
{
304
323
"id" : segment [api .ID ],
305
324
"seg" : segment [api .SEGMENTATION_ID ],
@@ -308,17 +327,40 @@ def bind_port(self, context: PortContext) -> None:
308
327
},
309
328
)
310
329
311
- def check_segment (self , segment ):
312
- """Verify a segment is valid for the Understack MechanismDriver .
330
+ def _find_or_create_network_segment (self , context : PortContext ):
331
+ """Ensure that a VLAN-type network segment is present for this network .
313
332
314
- Verify the requested segment is supported by Understack and return True or
315
- False to indicate this to callers.
333
+ In the VXLAN fabric, a Network has a VLAN created on every switch where
334
+ that network is in use. The VLAN is local to the VLAN GROUP (pair of
335
+ switches) and must be assigned an arbitrary vlan number that is unique
336
+ within that VLAN GROUP.
337
+
338
+ We use the switch name to figure out which VLAN GROUP this network is
339
+ being bound to. We then look for existing network segments in that VLAN
340
+ GROUP. If none are found then we create a new one with a dynamically
341
+ allocated VLAN ID.
316
342
"""
317
- network_type = segment [api .NETWORK_TYPE ]
318
- return network_type in [
319
- p_const .TYPE_VXLAN ,
320
- p_const .TYPE_VLAN ,
321
- ]
343
+ switch_name = str (
344
+ context .current ["binding:profile" ]["local_link_information" ][0 ]["switch_info" ]
345
+ )
346
+
347
+ vlan_group_name = vlan_group_name_convention .for_switch (switch_name )
348
+
349
+ for segment in context .network .network_segments :
350
+ if (
351
+ segment .get (api .NETWORK_TYPE ) == p_const .TYPE_VLAN
352
+ and segment .get (api .PHYSICAL_NETWORK ) == vlan_group_name
353
+ ):
354
+ LOG .info ("Using existing VLAN network segment %s" , segment )
355
+ return segment
356
+
357
+ segment_args = {
358
+ api .NETWORK_TYPE : p_const .TYPE_VLAN ,
359
+ api .PHYSICAL_NETWORK : vlan_group_name ,
360
+ }
361
+ segment = context .allocate_dynamic_segment (segment_args )
362
+ LOG .info ("Allocated new VLAN network segment %s" , segment )
363
+ return segment
322
364
323
365
def check_vlan_transparency (self , context ):
324
366
pass
0 commit comments