2020from netaddr .strategy import eui48
2121from neutron_lib .agent import constants as agent_consts
2222from neutron_lib .agent import topics
23+ from neutron_lib .api import converters
2324from neutron_lib .api .definitions import address_group as addrgrp_def
2425from neutron_lib .api .definitions import address_scope
2526from neutron_lib .api .definitions import agent as agent_apidef
@@ -1563,11 +1564,36 @@ def _after_create_port(self, context, result, mech_context):
15631564
15641565 return bound_context .current
15651566
1566- def allocate_ips_for_ports (self , context , ports ):
1567+ def allocate_macs_and_ips_for_ports (self , context , ports ):
1568+ macs = self ._generate_macs (len (ports ))
1569+ network_cache = dict ()
15671570 for port in ports :
15681571 port ['port' ]['id' ] = (
15691572 port ['port' ].get ('id' ) or uuidutils .generate_uuid ())
15701573
1574+ network_id = port ['port' ].get ('network_id' )
1575+ if network_id not in network_cache :
1576+ network = self .get_network (context , network_id )
1577+ network_cache [network_id ] = network
1578+
1579+ raw_mac_address = port ['port' ].get ('mac_address' ,
1580+ const .ATTR_NOT_SPECIFIED )
1581+ if raw_mac_address is const .ATTR_NOT_SPECIFIED :
1582+ raw_mac_address = macs .pop ()
1583+ elif self ._is_mac_in_use (context , network_id , raw_mac_address ):
1584+ raise exc .MacAddressInUse (net_id = network_id ,
1585+ mac = raw_mac_address )
1586+ eui_mac_address = converters .convert_to_sanitized_mac_address (
1587+ raw_mac_address )
1588+ # Create the Port object
1589+ # Note: netaddr has an issue with using the correct dialect when
1590+ # the input for EUI is another EUI object, see:
1591+ # https://github.com/netaddr/netaddr/issues/250
1592+ # TODO(lajoskatona): remove this once
1593+ # https://review.opendev.org/c/openstack/neutron-lib/+/865517 is
1594+ # released.
1595+ port ['port' ]['mac_address' ] = str (eui_mac_address )
1596+
15711597 # Call IPAM to allocate IP addresses
15721598 try :
15731599 port ['ipams' ] = self .ipam .allocate_ips_for_port (context , port )
@@ -1577,18 +1603,19 @@ def allocate_ips_for_ports(self, context, ports):
15771603 except ipam_exc .DeferIpam :
15781604 port ['ip_allocation' ] = (ipalloc_apidef .
15791605 IP_ALLOCATION_DEFERRED )
1580- return ports
1606+ return ports , network_cache
15811607
15821608 @utils .transaction_guard
15831609 def create_port_bulk (self , context , ports ):
15841610 port_list = ports .get ('ports' )
15851611 for port in port_list :
15861612 self ._before_create_port (context , port )
15871613
1588- port_list = self .allocate_ips_for_ports (context , port_list )
1614+ port_list , net_cache = self .allocate_macs_and_ips_for_ports (
1615+ context , port_list )
15891616
15901617 try :
1591- return self ._create_port_bulk (context , port_list )
1618+ return self ._create_port_bulk (context , port_list , net_cache )
15921619 except Exception :
15931620 with excutils .save_and_reraise_exception ():
15941621 # If any issue happened allocated IP addresses needs to be
@@ -1598,17 +1625,16 @@ def create_port_bulk(self, context, ports):
15981625 context , port , port ['ipams' ])
15991626
16001627 @db_api .retry_if_session_inactive ()
1601- def _create_port_bulk (self , context , port_list ):
1628+ def _create_port_bulk (self , context , port_list , network_cache ):
16021629 # TODO(njohnston): Break this up into smaller functions.
16031630 port_data = []
1604- network_cache = dict ()
1605- macs = self ._generate_macs (len (port_list ))
16061631 with db_api .CONTEXT_WRITER .using (context ):
16071632 for port in port_list :
16081633 # Set up the port request dict
16091634 pdata = port .get ('port' )
16101635 project_id = pdata .get ('project_id' ) or pdata .get ('tenant_id' )
16111636 security_group_ids = pdata .get ('security_groups' )
1637+ network_id = pdata .get ('network_id' )
16121638 if security_group_ids is const .ATTR_NOT_SPECIFIED :
16131639 security_group_ids = None
16141640 else :
@@ -1620,39 +1646,21 @@ def _create_port_bulk(self, context, port_list):
16201646 bulk_port_data = dict (
16211647 project_id = project_id ,
16221648 name = pdata .get ('name' ),
1623- network_id = pdata . get ( ' network_id' ) ,
1649+ network_id = network_id ,
16241650 admin_state_up = pdata .get ('admin_state_up' ),
16251651 status = pdata .get ('status' ,
16261652 const .PORT_STATUS_ACTIVE ),
16271653 device_id = pdata .get ('device_id' ),
16281654 device_owner = pdata .get ('device_owner' ),
16291655 description = pdata .get ('description' ))
16301656
1631- # Ensure that the networks exist.
1632- network_id = pdata .get ('network_id' )
1633- if network_id not in network_cache :
1634- network = self .get_network (context , network_id )
1635- network_cache [network_id ] = network
1636- else :
1637- network = network_cache [network_id ]
1638-
1639- # Determine the MAC address
1640- raw_mac_address = pdata .get ('mac_address' ,
1641- const .ATTR_NOT_SPECIFIED )
1642- if raw_mac_address is const .ATTR_NOT_SPECIFIED :
1643- raw_mac_address = macs .pop ()
1644- elif self ._is_mac_in_use (context , network_id , raw_mac_address ):
1645- raise exc .MacAddressInUse (net_id = network_id ,
1646- mac = raw_mac_address )
1647- eui_mac_address = netaddr .EUI (raw_mac_address ,
1648- dialect = eui48 .mac_unix_expanded )
1649- port ['port' ]['mac_address' ] = str (eui_mac_address )
1650-
1651- # Create the Port object
1652- db_port_obj = ports_obj .Port (context ,
1653- mac_address = eui_mac_address ,
1654- id = port ['port' ]['id' ],
1655- ** bulk_port_data )
1657+ network = network_cache [network_id ]
1658+
1659+ db_port_obj = ports_obj .Port (
1660+ context ,
1661+ mac_address = netaddr .EUI (port ['port' ]['mac_address' ],
1662+ dialect = eui48 .mac_unix_expanded ),
1663+ id = port ['port' ]['id' ], ** bulk_port_data )
16561664 db_port_obj .create ()
16571665
16581666 # Call IPAM to store allocated IP addresses
0 commit comments