Prevent bind fip to port has port forwarding
If one port has port forwarding and the port is under a dvr router, then binding floating IP to this port will not be allowed. Change-Id: Ia014e18264b43cf751a5bc0e82bc55d106582620 Closes-Bug: #1799138
This commit is contained in:
parent
76c2881201
commit
433228dd78
@ -1336,6 +1336,14 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
|
|||||||
|
|
||||||
def _create_floatingip(self, context, floatingip,
|
def _create_floatingip(self, context, floatingip,
|
||||||
initial_status=constants.FLOATINGIP_STATUS_ACTIVE):
|
initial_status=constants.FLOATINGIP_STATUS_ACTIVE):
|
||||||
|
try:
|
||||||
|
registry.publish(resources.FLOATING_IP, events.BEFORE_CREATE,
|
||||||
|
self, payload=events.DBEventPayload(
|
||||||
|
context, request_body=floatingip))
|
||||||
|
except exceptions.CallbackFailure as e:
|
||||||
|
# raise the underlying exception
|
||||||
|
raise e.errors[0].error
|
||||||
|
|
||||||
fip = floatingip['floatingip']
|
fip = floatingip['floatingip']
|
||||||
fip_id = uuidutils.generate_uuid()
|
fip_id = uuidutils.generate_uuid()
|
||||||
|
|
||||||
@ -1441,6 +1449,15 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
|
|||||||
return self._create_floatingip(context, floatingip, initial_status)
|
return self._create_floatingip(context, floatingip, initial_status)
|
||||||
|
|
||||||
def _update_floatingip(self, context, id, floatingip):
|
def _update_floatingip(self, context, id, floatingip):
|
||||||
|
try:
|
||||||
|
registry.publish(resources.FLOATING_IP, events.BEFORE_UPDATE,
|
||||||
|
self, payload=events.DBEventPayload(
|
||||||
|
context, request_body=floatingip,
|
||||||
|
resource_id=id))
|
||||||
|
except exceptions.CallbackFailure as e:
|
||||||
|
# raise the underlying exception
|
||||||
|
raise e.errors[0].error
|
||||||
|
|
||||||
fip = floatingip['floatingip']
|
fip = floatingip['floatingip']
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
floatingip_obj = self._get_floatingip(context, id)
|
floatingip_obj = self._get_floatingip(context, id)
|
||||||
|
@ -25,6 +25,12 @@ class PortForwardingNotSupportFilterField(n_exc.BadRequest):
|
|||||||
message = _("Port Forwarding filter %(filter)s is not supported.")
|
message = _("Port Forwarding filter %(filter)s is not supported.")
|
||||||
|
|
||||||
|
|
||||||
|
class PortHasPortForwarding(n_exc.BadRequest):
|
||||||
|
message = _("Cannot associate floating IP to port "
|
||||||
|
"%(port_id)s because it already has a "
|
||||||
|
"Port Forwarding binding.")
|
||||||
|
|
||||||
|
|
||||||
class FipInUseByPortForwarding(n_exc.InUse):
|
class FipInUseByPortForwarding(n_exc.InUse):
|
||||||
message = _("Floating IP %(id)s in use by Port Forwarding resources.")
|
message = _("Floating IP %(id)s in use by Port Forwarding resources.")
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ from neutron.api.rpc.handlers import resources_rpc
|
|||||||
from neutron.common import utils
|
from neutron.common import utils
|
||||||
from neutron.db import _resource_extend as resource_extend
|
from neutron.db import _resource_extend as resource_extend
|
||||||
from neutron.db import db_base_plugin_common
|
from neutron.db import db_base_plugin_common
|
||||||
|
from neutron.db import l3_dvr_db
|
||||||
from neutron.extensions import floating_ip_port_forwarding as fip_pf
|
from neutron.extensions import floating_ip_port_forwarding as fip_pf
|
||||||
from neutron.objects import base as base_obj
|
from neutron.objects import base as base_obj
|
||||||
from neutron.objects import port_forwarding as pf
|
from neutron.objects import port_forwarding as pf
|
||||||
@ -103,6 +104,27 @@ class PortForwardingPlugin(fip_pf.PortForwardingPluginBase):
|
|||||||
result_dict[apidef.COLLECTION_NAME] = port_forwarding_result
|
result_dict[apidef.COLLECTION_NAME] = port_forwarding_result
|
||||||
return result_dict
|
return result_dict
|
||||||
|
|
||||||
|
@registry.receives(resources.FLOATING_IP, [events.BEFORE_CREATE,
|
||||||
|
events.BEFORE_UPDATE])
|
||||||
|
def _check_port_has_port_forwarding(self, resource, event,
|
||||||
|
trigger, payload=None):
|
||||||
|
port_id = payload.request_body['floatingip'].get('port_id')
|
||||||
|
if not port_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
pf_objs = pf.PortForwarding.get_objects(
|
||||||
|
payload.context, internal_port_id=port_id)
|
||||||
|
if not pf_objs:
|
||||||
|
return
|
||||||
|
# Port may not bind to host yet, or port may migrate from one
|
||||||
|
# dvr_no_external host to one dvr host. So we just do not allow
|
||||||
|
# all dvr router's floating IP to be binded to a port which
|
||||||
|
# already has port forwarding.
|
||||||
|
router = self.l3_plugin.get_router(payload.context.elevated(),
|
||||||
|
pf_objs[0].router_id)
|
||||||
|
if l3_dvr_db.is_distributed_router(router):
|
||||||
|
raise pf_exc.PortHasPortForwarding(port_id=port_id)
|
||||||
|
|
||||||
@registry.receives(resources.FLOATING_IP, [events.PRECOMMIT_UPDATE,
|
@registry.receives(resources.FLOATING_IP, [events.PRECOMMIT_UPDATE,
|
||||||
events.PRECOMMIT_DELETE])
|
events.PRECOMMIT_DELETE])
|
||||||
def _check_floatingip_request(self, resource, event, trigger, context,
|
def _check_floatingip_request(self, resource, event, trigger, context,
|
||||||
|
@ -86,7 +86,7 @@ class PortForwardingTestCase(PortForwardingTestCaseBase):
|
|||||||
self._prepare_env()
|
self._prepare_env()
|
||||||
|
|
||||||
def _prepare_env(self):
|
def _prepare_env(self):
|
||||||
self.router = self._create_router()
|
self.router = self._create_router(distributed=True)
|
||||||
self.ext_net = self._create_network(
|
self.ext_net = self._create_network(
|
||||||
self.fmt, 'ext-net', True, arg_list=("router:external",),
|
self.fmt, 'ext-net', True, arg_list=("router:external",),
|
||||||
**{"router:external": True}).json['network']
|
**{"router:external": True}).json['network']
|
||||||
@ -461,3 +461,24 @@ class PortForwardingTestCase(PortForwardingTestCaseBase):
|
|||||||
self._simulate_concurrent_requests_process_and_raise(funcs, args_list)
|
self._simulate_concurrent_requests_process_and_raise(funcs, args_list)
|
||||||
self.assertEqual([], self.pf_plugin.get_floatingip_port_forwardings(
|
self.assertEqual([], self.pf_plugin.get_floatingip_port_forwardings(
|
||||||
self.context, floatingip_id=self.fip['id']))
|
self.context, floatingip_id=self.fip['id']))
|
||||||
|
|
||||||
|
def test_create_floatingip_port_forwarding_port_in_use(self):
|
||||||
|
res = self.pf_plugin.create_floatingip_port_forwarding(
|
||||||
|
self.context, self.fip['id'], self.port_forwarding)
|
||||||
|
expected = {
|
||||||
|
"external_port": 2225,
|
||||||
|
"internal_port": 25,
|
||||||
|
"internal_port_id": self.port['id'],
|
||||||
|
"protocol": "tcp",
|
||||||
|
"internal_ip_address": self.port['fixed_ips'][0]['ip_address'],
|
||||||
|
'id': mock.ANY,
|
||||||
|
'router_id': self.router['id'],
|
||||||
|
'floating_ip_address': self.fip['floating_ip_address'],
|
||||||
|
'floatingip_id': self.fip['id']}
|
||||||
|
self.assertEqual(expected, res)
|
||||||
|
|
||||||
|
fip_2 = self._create_floatingip(self.ext_net['id'])
|
||||||
|
self.assertRaises(
|
||||||
|
pf_exc.PortHasPortForwarding,
|
||||||
|
self._update_floatingip,
|
||||||
|
fip_2['id'], {'port_id': self.port['id']})
|
||||||
|
@ -16,18 +16,22 @@ from neutron_lib.api.definitions import external_net as extnet_apidef
|
|||||||
from neutron_lib.api.definitions import floating_ip_port_forwarding as apidef
|
from neutron_lib.api.definitions import floating_ip_port_forwarding as apidef
|
||||||
from neutron_lib import constants
|
from neutron_lib import constants
|
||||||
from neutron_lib import context
|
from neutron_lib import context
|
||||||
|
from neutron_lib.plugins import constants as plugin_constants
|
||||||
|
from neutron_lib.plugins import directory
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
from webob import exc
|
||||||
|
|
||||||
|
from neutron.db import l3_dvr_db
|
||||||
from neutron.db import l3_fip_qos
|
from neutron.db import l3_fip_qos
|
||||||
from neutron.extensions import floating_ip_port_forwarding as pf_ext
|
from neutron.extensions import floating_ip_port_forwarding as pf_ext
|
||||||
from neutron.extensions import l3
|
from neutron.extensions import l3
|
||||||
from neutron.objects.qos import policy
|
from neutron.objects.qos import policy
|
||||||
from neutron.services.portforwarding import pf_plugin
|
|
||||||
from neutron.tests.unit.db import test_db_base_plugin_v2
|
from neutron.tests.unit.db import test_db_base_plugin_v2
|
||||||
from neutron.tests.unit.extensions import test_l3
|
from neutron.tests.unit.extensions import test_l3
|
||||||
|
|
||||||
|
|
||||||
PLUGIN_NAME = 'port_forwarding'
|
PF_PLUGIN_NAME = ('neutron.services.portforwarding.'
|
||||||
|
'pf_plugin.PortForwardingPlugin')
|
||||||
L3_PLUGIN = ('neutron.tests.unit.extensions.'
|
L3_PLUGIN = ('neutron.tests.unit.extensions.'
|
||||||
'test_expose_port_forwarding_in_fip.'
|
'test_expose_port_forwarding_in_fip.'
|
||||||
'TestL3PorForwardingServicePlugin')
|
'TestL3PorForwardingServicePlugin')
|
||||||
@ -70,14 +74,13 @@ class TestExtendFipPortForwardingExtension(
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
mock.patch('neutron.api.rpc.handlers.resources_rpc.'
|
mock.patch('neutron.api.rpc.handlers.resources_rpc.'
|
||||||
'ResourcesPushRpcApi').start()
|
'ResourcesPushRpcApi').start()
|
||||||
svc_plugins = {'l3_plugin_name': L3_PLUGIN,
|
svc_plugins = (L3_PLUGIN, PF_PLUGIN_NAME,
|
||||||
'port_forwarding_plugin_name': PLUGIN_NAME,
|
'neutron.services.qos.qos_plugin.QoSPlugin')
|
||||||
'qos': 'neutron.services.qos.qos_plugin.QoSPlugin'}
|
|
||||||
ext_mgr = ExtendFipPortForwardingExtensionManager()
|
ext_mgr = ExtendFipPortForwardingExtensionManager()
|
||||||
super(TestExtendFipPortForwardingExtension, self).setUp(
|
super(TestExtendFipPortForwardingExtension, self).setUp(
|
||||||
plugin=CORE_PLUGIN, ext_mgr=ext_mgr, service_plugins=svc_plugins)
|
plugin=CORE_PLUGIN, ext_mgr=ext_mgr, service_plugins=svc_plugins)
|
||||||
self.pf_plugin = pf_plugin.PortForwardingPlugin()
|
self.l3_plugin = directory.get_plugin(plugin_constants.L3)
|
||||||
self.l3_plugin = TestL3PorForwardingServicePlugin()
|
self.pf_plugin = directory.get_plugin(plugin_constants.PORTFORWARDING)
|
||||||
|
|
||||||
ctx = context.get_admin_context()
|
ctx = context.get_admin_context()
|
||||||
self.policy_1 = policy.QosPolicy(ctx,
|
self.policy_1 = policy.QosPolicy(ctx,
|
||||||
@ -291,3 +294,56 @@ class TestExtendFipPortForwardingExtension(
|
|||||||
self._router_interface_action(
|
self._router_interface_action(
|
||||||
'remove', router['router']['id'],
|
'remove', router['router']['id'],
|
||||||
insub3['subnet']['id'], None)
|
insub3['subnet']['id'], None)
|
||||||
|
|
||||||
|
@mock.patch.object(l3_dvr_db.L3_NAT_with_dvr_db_mixin,
|
||||||
|
'_notify_floating_ip_change')
|
||||||
|
@mock.patch.object(l3_dvr_db.DVRResourceOperationHandler,
|
||||||
|
'_create_dvr_floating_gw_port')
|
||||||
|
def test_port_in_used_by_port_forwarding(self, mock_gw_port, mock_notify):
|
||||||
|
port_forwarding = {
|
||||||
|
apidef.RESOURCE_NAME:
|
||||||
|
{apidef.EXTERNAL_PORT: 2225,
|
||||||
|
apidef.INTERNAL_PORT: 25,
|
||||||
|
apidef.INTERNAL_PORT_ID: None,
|
||||||
|
apidef.PROTOCOL: "tcp",
|
||||||
|
apidef.INTERNAL_IP_ADDRESS: None}}
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
kwargs = {'arg_list': (extnet_apidef.EXTERNAL,),
|
||||||
|
extnet_apidef.EXTERNAL: True}
|
||||||
|
with self.network(**kwargs) as extnet, self.network() as innet:
|
||||||
|
with self.subnet(network=extnet, cidr='200.0.0.0/22'),\
|
||||||
|
self.subnet(network=innet, cidr='10.0.0.0/24') as insub,\
|
||||||
|
self.router(distributed=True) as router:
|
||||||
|
fip = self._make_floatingip(self.fmt, extnet['network']['id'])
|
||||||
|
# check the floatingip response contains port_forwarding field
|
||||||
|
self.assertIn(apidef.COLLECTION_NAME, fip['floatingip'])
|
||||||
|
self._add_external_gateway_to_router(router['router']['id'],
|
||||||
|
extnet['network']['id'])
|
||||||
|
self._router_interface_action('add', router['router']['id'],
|
||||||
|
insub['subnet']['id'], None)
|
||||||
|
|
||||||
|
with self.port(subnet=insub) as port1:
|
||||||
|
update_dict1 = {
|
||||||
|
apidef.INTERNAL_PORT_ID: port1['port']['id'],
|
||||||
|
apidef.INTERNAL_IP_ADDRESS:
|
||||||
|
port1['port']['fixed_ips'][0]['ip_address']}
|
||||||
|
port_forwarding[apidef.RESOURCE_NAME].update(update_dict1)
|
||||||
|
self.pf_plugin.create_floatingip_port_forwarding(
|
||||||
|
ctx, fip['floatingip']['id'], port_forwarding)
|
||||||
|
|
||||||
|
body = self._show('floatingips', fip['floatingip']['id'])
|
||||||
|
self.assertEqual(
|
||||||
|
1, len(body['floatingip'][apidef.COLLECTION_NAME]))
|
||||||
|
|
||||||
|
self._make_floatingip(
|
||||||
|
self.fmt,
|
||||||
|
extnet['network']['id'],
|
||||||
|
port_id=port1['port']['id'],
|
||||||
|
http_status=exc.HTTPBadRequest.code)
|
||||||
|
fip_2 = self._make_floatingip(self.fmt,
|
||||||
|
extnet['network']['id'])
|
||||||
|
self._update(
|
||||||
|
'floatingips',
|
||||||
|
fip_2['floatingip']['id'],
|
||||||
|
{'floatingip': {'port_id': port1['port']['id']}},
|
||||||
|
expected_code=exc.HTTPBadRequest.code)
|
||||||
|
@ -388,6 +388,8 @@ class L3NatTestCaseMixin(object):
|
|||||||
# Arg must be present and not empty
|
# Arg must be present and not empty
|
||||||
if arg in kwargs:
|
if arg in kwargs:
|
||||||
data['router'][arg] = kwargs[arg]
|
data['router'][arg] = kwargs[arg]
|
||||||
|
if 'distributed' in kwargs:
|
||||||
|
data['router']['distributed'] = bool(kwargs['distributed'])
|
||||||
router_req = self.new_create_request('routers', data, fmt)
|
router_req = self.new_create_request('routers', data, fmt)
|
||||||
if set_context and tenant_id:
|
if set_context and tenant_id:
|
||||||
# create a specific auth context for this request
|
# create a specific auth context for this request
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
other:
|
||||||
|
- |
|
||||||
|
If an instance port is under a dvr router, and the port already has
|
||||||
|
binding port forwarding(s). Neutron will no longer allow binding a
|
||||||
|
floating IP to that port again, because dvr floating IP traffic rules
|
||||||
|
will break the existing port forwarding functionality.
|
Loading…
x
Reference in New Issue
Block a user