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:
parent
f87a63a46e
commit
0d71c5a1c1
@ -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
|
||||
|
53
nova/tests/functional/test_ip_allocation.py
Normal file
53
nova/tests/functional/test_ip_allocation.py
Normal 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}])
|
@ -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)
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user