From 0d71c5a1c1f0e474d4b612eadf8787172af34e15 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Fri, 5 Jul 2019 17:16:09 +0000 Subject: [PATCH] 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 --- nova/network/neutron.py | 12 ++++- nova/tests/functional/test_ip_allocation.py | 53 +++++++++++++++++++ nova/tests/unit/network/test_neutron.py | 21 ++++++++ ...ith-unaddressed-port-4cb05bb6dc859d98.yaml | 3 ++ 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 nova/tests/functional/test_ip_allocation.py create mode 100644 releasenotes/notes/bp-boot-vm-with-unaddressed-port-4cb05bb6dc859d98.yaml diff --git a/nova/network/neutron.py b/nova/network/neutron.py index e02da93b574d..9cf07d7f2c2a 100644 --- a/nova/network/neutron.py +++ b/nova/network/neutron.py @@ -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 diff --git a/nova/tests/functional/test_ip_allocation.py b/nova/tests/functional/test_ip_allocation.py new file mode 100644 index 000000000000..a899641abe64 --- /dev/null +++ b/nova/tests/functional/test_ip_allocation.py @@ -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}]) diff --git a/nova/tests/unit/network/test_neutron.py b/nova/tests/unit/network/test_neutron.py index 5056b70c4ec8..6c6e6ca5ee91 100644 --- a/nova/tests/unit/network/test_neutron.py +++ b/nova/tests/unit/network/test_neutron.py @@ -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) diff --git a/releasenotes/notes/bp-boot-vm-with-unaddressed-port-4cb05bb6dc859d98.yaml b/releasenotes/notes/bp-boot-vm-with-unaddressed-port-4cb05bb6dc859d98.yaml new file mode 100644 index 000000000000..55c43ddd4c7b --- /dev/null +++ b/releasenotes/notes/bp-boot-vm-with-unaddressed-port-4cb05bb6dc859d98.yaml @@ -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.