From 0ce584bd9af19daf96bc92dce231a7fac5085aac Mon Sep 17 00:00:00 2001 From: Oleg Bondarev Date: Tue, 2 Nov 2021 14:08:16 +0300 Subject: [PATCH] Add Local IP fullstack test cases The test: - creates 3 VMs in the same network on 2 hosts - creates Local IP and associates first VM's port with it - pings first VM from the second one (same host) by local IP address and checks pings are ok - pings first VM from the third one (diff host) by local IP and verifies local IP is not accessible Second test does the same but with openvswitch security groups enabled on hosts and with static NAT rules for Local IPs. Partial-Bug: #1930200 Change-Id: I27a57170b88eda951ff3ef1d5320cb8c692a074a --- neutron/tests/fullstack/resources/client.py | 32 +++++ neutron/tests/fullstack/resources/config.py | 4 + .../tests/fullstack/resources/environment.py | 5 +- neutron/tests/fullstack/test_local_ip.py | 123 ++++++++++++++++++ 4 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 neutron/tests/fullstack/test_local_ip.py diff --git a/neutron/tests/fullstack/resources/client.py b/neutron/tests/fullstack/resources/client.py index 4b6ea289e6a..9a655b8e62d 100644 --- a/neutron/tests/fullstack/resources/client.py +++ b/neutron/tests/fullstack/resources/client.py @@ -62,6 +62,38 @@ class ClientFixture(fixtures.Fixture): return delete(id) + def create_local_ip(self, project_id, network_id=None): + delete = self.delete_local_ip + + path = '/local_ips' + body = {'local_ip': {'project_id': project_id, + 'network_id': network_id}} + resp = self.client.post(path, body=body) + data = resp['local_ip'] + self.addCleanup(_safe_method(delete), data['id']) + return data + + def create_local_ip_association(self, local_ip_id, port_id, fixed_ip=None): + delete = self.delete_local_ip_association + + path = '/local_ips/{0}/port_associations'.format(local_ip_id) + body = {'port_association': {'fixed_port_id': port_id}} + if fixed_ip: + body['port_association']['fixed_ip'] = fixed_ip + resp = self.client.post(path, body=body) + data = resp['port_association'] + self.addCleanup(_safe_method(delete), local_ip_id, port_id) + return data + + def delete_local_ip(self, local_ip_id): + path = "/local-ips/{0}".format(local_ip_id) + self.client.delete(path) + + def delete_local_ip_association(self, local_ip_id, port_id): + path = "/local_ips/{0}/port_associations/{1}".format( + local_ip_id, port_id) + self.client.delete(path) + def create_router(self, tenant_id, name=None, ha=False, external_network=None, external_subnet=None): resource_type = 'router' diff --git a/neutron/tests/fullstack/resources/config.py b/neutron/tests/fullstack/resources/config.py index 806feed710d..77c70ba0996 100644 --- a/neutron/tests/fullstack/resources/config.py +++ b/neutron/tests/fullstack/resources/config.py @@ -264,6 +264,10 @@ class OVSConfigFixture(ConfigFixture): 'dhcp_renewal_time': '0', 'dhcp_rebinding_time': '0'} }) + if env_desc.local_ip_ext: + self.config['agent']['extensions'] = 'local_ip' + if host_desc.firewall_driver == 'openvswitch': + self.config['local_ip'] = {'static_nat': 'True'} def _setUp(self): self.config['ovs'].update({ diff --git a/neutron/tests/fullstack/resources/environment.py b/neutron/tests/fullstack/resources/environment.py index 03907b5d799..5c7136695a5 100644 --- a/neutron/tests/fullstack/resources/environment.py +++ b/neutron/tests/fullstack/resources/environment.py @@ -42,7 +42,7 @@ class EnvironmentDescription(object): has_placement=False, placement_port=None, dhcp_scheduler_class=None, ml2_extension_drivers=None, api_workers=1, - enable_traditional_dhcp=True): + enable_traditional_dhcp=True, local_ip_ext=False): self.network_type = network_type self.l2_pop = l2_pop self.qos = qos @@ -66,6 +66,9 @@ class EnvironmentDescription(object): self.ml2_extension_drivers = ml2_extension_drivers self.api_workers = api_workers self.enable_traditional_dhcp = enable_traditional_dhcp + self.local_ip_ext = local_ip_ext + if self.local_ip_ext: + self.service_plugins += ',local_ip' @property def tunneling_enabled(self): diff --git a/neutron/tests/fullstack/test_local_ip.py b/neutron/tests/fullstack/test_local_ip.py new file mode 100644 index 00000000000..d957e1f2043 --- /dev/null +++ b/neutron/tests/fullstack/test_local_ip.py @@ -0,0 +1,123 @@ +# Copyright 2021 Huawei, Inc. +# All Rights Reserved. +# +# 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 neutron_lib import constants +from oslo_utils import uuidutils + +from neutron.tests.fullstack import base +from neutron.tests.fullstack.resources import environment +from neutron.tests.fullstack.resources import machine +from neutron.tests.unit import testlib_api + +load_tests = testlib_api.module_load_tests + + +class LocalIPTestCase(base.BaseFullStackTestCase): + number_of_hosts = 2 + + scenarios = [ + ('with_conntrack_rules', {'firewall_driver': 'iptables_hybrid'}), + # with ovs firewall driver - test config will set static_nat=True + ('static_nat', {'firewall_driver': 'openvswitch'}), + ] + + def setUp(self): + host_desc = [ + environment.HostDescription( + l2_agent_type=constants.AGENT_TYPE_OVS, + firewall_driver=self.firewall_driver, + l3_agent=False, + dhcp_agent=False) for _ in range(self.number_of_hosts)] + env_desc = environment.EnvironmentDescription( + mech_drivers='openvswitch', + local_ip_ext=True) + env = environment.Environment(env_desc, host_desc) + super(LocalIPTestCase, self).setUp(env) + self.project_id = uuidutils.generate_uuid() + + self.network = self.safe_client.create_network( + self.project_id, 'network-local-ip-test') + self.subnet_v4 = self.safe_client.create_subnet( + self.project_id, self.network['id'], + cidr='10.0.0.0/24', + gateway_ip='10.0.0.1', + name='subnet-v4-test') + + def _prepare_vms(self): + port1 = self.safe_client.create_port( + self.project_id, self.network['id'], + self.environment.hosts[0].hostname, + device_owner="compute:test_local_ip") + + port2 = self.safe_client.create_port( + self.project_id, self.network['id'], + self.environment.hosts[0].hostname, + device_owner="compute:test_local_ip") + + port3 = self.safe_client.create_port( + self.project_id, self.network['id'], + self.environment.hosts[1].hostname, + device_owner="compute:test_local_ip") + + vm1 = self.useFixture( + machine.FakeFullstackMachine( + self.environment.hosts[0], + self.network['id'], + self.project_id, + self.safe_client, + neutron_port=port1)) + + vm2 = self.useFixture( + machine.FakeFullstackMachine( + self.environment.hosts[0], + self.network['id'], + self.project_id, + self.safe_client, + neutron_port=port2)) + + vm_diff_host = self.useFixture( + machine.FakeFullstackMachine( + self.environment.hosts[1], + self.network['id'], + self.project_id, + self.safe_client, + neutron_port=port3)) + return machine.FakeFullstackMachinesList([vm1, vm2, vm_diff_host]) + + def _create_local_ip(self): + return self.safe_client.create_local_ip( + self.project_id, network_id=self.network['id']) + + def _associate_local_ip(self, local_ip_id, port_id): + return self.safe_client.create_local_ip_association( + local_ip_id, port_id) + + def test_vm_is_accessible_by_local_ip(self): + vms = self._prepare_vms() + vms.block_until_all_boot() + # first check basic connectivity between VMs + vms.ping_all() + + local_ip = self._create_local_ip() + self._associate_local_ip( + local_ip['id'], vms[0].neutron_port['id']) + # VM on same host should have access to this Local IP + vms[1].block_until_ping(local_ip['local_ip_address']) + + # VM on different host shouldn't have access to this Local IP + vms[2].assert_no_ping(local_ip['local_ip_address']) + + # check that VMs can still access each other with fixed IPs + vms.ping_all()