NSX|P: Support bulk subnet create

Change-Id: Ia6456a782e03c2b39519f07513244feb249734b1
This commit is contained in:
Adit Sarfaty 2019-01-06 14:29:47 +02:00
parent 2f61af4101
commit 2424a213a3
4 changed files with 95 additions and 100 deletions

View File

@ -1529,6 +1529,101 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
context, created_subnet['id'])
return created_subnet
def _create_bulk_with_callback(self, resource, context, request_items,
post_create_func=None, rollback_func=None):
# This is a copy of the _create_bulk() in db_base_plugin_v2.py,
# but extended with user-provided callback functions.
objects = []
collection = "%ss" % resource
items = request_items[collection]
try:
with db_api.CONTEXT_WRITER.using(context):
for item in items:
obj_creator = getattr(self, 'create_%s' % resource)
obj = obj_creator(context, item)
objects.append(obj)
if post_create_func:
# The user-provided post_create function is called
# after a new object is created.
post_create_func(obj)
except Exception:
if rollback_func:
# The user-provided rollback function is called when an
# exception occurred.
for obj in objects:
rollback_func(obj)
# Note that the session.rollback() function is called here.
# session.rollback() will invoke transaction.rollback() on
# the transaction this session maintains. The latter will
# deactive the transaction and clear the session's cache.
#
# But depending on where the exception occurred,
# transaction.rollback() may have already been called
# internally before reaching here.
#
# For example, if the exception happened under a
# "with session.begin(subtransactions=True):" statement
# anywhere in the middle of processing obj_creator(),
# transaction.__exit__() will invoke transaction.rollback().
# Thus when the exception reaches here, the session's cache
# is already empty.
context.session.rollback()
with excutils.save_and_reraise_exception():
LOG.error("An exception occurred while creating "
"the %(resource)s:%(item)s",
{'resource': resource, 'item': item})
return objects
def _post_create_subnet(self, context, subnet):
LOG.debug("Collect native DHCP entries for network %s",
subnet['network_id'])
dhcp_service = nsx_db.get_nsx_service_binding(
context.session, subnet['network_id'], nsxlib_consts.SERVICE_DHCP)
if dhcp_service:
_net_id, nsx_port_id = nsx_db.get_nsx_switch_and_port_id(
context.session, dhcp_service['port_id'])
return {'nsx_port_id': nsx_port_id,
'nsx_service_id': dhcp_service['nsx_service_id']}
def _rollback_subnet(self, subnet, dhcp_info):
LOG.debug("Rollback native DHCP entries for network %s",
subnet['network_id'])
if dhcp_info and self.nsxlib:
try:
self.nsxlib.logical_port.delete(dhcp_info['nsx_port_id'])
except Exception as e:
LOG.error("Failed to delete logical port %(id)s "
"during rollback. Exception: %(e)s",
{'id': dhcp_info['nsx_port_id'], 'e': e})
try:
self.nsxlib.dhcp_server.delete(dhcp_info['nsx_service_id'])
except Exception as e:
LOG.error("Failed to delete logical DHCP server %(id)s "
"during rollback. Exception: %(e)s",
{'id': dhcp_info['nsx_service_id'], 'e': e})
def create_subnet_bulk(self, context, subnets):
# Maintain a local cache here because when the rollback function
# is called, the cache in the session may have already been cleared.
_subnet_dhcp_info = {}
def _post_create(subnet):
if subnet['enable_dhcp']:
_subnet_dhcp_info[subnet['id']] = self._post_create_subnet(
context, subnet)
def _rollback(subnet):
if subnet['enable_dhcp'] and subnet['id'] in _subnet_dhcp_info:
self._rollback_subnet(subnet, _subnet_dhcp_info[subnet['id']])
del _subnet_dhcp_info[subnet['id']]
if self._has_native_dhcp_metadata():
return self._create_bulk_with_callback('subnet', context, subnets,
_post_create, _rollback)
else:
return self._create_bulk('subnet', context, subnets)
def _get_neutron_net_ids_by_nsx_id(self, context, nsx_id):
"""Should be implemented by each plugin"""
pass

View File

@ -1280,101 +1280,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
LOG.warning("Failed to update network %(id)s dhcp server on "
"the NSX: %(e)s", {'id': network['id'], 'e': e})
def _create_bulk_with_callback(self, resource, context, request_items,
post_create_func=None, rollback_func=None):
# This is a copy of the _create_bulk() in db_base_plugin_v2.py,
# but extended with user-provided callback functions.
objects = []
collection = "%ss" % resource
items = request_items[collection]
try:
with db_api.CONTEXT_WRITER.using(context):
for item in items:
obj_creator = getattr(self, 'create_%s' % resource)
obj = obj_creator(context, item)
objects.append(obj)
if post_create_func:
# The user-provided post_create function is called
# after a new object is created.
post_create_func(obj)
except Exception:
if rollback_func:
# The user-provided rollback function is called when an
# exception occurred.
for obj in objects:
rollback_func(obj)
# Note that the session.rollback() function is called here.
# session.rollback() will invoke transaction.rollback() on
# the transaction this session maintains. The latter will
# deactive the transaction and clear the session's cache.
#
# But depending on where the exception occurred,
# transaction.rollback() may have already been called
# internally before reaching here.
#
# For example, if the exception happened under a
# "with session.begin(subtransactions=True):" statement
# anywhere in the middle of processing obj_creator(),
# transaction.__exit__() will invoke transaction.rollback().
# Thus when the exception reaches here, the session's cache
# is already empty.
context.session.rollback()
with excutils.save_and_reraise_exception():
LOG.error("An exception occurred while creating "
"the %(resource)s:%(item)s",
{'resource': resource, 'item': item})
return objects
def _post_create_subnet(self, context, subnet):
LOG.debug("Collect native DHCP entries for network %s",
subnet['network_id'])
dhcp_service = nsx_db.get_nsx_service_binding(
context.session, subnet['network_id'], nsxlib_consts.SERVICE_DHCP)
if dhcp_service:
_net_id, nsx_port_id = nsx_db.get_nsx_switch_and_port_id(
context.session, dhcp_service['port_id'])
return {'nsx_port_id': nsx_port_id,
'nsx_service_id': dhcp_service['nsx_service_id']}
def _rollback_subnet(self, subnet, dhcp_info):
LOG.debug("Rollback native DHCP entries for network %s",
subnet['network_id'])
if dhcp_info:
try:
self.nsxlib.logical_port.delete(dhcp_info['nsx_port_id'])
except Exception as e:
LOG.error("Failed to delete logical port %(id)s "
"during rollback. Exception: %(e)s",
{'id': dhcp_info['nsx_port_id'], 'e': e})
try:
self.nsxlib.dhcp_server.delete(dhcp_info['nsx_service_id'])
except Exception as e:
LOG.error("Failed to delete logical DHCP server %(id)s "
"during rollback. Exception: %(e)s",
{'id': dhcp_info['nsx_service_id'], 'e': e})
def create_subnet_bulk(self, context, subnets):
# Maintain a local cache here because when the rollback function
# is called, the cache in the session may have already been cleared.
_subnet_dhcp_info = {}
def _post_create(subnet):
if subnet['enable_dhcp']:
_subnet_dhcp_info[subnet['id']] = self._post_create_subnet(
context, subnet)
def _rollback(subnet):
if subnet['enable_dhcp'] and subnet['id'] in _subnet_dhcp_info:
self._rollback_subnet(subnet, _subnet_dhcp_info[subnet['id']])
del _subnet_dhcp_info[subnet['id']]
if cfg.CONF.nsx_v3.native_dhcp_metadata:
return self._create_bulk_with_callback('subnet', context, subnets,
_post_create, _rollback)
else:
return self._create_bulk('subnet', context, subnets)
def create_subnet(self, context, subnet):
self._validate_host_routes_input(subnet)
# TODO(berlin): public external subnet announcement

View File

@ -206,8 +206,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
True)
def test_dhcp_service_with_create_dhcp_subnet_bulk(self):
# TODO(asarfaty) Enable this test once create_subnet_bulk is supported
return
# Test if DHCP service is enabled on all networks after a
# create_subnet_bulk operation.
with self.network() as network1, self.network() as network2:
@ -239,8 +237,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
self.assertTrue(dhcp_service)
def test_dhcp_service_with_create_dhcp_subnet_bulk_failure(self):
# TODO(asarfaty) Enable this test once create_subnet_bulk is supported
return
# Test if user-provided rollback function is invoked when
# exception occurred during a create_subnet_bulk operation.
with self.network() as network1, self.network() as network2:

View File

@ -836,7 +836,6 @@ class NsxPTestPorts(test_db_base_plugin_v2.TestPortsV2,
class NsxPTestSubnets(test_db_base_plugin_v2.TestSubnetsV2,
NsxPPluginTestCaseMixin):
# TODO(asarfaty) add NsxNativeDhcpTestCase tests here too
def setUp(self, plugin=PLUGIN_NAME, ext_mgr=None):
super(NsxPTestSubnets, self).setUp(plugin=plugin, ext_mgr=ext_mgr)
self.disable_dhcp = False