Add bulk port creation of DB objects
Implement a new function called create_port_obj_bulk that optimizes bulk port creation operations by streamlining ensuring network existence and operates on an array of port data. Change-Id: Ie819c215944514d0bb43c2ce87394825bda41e94 Partially-Implements: blueprint speed-up-neutron-bulk-creation
This commit is contained in:
parent
dd14501c12
commit
80b48ebd4a
|
@ -61,6 +61,7 @@ from neutron.objects import network as network_obj
|
|||
from neutron.objects import ports as port_obj
|
||||
from neutron.objects import subnet as subnet_obj
|
||||
from neutron.objects import subnetpool as subnetpool_obj
|
||||
from neutron.plugins.ml2.common import exceptions as ml2_exceptions
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -1266,6 +1267,24 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
|
|||
def create_port_bulk(self, context, ports):
|
||||
return self._create_bulk('port', context, ports)
|
||||
|
||||
def _create_db_port_obj_bulk(self, context, port_data):
|
||||
db_ports = []
|
||||
macs = self._generate_macs(len(port_data))
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
for port in port_data:
|
||||
raw_mac_address = port.pop('mac_address',
|
||||
constants.ATTR_NOT_SPECIFIED)
|
||||
if raw_mac_address is constants.ATTR_NOT_SPECIFIED:
|
||||
raw_mac_address = macs.pop()
|
||||
eui_mac_address = netaddr.EUI(raw_mac_address, 48)
|
||||
db_port_obj = port_obj.Port(context,
|
||||
mac_address=eui_mac_address,
|
||||
id=uuidutils.generate_uuid(),
|
||||
**port)
|
||||
db_port_obj.create()
|
||||
db_ports.append(db_port_obj)
|
||||
return db_ports
|
||||
|
||||
def _create_db_port_obj(self, context, port_data):
|
||||
mac_address = port_data.pop('mac_address', None)
|
||||
if mac_address:
|
||||
|
@ -1284,6 +1303,50 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
|
|||
db_port = self.create_port_db(context, port)
|
||||
return self._make_port_dict(db_port, process_extensions=False)
|
||||
|
||||
def create_port_obj_bulk(self, context, ports):
|
||||
bulk_port_data = []
|
||||
network_ids = set()
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
for port in ports:
|
||||
fixed_ips = port['port'].get('fixed_ips')
|
||||
if fixed_ips is not constants.ATTR_NOT_SPECIFIED:
|
||||
raise ml2_exceptions.BulkPortCannotHaveFixedIpError()
|
||||
pdata = port['port']
|
||||
if pdata.get('device_owner'):
|
||||
self._enforce_device_owner_not_router_intf_or_device_id(
|
||||
context, pdata.get('device_owner'),
|
||||
pdata.get('device_id'), pdata.get('tenant_id'))
|
||||
bulk_port_data.append(dict(project_id=pdata.get('project_id'),
|
||||
name=pdata.get('name'),
|
||||
network_id=pdata.get('network_id'),
|
||||
admin_state_up=pdata.get('admin_state_up'),
|
||||
status=pdata.get('status',
|
||||
constants.PORT_STATUS_ACTIVE),
|
||||
mac_address=pdata.get('mac_address'),
|
||||
device_id=pdata.get('device_id'),
|
||||
device_owner=pdata.get('device_owner'),
|
||||
description=pdata.get('description')))
|
||||
|
||||
# Ensure that the networks exist.
|
||||
network_id = pdata.get('network_id')
|
||||
if network_id not in network_ids:
|
||||
self._get_network(context, network_id)
|
||||
network_ids.add(network_id)
|
||||
|
||||
db_ports = self._create_db_port_obj_bulk(context, bulk_port_data)
|
||||
|
||||
for db_port in db_ports:
|
||||
try:
|
||||
self.ipam.allocate_ips_for_port_and_store(
|
||||
context, db_port, db_port['id'])
|
||||
db_port['ip_allocation'] = (ipalloc_apidef.
|
||||
IP_ALLOCATION_IMMEDIATE)
|
||||
except ipam_exc.DeferIpam:
|
||||
db_port['ip_allocation'] = (ipalloc_apidef.
|
||||
IP_ALLOCATION_DEFERRED)
|
||||
|
||||
return db_ports
|
||||
|
||||
def create_port_db(self, context, port):
|
||||
p = port['port']
|
||||
port_id = p.get('id') or uuidutils.generate_uuid()
|
||||
|
|
|
@ -165,7 +165,14 @@ class IpamPluggableBackend(ipam_backend_mixin.IpamBackendMixin):
|
|||
# Deepcopy doesn't work correctly in this case, because copy of
|
||||
# ATTR_NOT_SPECIFIED object happens. Address of copied object doesn't
|
||||
# match original object, so 'is' check fails
|
||||
port_copy = {'port': port['port'].copy()}
|
||||
# TODO(njohnston): Different behavior is required depending on whether
|
||||
# a Port object is used or not; once conversion to OVO is complete only
|
||||
# the 'else' will be needed.
|
||||
if isinstance(port, dict):
|
||||
port_copy = {'port': port['port'].copy()}
|
||||
else:
|
||||
port_copy = {'port': port.to_dict()}
|
||||
|
||||
port_copy['port']['id'] = port_id
|
||||
network_id = port_copy['port']['network_id']
|
||||
ips = []
|
||||
|
|
|
@ -42,6 +42,12 @@ class ExtensionDriverNotFound(exceptions.InvalidConfigurationOption):
|
|||
"service plugin %(service_plugin)s not found.")
|
||||
|
||||
|
||||
class BulkPortCannotHaveFixedIpError(exceptions.InvalidInput):
|
||||
"""You cannot request fixed IP addresses in a bulk port request."""
|
||||
message = _("Fixed IP addresses cannot be requested from a bulk port "
|
||||
"allocation.")
|
||||
|
||||
|
||||
class UnknownNetworkType(exceptions.NeutronException):
|
||||
"""Network with unknown type."""
|
||||
message = _("Unknown network type %(network_type)s.")
|
||||
|
|
|
@ -21,6 +21,7 @@ import itertools
|
|||
import eventlet
|
||||
import mock
|
||||
import netaddr
|
||||
from neutron_lib.api import validators
|
||||
from neutron_lib.callbacks import exceptions
|
||||
from neutron_lib.callbacks import registry
|
||||
from neutron_lib import constants
|
||||
|
@ -59,6 +60,7 @@ from neutron.ipam.drivers.neutrondb_ipam import driver as ipam_driver
|
|||
from neutron.ipam import exceptions as ipam_exc
|
||||
from neutron.objects import network as network_obj
|
||||
from neutron.objects import router as l3_obj
|
||||
from neutron.plugins.ml2.common import exceptions as ml2_exceptions
|
||||
from neutron.tests import base
|
||||
from neutron.tests import tools
|
||||
from neutron.tests.unit.api import test_extensions
|
||||
|
@ -2579,6 +2581,58 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
|
|||
plugin = directory.get_plugin()
|
||||
self._test_delete_ports_ignores_port_not_found(plugin)
|
||||
|
||||
def test_create_port_obj_bulk(self):
|
||||
cfg.CONF.set_override('base_mac', "12:34:56:00")
|
||||
test_mac = "00-12-34-56-78-90"
|
||||
num_ports = 4
|
||||
plugin = directory.get_plugin()
|
||||
tenant_id = 'some_tenant'
|
||||
device_owner = "me"
|
||||
ctx = context.Context('', tenant_id)
|
||||
with self.network(tenant_id=tenant_id) as network_to_use:
|
||||
net_id = network_to_use['network']['id']
|
||||
port = {'port': {'name': 'port',
|
||||
'network_id': net_id,
|
||||
'mac_address': constants.ATTR_NOT_SPECIFIED,
|
||||
'fixed_ips': constants.ATTR_NOT_SPECIFIED,
|
||||
'admin_state_up': True,
|
||||
'device_id': 'device_id',
|
||||
'device_owner': device_owner,
|
||||
'tenant_id': tenant_id}}
|
||||
ports = [copy.deepcopy(port) for x in range(num_ports)]
|
||||
ports[1]['port']['mac_address'] = test_mac
|
||||
port_data = plugin.create_port_obj_bulk(ctx, ports)
|
||||
self.assertEqual(num_ports, len(port_data))
|
||||
result_macs = []
|
||||
for port in port_data:
|
||||
port_mac = str(port.get('mac_address'))
|
||||
self.assertIsNone(validators.validate_mac_address(port_mac))
|
||||
result_macs.append(port_mac)
|
||||
for ip_addr in port.get('fixed_ips'):
|
||||
self.assertIsNone(validators.validate_ip_address(ip_addr))
|
||||
self.assertTrue(test_mac in result_macs)
|
||||
|
||||
def test_create_port_obj_bulk_with_fixed_ips(self):
|
||||
num_ports = 4
|
||||
plugin = directory.get_plugin()
|
||||
tenant_id = 'some_tenant'
|
||||
device_owner = "me"
|
||||
ctx = context.Context('', tenant_id)
|
||||
with self.network(tenant_id=tenant_id) as network_to_use:
|
||||
net_id = network_to_use['network']['id']
|
||||
fixed_ip = [dict(ip_address='10.0.0.5')]
|
||||
port = {'port': {'name': 'port',
|
||||
'network_id': net_id,
|
||||
'mac_address': constants.ATTR_NOT_SPECIFIED,
|
||||
'fixed_ips': fixed_ip,
|
||||
'admin_state_up': True,
|
||||
'device_id': 'device_id',
|
||||
'device_owner': device_owner,
|
||||
'tenant_id': tenant_id}}
|
||||
ports = [port for x in range(num_ports)]
|
||||
self.assertRaises(ml2_exceptions.BulkPortCannotHaveFixedIpError,
|
||||
plugin.create_port_obj_bulk, ctx, ports)
|
||||
|
||||
|
||||
class TestNetworksV2(NeutronDbPluginV2TestCase):
|
||||
# NOTE(cerberus): successful network update and delete are
|
||||
|
|
Loading…
Reference in New Issue