Enable deferred IP on Neutron ports
When booting a VM using an existing port, Nova checks that the port has fixed ips and fails if it doesn't. In the context of Neutron routed networks, a port may not have an IP address because address allocation was deferred. Neutron can communicate that this is ok through the ip_allocation attribute of the port added in the patch on which this one depends. In the absence of the ip_allocation attribute, Nova falls back to its previous behavior. Change-Id: I03f2e02377a743f4dd10ca12e6c31bb71ee36767 Depends-On: I591d32df512712e4de36fe20e389b3a14d58157f Depends-On: I8dc8890907d1e241dd12448fa184cea1b0620663 Partially-Implements: blueprint neutron-routed-networks
This commit is contained in:
parent
a0fcff8bad
commit
54b8d7770a
@ -420,8 +420,8 @@ class ServersController(wsgi.Controller):
|
|||||||
raise exc.HTTPBadRequest(explanation=msg)
|
raise exc.HTTPBadRequest(explanation=msg)
|
||||||
if request.address is not None:
|
if request.address is not None:
|
||||||
msg = _("Specified Fixed IP '%(addr)s' cannot be used "
|
msg = _("Specified Fixed IP '%(addr)s' cannot be used "
|
||||||
"with port '%(port)s': port already has "
|
"with port '%(port)s': the two cannot be "
|
||||||
"a Fixed IP allocated.") % {
|
"specified together.") % {
|
||||||
"addr": request.address,
|
"addr": request.address,
|
||||||
"port": request.port_id}
|
"port": request.port_id}
|
||||||
raise exc.HTTPBadRequest(explanation=msg)
|
raise exc.HTTPBadRequest(explanation=msg)
|
||||||
|
@ -1413,7 +1413,12 @@ class API(base_api.NetworkAPI):
|
|||||||
neutron_client=neutron)
|
neutron_client=neutron)
|
||||||
if port.get('device_id', None):
|
if port.get('device_id', None):
|
||||||
raise exception.PortInUse(port_id=request.port_id)
|
raise exception.PortInUse(port_id=request.port_id)
|
||||||
if not port.get('fixed_ips'):
|
deferred_ip = port.get('ip_allocation') == 'deferred'
|
||||||
|
# NOTE(carl_baldwin) A deferred IP port doesn't have an
|
||||||
|
# address here. If it fails to get one later when nova
|
||||||
|
# updates it with host info, Neutron will error which
|
||||||
|
# raises an exception.
|
||||||
|
if not deferred_ip and not port.get('fixed_ips'):
|
||||||
raise exception.PortRequiresFixedIP(
|
raise exception.PortRequiresFixedIP(
|
||||||
port_id=request.port_id)
|
port_id=request.port_id)
|
||||||
request.network_id = port['network_id']
|
request.network_id = port['network_id']
|
||||||
|
@ -419,7 +419,8 @@ class SecurityGroupAPI(security_group_base.SecurityGroupBase):
|
|||||||
def _has_security_group_requirements(self, port):
|
def _has_security_group_requirements(self, port):
|
||||||
port_security_enabled = port.get('port_security_enabled', True)
|
port_security_enabled = port.get('port_security_enabled', True)
|
||||||
has_ip = port.get('fixed_ips')
|
has_ip = port.get('fixed_ips')
|
||||||
if has_ip:
|
deferred_ip = port.get('ip_allocation') == 'deferred'
|
||||||
|
if has_ip or deferred_ip:
|
||||||
return port_security_enabled
|
return port_security_enabled
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ class TestNeutronSecurityGroupsV21(
|
|||||||
def _create_port(self, **kwargs):
|
def _create_port(self, **kwargs):
|
||||||
body = {'port': {'binding:vnic_type': model.VNIC_TYPE_NORMAL}}
|
body = {'port': {'binding:vnic_type': model.VNIC_TYPE_NORMAL}}
|
||||||
fields = ['security_groups', 'device_id', 'network_id',
|
fields = ['security_groups', 'device_id', 'network_id',
|
||||||
'port_security_enabled']
|
'port_security_enabled', 'ip_allocation']
|
||||||
for field in fields:
|
for field in fields:
|
||||||
if field in kwargs:
|
if field in kwargs:
|
||||||
body['port'][field] = kwargs[field]
|
body['port'][field] = kwargs[field]
|
||||||
@ -278,6 +278,22 @@ class TestNeutronSecurityGroupsV21(
|
|||||||
self.manager._addSecurityGroup,
|
self.manager._addSecurityGroup,
|
||||||
req, UUID_SERVER, body)
|
req, UUID_SERVER, body)
|
||||||
|
|
||||||
|
def test_associate_deferred_ip_port(self):
|
||||||
|
sg = self._create_sg_template().get('security_group')
|
||||||
|
net = self._create_network()
|
||||||
|
self._create_port(
|
||||||
|
network_id=net['network']['id'], security_groups=[sg['id']],
|
||||||
|
port_security_enabled=True, ip_allocation='deferred',
|
||||||
|
device_id=UUID_SERVER)
|
||||||
|
|
||||||
|
self.stub_out('nova.db.instance_get_by_uuid',
|
||||||
|
test_security_groups.return_server)
|
||||||
|
body = dict(addSecurityGroup=dict(name="test"))
|
||||||
|
|
||||||
|
req = fakes.HTTPRequest.blank('/v2/fake/servers/%s/action' %
|
||||||
|
UUID_SERVER)
|
||||||
|
self.manager._addSecurityGroup(req, UUID_SERVER, body)
|
||||||
|
|
||||||
def test_disassociate_by_non_existing_security_group_name(self):
|
def test_disassociate_by_non_existing_security_group_name(self):
|
||||||
self.stub_out('nova.db.instance_get_by_uuid',
|
self.stub_out('nova.db.instance_get_by_uuid',
|
||||||
test_security_groups.return_server)
|
test_security_groups.return_server)
|
||||||
@ -696,6 +712,7 @@ class MockClient(object):
|
|||||||
'admin_state_up': p.get('admin_state_up', True),
|
'admin_state_up': p.get('admin_state_up', True),
|
||||||
'security_groups': p.get('security_groups', []),
|
'security_groups': p.get('security_groups', []),
|
||||||
'network_id': p.get('network_id'),
|
'network_id': p.get('network_id'),
|
||||||
|
'ip_allocation': p.get('ip_allocation'),
|
||||||
'binding:vnic_type':
|
'binding:vnic_type':
|
||||||
p.get('binding:vnic_type') or model.VNIC_TYPE_NORMAL}
|
p.get('binding:vnic_type') or model.VNIC_TYPE_NORMAL}
|
||||||
|
|
||||||
@ -710,7 +727,7 @@ class MockClient(object):
|
|||||||
if not port_security and ret['security_groups']:
|
if not port_security and ret['security_groups']:
|
||||||
raise exception.SecurityGroupCannotBeApplied()
|
raise exception.SecurityGroupCannotBeApplied()
|
||||||
|
|
||||||
if network['subnets']:
|
if network['subnets'] and p.get('ip_allocation') != 'deferred':
|
||||||
ret['fixed_ips'] = [{'subnet_id': network['subnets'][0],
|
ret['fixed_ips'] = [{'subnet_id': network['subnets'][0],
|
||||||
'ip_address': '10.0.0.1'}]
|
'ip_address': '10.0.0.1'}]
|
||||||
if not ret['security_groups'] and (port_security is None or
|
if not ret['security_groups'] and (port_security is None or
|
||||||
|
@ -3055,6 +3055,37 @@ class TestNeutronv2WithMock(test.TestCase):
|
|||||||
'fake-user', 'fake-project',
|
'fake-user', 'fake-project',
|
||||||
auth_token='bff4a5a6b9eb4ea2a6efec6eefb77936')
|
auth_token='bff4a5a6b9eb4ea2a6efec6eefb77936')
|
||||||
|
|
||||||
|
@mock.patch('nova.network.neutronv2.api.API._show_port')
|
||||||
|
def test_deferred_ip_port_immediate_allocation(self, mock_show):
|
||||||
|
port = {'network_id': 'my_netid1',
|
||||||
|
'device_id': None,
|
||||||
|
'id': uuids.port,
|
||||||
|
'fixed_ips': [], # no fixed ip
|
||||||
|
'ip_allocation': 'immediate', }
|
||||||
|
|
||||||
|
mock_show.return_value = port
|
||||||
|
|
||||||
|
requested_networks = objects.NetworkRequestList(
|
||||||
|
objects=[objects.NetworkRequest(port_id=port['id'])])
|
||||||
|
self.assertRaises(exception.PortRequiresFixedIP,
|
||||||
|
self.api.validate_networks,
|
||||||
|
self.context, requested_networks, 1)
|
||||||
|
|
||||||
|
@mock.patch('nova.network.neutronv2.api.API._show_port')
|
||||||
|
def test_deferred_ip_port_deferred_allocation(self, mock_show):
|
||||||
|
port = {'network_id': 'my_netid1',
|
||||||
|
'device_id': None,
|
||||||
|
'id': uuids.port,
|
||||||
|
'fixed_ips': [], # no fixed ip
|
||||||
|
'ip_allocation': 'deferred', }
|
||||||
|
|
||||||
|
mock_show.return_value = port
|
||||||
|
|
||||||
|
requested_networks = objects.NetworkRequestList(
|
||||||
|
objects=[objects.NetworkRequest(port_id=port['id'])])
|
||||||
|
count = self.api.validate_networks(self.context, requested_networks, 1)
|
||||||
|
self.assertEqual(1, count)
|
||||||
|
|
||||||
@mock.patch('oslo_concurrency.lockutils.lock')
|
@mock.patch('oslo_concurrency.lockutils.lock')
|
||||||
def test_get_instance_nw_info_locks_per_instance(self, mock_lock):
|
def test_get_instance_nw_info_locks_per_instance(self, mock_lock):
|
||||||
instance = objects.Instance(uuid=uuid.uuid4())
|
instance = objects.Instance(uuid=uuid.uuid4())
|
||||||
|
Loading…
Reference in New Issue
Block a user