neutron: Allow to spawn VMs with port without IP address

Currently, all ports attached to an instance must have a fixed IP
address already associated with them ('immediate' IP allocation policy)
or must get one during instance creation ('deferred' IP allocation
policy). However, there are situations where is can be helpful to create
a port without an IP address, for example, when there is an IP address
but it is not managed by neutron (this is unfortunately quite common for
certain NFV applications). The 'vm-without-l3-address' neutron blueprint
[1] added support for these kinds of ports, but until now, nova still
insisted on either a pre-existing IP assignment or deferred IP
assignment. Close the gap and allow nova to use these ports.

Thanks to I438cbab43b45b5f7afc820b77fcf5a0e823d0eff we no longer need to
check after binding to ensure we're on a backend that has
'connectivity' of 'l2'.

[1] https://specs.openstack.org/openstack/neutron-specs/specs/newton/unaddressed-port.html

Change-Id: I3c49f151ff1391e0a72c073d0d9c24e986c08938
Implements-blueprint: vm-boot-with-unaddressed-port
This commit is contained in:
Rodolfo Alonso Hernandez 2019-07-05 17:16:09 +00:00 committed by Sylvain Bauza
parent f87a63a46e
commit 0d71c5a1c1
4 changed files with 88 additions and 1 deletions

View File

@ -2366,14 +2366,24 @@ class API:
neutron_client=neutron)
if port.get('device_id', None):
raise exception.PortInUse(port_id=request.port_id)
deferred_ip = port.get('ip_allocation') == 'deferred'
ipless_port = port.get('ip_allocation') == 'none'
# 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'):
# NOTE(sbauza): We don't need to validate the
# 'connectivity' attribute of the port's
# 'binding:vif_details' to ensure it's 'l2', as Neutron
# already verifies it.
if (
not (deferred_ip or ipless_port) and
not port.get('fixed_ips')
):
raise exception.PortRequiresFixedIP(
port_id=request.port_id)
request.network_id = port['network_id']
else:
ports_needed_per_instance += 1

View File

@ -0,0 +1,53 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from nova.tests.functional import integrated_helpers
class IPAllocationTests(integrated_helpers._IntegratedTestBase):
"""Test behavior with various IP allocation policies.
This mainly exists to test the 'deferred' and 'none' policies.
"""
compute_driver = 'fake.MediumFakeDriver'
microversion = 'latest'
ADMIN_API = True
def setUp(self):
super().setUp()
# add a port with an ip_allocation of 'none'
port = {
'name': '',
'description': '',
'network_id': self.neutron.network_1['id'],
'admin_state_up': True,
'status': 'ACTIVE',
'mac_address': 'ee:94:88:57:d5:7a',
# The ip_allocation is 'none', so fixed_ips should be null
'fixed_ips': [],
'tenant_id': self.neutron.tenant_id,
'project_id': self.neutron.tenant_id,
'device_id': '',
'binding:profile': {},
'binding:vnic_type': 'normal',
'binding:vif_type': 'ovs',
'binding:vif_details': {},
'ip_allocation': 'none',
}
created_port = self.neutron.create_port({'port': port})
self.port_id = created_port['port']['id']
def test_boot_with_none_policy(self):
"""Create a port with the 'none' policy."""
self._create_server(
networks=[{'port': self.port_id}])

View File

@ -3711,6 +3711,27 @@ class TestAPI(TestAPIBase):
count = self.api.validate_networks(self.context, requested_networks, 1)
self.assertEqual(1, count)
@mock.patch('nova.network.neutron.API._show_port')
def test_deferred_ip_port_none_allocation(self, mock_show):
"""Test behavior when the 'none' IP allocation policy is used."""
port = {
'network_id': 'my_netid1',
'device_id': None,
'id': uuids.port,
'fixed_ips': [], # no fixed ip
'ip_allocation': 'none',
'binding:vif_details': {
'connectivity': 'l2',
},
}
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=uuids.fake)

View File

@ -0,0 +1,3 @@
features:
- Nova now allows to create an instance with a non-deferred port that has
no fixed IP address if the network backend has level-2 connectivity.