Merge "Separate NVP create lport operation and neutron db transaction"

This commit is contained in:
Jenkins 2013-07-30 23:11:45 +00:00 committed by Gerrit Code Review
commit ef0405a39b
3 changed files with 130 additions and 54 deletions

View File

@ -435,6 +435,22 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
port_data[ext_qos.QUEUE],
port_data.get(mac_ext.MAC_LEARNING))
def _handle_create_port_exception(self, context, port_id,
ls_uuid, lp_uuid):
with excutils.save_and_reraise_exception():
# rollback nvp logical port only if it was successfully
# created on NVP. Should this command fail the original
# exception will be raised.
if lp_uuid:
# Remove orphaned port from NVP
nvplib.delete_port(self.cluster, ls_uuid, lp_uuid)
# rollback the neutron-nvp port mapping
nicira_db.delete_neutron_nvp_port_mapping(context.session,
port_id)
msg = (_("An exception occured while creating the "
"quantum port %s on the NVP plaform") % port_id)
LOG.exception(msg)
def _nvp_create_port(self, context, port_data):
"""Driver for creating a logical switch port on NVP platform."""
# FIXME(salvatore-orlando): On the NVP platform we do not really have
@ -448,6 +464,8 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
port_data['network_id'])
# No need to actually update the DB state - the default is down
return port_data
lport = None
selected_lswitch = None
try:
selected_lswitch = self._nvp_find_lswitch_for_port(context,
port_data)
@ -466,11 +484,11 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
LOG.debug(_("_nvp_create_port completed for port %(name)s "
"on network %(network_id)s. The new port id is "
"%(id)s."), port_data)
except NvpApiClient.NvpApiException:
msg = (_("An exception occured while plugging the interface "
"into network:%s") % port_data['network_id'])
LOG.exception(msg)
raise q_exc.NeutronException(message=msg)
except (NvpApiClient.NvpApiException, q_exc.NeutronException):
self._handle_create_port_exception(
context, port_data['id'],
selected_lswitch and selected_lswitch['uuid'],
lport and lport['uuid'])
def _nvp_delete_port(self, context, port_data):
# FIXME(salvatore-orlando): On the NVP platform we do not really have
@ -515,7 +533,7 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
lrouter_id,
port_data['network_id'],
nvp_port_id)
except (NvpApiClient.NvpApiException, NvpApiClient.ResourceNotFound):
except NvpApiClient.NvpApiException:
# Do not raise because the issue might as well be that the
# router has already been deleted, so there would be nothing
# to do here
@ -534,18 +552,26 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
err_msg=(_("It is not allowed to create router interface "
"ports on external networks as '%s'") %
port_data['network_id']))
selected_lswitch = self._nvp_find_lswitch_for_port(context,
port_data)
# Do not apply port security here!
lport = self._nvp_create_port_helper(self.cluster,
selected_lswitch['uuid'],
port_data,
False)
nicira_db.add_neutron_nvp_port_mapping(
context.session, port_data['id'], lport['uuid'])
LOG.debug(_("_nvp_create_port completed for port %(name)s on "
"network %(network_id)s. The new port id is %(id)s."),
port_data)
lport = None
selected_lswitch = None
try:
selected_lswitch = self._nvp_find_lswitch_for_port(
context, port_data)
# Do not apply port security here!
lport = self._nvp_create_port_helper(
self.cluster, selected_lswitch['uuid'],
port_data, False)
nicira_db.add_neutron_nvp_port_mapping(
context.session, port_data['id'], lport['uuid'])
LOG.debug(_("_nvp_create_router_port completed for port "
"%(name)s on network %(network_id)s. The new "
"port id is %(id)s."),
port_data)
except (NvpApiClient.NvpApiException, q_exc.NeutronException):
self._handle_create_port_exception(
context, port_data['id'],
selected_lswitch and selected_lswitch['uuid'],
lport and lport['uuid'])
def _find_router_gw_port(self, context, port_data):
router_id = port_data['device_id']
@ -651,21 +677,30 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
port_data['network_id'])
# No need to actually update the DB state - the default is down
return port_data
selected_lswitch = self._nvp_find_lswitch_for_port(context,
port_data)
lport = self._nvp_create_port_helper(self.cluster,
selected_lswitch['uuid'],
port_data,
True)
nicira_db.add_neutron_nvp_port_mapping(
context.session, port_data['id'], lport['uuid'])
nvplib.plug_l2_gw_service(
self.cluster,
port_data['network_id'],
lport['uuid'],
port_data['device_id'],
int(port_data.get('gw:segmentation_id') or 0))
LOG.debug(_("_nvp_create_port completed for port %(name)s "
lport = None
try:
selected_lswitch = self._nvp_find_lswitch_for_port(
context, port_data)
lport = self._nvp_create_port_helper(
self.cluster,
selected_lswitch['uuid'],
port_data,
True)
nicira_db.add_neutron_nvp_port_mapping(
context.session, port_data['id'], lport['uuid'])
nvplib.plug_l2_gw_service(
self.cluster,
port_data['network_id'],
lport['uuid'],
port_data['device_id'],
int(port_data.get('gw:segmentation_id') or 0))
except Exception:
with excutils.save_and_reraise_exception():
if lport:
nvplib.delete_port(self.cluster,
selected_lswitch['uuid'],
lport['uuid'])
LOG.debug(_("_nvp_create_l2_gw_port completed for port %(name)s "
"on network %(network_id)s. The new port id "
"is %(id)s."), port_data)
@ -1205,6 +1240,7 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
with context.session.begin(subtransactions=True):
# First we allocate port in neutron database
neutron_db = super(NvpPluginV2, self).create_port(context, port)
neutron_port_id = neutron_db['id']
# Update fields obtained from neutron db (eg: MAC address)
port["port"].update(neutron_db)
# metadata_dhcp_host_route
@ -1236,27 +1272,6 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
self._create_mac_learning_state(context, port_data)
elif mac_ext.MAC_LEARNING in port_data:
port_data.pop(mac_ext.MAC_LEARNING)
# provider networking extension checks
# Fetch the network and network binding from neutron db
try:
port_data = port['port'].copy()
port_create_func = self._port_drivers['create'].get(
port_data['device_owner'],
self._port_drivers['create']['default'])
port_create_func(context, port_data)
except q_exc.NotFound:
LOG.warning(_("Network %s was not found in NVP."),
port_data['network_id'])
port_data['status'] = constants.PORT_STATUS_ERROR
except Exception:
with excutils.save_and_reraise_exception():
# FIXME (arosen) or the plugin_interface call failed in
# which case we need to garbage collect the left over
# port in nvp.
err_msg = _("Unable to create port or set port "
"attachment in NVP.")
LOG.error(err_msg)
LOG.debug(_("create_port completed on NVP for tenant "
"%(tenant_id)s: (%(id)s)"), port_data)
@ -1267,6 +1282,32 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
self._extend_port_qos_queue(context, port_data)
self._process_portbindings_create_and_update(context,
port, port_data)
# DB Operation is complete, perform NVP operation
try:
port_data = port['port'].copy()
port_create_func = self._port_drivers['create'].get(
port_data['device_owner'],
self._port_drivers['create']['default'])
port_create_func(context, port_data)
except q_exc.NotFound:
LOG.warning(_("Logical switch for network %s was not "
"found in NVP."), port_data['network_id'])
# Put port in error on quantum DB
with context.session.begin(subtransactions=True):
port = self._get_port(context, neutron_port_id)
port_data['status'] = constants.PORT_STATUS_ERROR
port['status'] = port_data['status']
context.session.add(port)
except Exception:
# Port must be removed from Quantum DB
with excutils.save_and_reraise_exception():
LOG.error(_("Unable to create port or set port "
"attachment in NVP."))
with context.session.begin(subtransactions=True):
self._delete_port(context, neutron_port_id)
# Port has been created both on DB and NVP - proceed with
# scheduling network and notifying DHCP agent
net = self.get_network(context, port_data['network_id'])
self.schedule_network(context, net)
if notify_dhcp_agent:

View File

@ -72,6 +72,11 @@ def get_nvp_port_id(session, neutron_id):
return
def delete_neutron_nvp_port_mapping(session, neutron_id):
return (session.query(nicira_models.NeutronNvpPortMapping).
filter_by(quantum_id=neutron_id).delete())
def unset_default_network_gateways(session):
with session.begin(subtransactions=True):
session.query(nicira_networkgw_db.NetworkGateway).update(

View File

@ -21,6 +21,7 @@ from oslo.config import cfg
import webob.exc
from neutron.common import constants
from neutron.common import exceptions as ntn_exc
import neutron.common.test_lib as test_lib
from neutron import context
from neutron.extensions import l3
@ -29,6 +30,7 @@ from neutron.extensions import providernet as pnet
from neutron.extensions import securitygroup as secgrp
from neutron import manager
from neutron.openstack.common import uuidutils
from neutron.plugins.nicira.dbexts import nicira_db
from neutron.plugins.nicira.dbexts import nicira_qos_db as qos_db
from neutron.plugins.nicira.extensions import nvp_networkgw
from neutron.plugins.nicira.extensions import nvp_qos as ext_qos
@ -162,6 +164,34 @@ class TestNiciraPortsV2(test_plugin.TestPortsV2,
# Assert the neutron name is not truncated
self.assertEqual(name, port['port']['name'])
def _verify_no_orphan_left(self, net_id):
# Verify no port exists on net
# ie: cleanup on db was successful
query_params = "network_id=%s" % net_id
self._test_list_resources('port', [],
query_params=query_params)
# Also verify no orphan port was left on nvp
# no port should be there at all
self.assertFalse(self.fc._fake_lswitch_lport_dict)
def test_create_port_nvp_error_no_orphan_left(self):
with mock.patch.object(nvplib, 'create_lport',
side_effect=NvpApiClient.NvpApiException):
with self.network() as net:
net_id = net['network']['id']
self._create_port(self.fmt, net_id,
webob.exc.HTTPInternalServerError.code)
self._verify_no_orphan_left(net_id)
def test_create_port_neutron_error_no_orphan_left(self):
with mock.patch.object(nicira_db, 'add_neutron_nvp_port_mapping',
side_effect=ntn_exc.NeutronException):
with self.network() as net:
net_id = net['network']['id']
self._create_port(self.fmt, net_id,
webob.exc.HTTPInternalServerError.code)
self._verify_no_orphan_left(net_id)
class TestNiciraNetworksV2(test_plugin.TestNetworksV2,
NiciraPluginV2TestCase):