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:
Carl Baldwin 2016-03-30 19:22:41 +00:00
parent a0fcff8bad
commit 54b8d7770a
5 changed files with 60 additions and 6 deletions

View File

@ -420,8 +420,8 @@ class ServersController(wsgi.Controller):
raise exc.HTTPBadRequest(explanation=msg)
if request.address is not None:
msg = _("Specified Fixed IP '%(addr)s' cannot be used "
"with port '%(port)s': port already has "
"a Fixed IP allocated.") % {
"with port '%(port)s': the two cannot be "
"specified together.") % {
"addr": request.address,
"port": request.port_id}
raise exc.HTTPBadRequest(explanation=msg)

View File

@ -1413,7 +1413,12 @@ class API(base_api.NetworkAPI):
neutron_client=neutron)
if port.get('device_id', None):
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(
port_id=request.port_id)
request.network_id = port['network_id']

View File

@ -419,7 +419,8 @@ class SecurityGroupAPI(security_group_base.SecurityGroupBase):
def _has_security_group_requirements(self, port):
port_security_enabled = port.get('port_security_enabled', True)
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 False

View File

@ -73,7 +73,7 @@ class TestNeutronSecurityGroupsV21(
def _create_port(self, **kwargs):
body = {'port': {'binding:vnic_type': model.VNIC_TYPE_NORMAL}}
fields = ['security_groups', 'device_id', 'network_id',
'port_security_enabled']
'port_security_enabled', 'ip_allocation']
for field in fields:
if field in kwargs:
body['port'][field] = kwargs[field]
@ -278,6 +278,22 @@ class TestNeutronSecurityGroupsV21(
self.manager._addSecurityGroup,
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):
self.stub_out('nova.db.instance_get_by_uuid',
test_security_groups.return_server)
@ -696,6 +712,7 @@ class MockClient(object):
'admin_state_up': p.get('admin_state_up', True),
'security_groups': p.get('security_groups', []),
'network_id': p.get('network_id'),
'ip_allocation': p.get('ip_allocation'),
'binding:vnic_type':
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']:
raise exception.SecurityGroupCannotBeApplied()
if network['subnets']:
if network['subnets'] and p.get('ip_allocation') != 'deferred':
ret['fixed_ips'] = [{'subnet_id': network['subnets'][0],
'ip_address': '10.0.0.1'}]
if not ret['security_groups'] and (port_security is None or

View File

@ -3055,6 +3055,37 @@ class TestNeutronv2WithMock(test.TestCase):
'fake-user', 'fake-project',
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')
def test_get_instance_nw_info_locks_per_instance(self, mock_lock):
instance = objects.Instance(uuid=uuid.uuid4())