[OVN] Prevent binding a virtual type port
A LSP is type=virtual when its IP address is used by other ports as allowed address. If a LSP is type=virtual, this port cannot be bound (that means cannot be used as a port for a virtual machine). Closes-Bug: #2018529 Change-Id: I1943e6e0d7d8e255e95f93881cc3caec16ab67fe
This commit is contained in:
parent
2968e4523b
commit
68ecae5ff9
@ -1051,3 +1051,27 @@ def determine_bind_host(sb_idl, port, port_context=None):
|
|||||||
bp_info.bp_param[
|
bp_info.bp_param[
|
||||||
constants.VIF_DETAILS_CARD_SERIAL_NUMBER]).hostname
|
constants.VIF_DETAILS_CARD_SERIAL_NUMBER]).hostname
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
def validate_port_binding_and_virtual_port(
|
||||||
|
port_context, nb_idl, sb_idl, ml2_plugin, port):
|
||||||
|
"""If the port is type=virtual and it is bound, raise BadRequest"""
|
||||||
|
if not determine_bind_host(sb_idl, port, port_context=port_context):
|
||||||
|
# The port is not bound, exit.
|
||||||
|
return
|
||||||
|
|
||||||
|
fixed_ips = port.get('fixed_ips', [])
|
||||||
|
subnet_ids = set([fixed_ip['subnet_id'] for fixed_ip in fixed_ips
|
||||||
|
if 'subnet_id' in fixed_ip])
|
||||||
|
if not subnet_ids:
|
||||||
|
# If the port has no fixed_ips/subnets, it cannot be virtual.
|
||||||
|
return
|
||||||
|
|
||||||
|
subnets = ml2_plugin.get_subnets(port_context.plugin_context,
|
||||||
|
filters={'id': list(subnet_ids)})
|
||||||
|
port_type, _, _ = get_port_type_virtual_and_parents(
|
||||||
|
subnets, fixed_ips, port['network_id'], port['id'], nb_idl)
|
||||||
|
if port_type == constants.LSP_TYPE_VIRTUAL:
|
||||||
|
raise n_exc.BadRequest(
|
||||||
|
resource='port',
|
||||||
|
msg='A virtual logical switch port cannot be bound to a host')
|
||||||
|
@ -818,6 +818,8 @@ class OVNMechanismDriver(api.MechanismDriver):
|
|||||||
self._validate_ignored_port(port, original_port)
|
self._validate_ignored_port(port, original_port)
|
||||||
ovn_utils.validate_and_get_data_from_binding_profile(port)
|
ovn_utils.validate_and_get_data_from_binding_profile(port)
|
||||||
self._validate_port_extra_dhcp_opts(port)
|
self._validate_port_extra_dhcp_opts(port)
|
||||||
|
ovn_utils.validate_port_binding_and_virtual_port(
|
||||||
|
context, self.nb_ovn, self.sb_ovn, self._plugin, port)
|
||||||
if self._is_port_provisioning_required(port, context.host,
|
if self._is_port_provisioning_required(port, context.host,
|
||||||
context.original_host):
|
context.original_host):
|
||||||
self._insert_port_provisioning_block(context.plugin_context,
|
self._insert_port_provisioning_block(context.plugin_context,
|
||||||
|
@ -4345,7 +4345,7 @@ class TestOVNVVirtualPort(OVNMechanismDriverTestCase):
|
|||||||
self.fmt, name='net1', admin_state_up=True)['network']
|
self.fmt, name='net1', admin_state_up=True)['network']
|
||||||
self.subnet = self._make_subnet(
|
self.subnet = self._make_subnet(
|
||||||
self.fmt, {'network': self.net},
|
self.fmt, {'network': self.net},
|
||||||
'10.0.0.1', '10.0.0.0/24')['subnet']
|
'10.0.0.1', '10.0.0.0/24')
|
||||||
|
|
||||||
@mock.patch.object(ovn_utils, 'determine_bind_host')
|
@mock.patch.object(ovn_utils, 'determine_bind_host')
|
||||||
def test_create_port_with_virtual_type_and_options(self, *args):
|
def test_create_port_with_virtual_type_and_options(self, *args):
|
||||||
@ -4356,7 +4356,7 @@ class TestOVNVVirtualPort(OVNMechanismDriverTestCase):
|
|||||||
'mac_address': '00:00:00:00:00:00',
|
'mac_address': '00:00:00:00:00:00',
|
||||||
'device_owner': device_owner,
|
'device_owner': device_owner,
|
||||||
'network_id': self.net['id'],
|
'network_id': self.net['id'],
|
||||||
'fixed_ips': [{'subnet_id': self.subnet['id'],
|
'fixed_ips': [{'subnet_id': self.subnet['subnet']['id'],
|
||||||
'ip_address': '10.0.0.55'}],
|
'ip_address': '10.0.0.55'}],
|
||||||
portbindings.PROFILE: {},
|
portbindings.PROFILE: {},
|
||||||
}
|
}
|
||||||
@ -4419,6 +4419,25 @@ class TestOVNVVirtualPort(OVNMechanismDriverTestCase):
|
|||||||
self.nb_idl.unset_lswitch_port_to_virtual_type.assert_called_once_with(
|
self.nb_idl.unset_lswitch_port_to_virtual_type.assert_called_once_with(
|
||||||
virt_port['id'], parent['id'], if_exists=True)
|
virt_port['id'], parent['id'], if_exists=True)
|
||||||
|
|
||||||
|
def test_update_port_bound(self):
|
||||||
|
with self.port(subnet=self.subnet, is_admin=True) as port:
|
||||||
|
port = port['port']
|
||||||
|
updated_port = copy.deepcopy(port)
|
||||||
|
updated_port[portbindings.HOST_ID] = 'host1'
|
||||||
|
context = mock.Mock(current=updated_port, original=port)
|
||||||
|
with mock.patch.object(self.mech_driver._plugin, 'get_subnets') \
|
||||||
|
as mock_get_subnets:
|
||||||
|
mock_get_subnets.return_value = [self.subnet['subnet']]
|
||||||
|
# 1) The port is not virtual, it has no parents.
|
||||||
|
self.mock_vp_parents.return_value = ''
|
||||||
|
self.mech_driver.update_port_precommit(context)
|
||||||
|
# 2) The port (LSP) has parents, that means it is a virtual
|
||||||
|
# port.
|
||||||
|
self.mock_vp_parents.return_value = ['parent-0', 'parent-1']
|
||||||
|
self.assertRaises(n_exc.BadRequest,
|
||||||
|
self.mech_driver.update_port_precommit,
|
||||||
|
context)
|
||||||
|
|
||||||
|
|
||||||
class TestOVNAvailabilityZone(OVNMechanismDriverTestCase):
|
class TestOVNAvailabilityZone(OVNMechanismDriverTestCase):
|
||||||
|
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
other:
|
||||||
|
- |
|
||||||
|
A ML2/OVN virtual port cannot be bound to a virtual machine. If a port
|
||||||
|
IP address is assigned as an allowed address pair into another port, the
|
||||||
|
first one is considered a virtual port. If the second port (non-virtual)
|
||||||
|
is bound to ML2/OVN, the virtual port cannot be bound to a virtual
|
||||||
|
machine; a virtual port is created only to reserve a set of IP addresses
|
||||||
|
to be used by other ports.
|
Loading…
Reference in New Issue
Block a user