diff --git a/vmware_nsx/plugins/common_v3/plugin.py b/vmware_nsx/plugins/common_v3/plugin.py index f91e4543fd..2b7344f125 100644 --- a/vmware_nsx/plugins/common_v3/plugin.py +++ b/vmware_nsx/plugins/common_v3/plugin.py @@ -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 diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index 28522ec287..edc3361f6b 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -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 diff --git a/vmware_nsx/tests/unit/nsx_p/test_dhcp_metadata.py b/vmware_nsx/tests/unit/nsx_p/test_dhcp_metadata.py index e249169d0e..621b96c3c1 100644 --- a/vmware_nsx/tests/unit/nsx_p/test_dhcp_metadata.py +++ b/vmware_nsx/tests/unit/nsx_p/test_dhcp_metadata.py @@ -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: diff --git a/vmware_nsx/tests/unit/nsx_p/test_plugin.py b/vmware_nsx/tests/unit/nsx_p/test_plugin.py index 7823163f8b..c940cc8e23 100644 --- a/vmware_nsx/tests/unit/nsx_p/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_p/test_plugin.py @@ -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