From ed79a4d738f6234557ee391097250d7f61dd6a74 Mon Sep 17 00:00:00 2001 From: zhiyuan_cai Date: Thu, 25 May 2017 17:35:32 +0800 Subject: [PATCH] Boot a VM in a subnet without gateway 1. What is the problem We can create a subnet without gateway in the central Neutron, but when we boot a VM in that subnet, the request fails since local Neutron is trying to calculate the IP allocation pool based on the central subnet gateway IP, which is None. 2. What is the solution for the problem If a central subnet doesn't have gateway, do not re-calculate local IP allocation pool when creating local subnet. 3. What features need to be implemented to the Tricircle to realize the solution Support booting a VM in a subnet that has not gateway Change-Id: I04f7f9f9926f22bca3490ee46e2163b9c17bb8fe Closes-Bug: #1693446 --- tricircle/network/helper.py | 3 ++ tricircle/network/local_plugin.py | 10 ++++-- tricircle/tempestplugin/smoke_test.sh | 10 ++++++ .../tempestplugin/smoke_test_validation.py | 6 ++-- .../tests/unit/network/test_local_plugin.py | 32 +++++++++++++------ 5 files changed, 47 insertions(+), 14 deletions(-) diff --git a/tricircle/network/helper.py b/tricircle/network/helper.py index caf207b0..bce1d1be 100644 --- a/tricircle/network/helper.py +++ b/tricircle/network/helper.py @@ -364,6 +364,9 @@ class NetworkHelper(object): """ pools = t_subnet['allocation_pools'] t_gateway_ip = t_subnet['gateway_ip'] + if not t_gateway_ip: + # gateway is None, so we don't need to split allocation pools + return pools new_pools = NetworkHelper._split_pools_by_bottom_gateway_ip( pools, b_gateway_ip) if t_gateway_ip == b_gateway_ip: diff --git a/tricircle/network/local_plugin.py b/tricircle/network/local_plugin.py index 63f9ea8f..1bbe9233 100644 --- a/tricircle/network/local_plugin.py +++ b/tricircle/network/local_plugin.py @@ -342,10 +342,14 @@ class TricirclePlugin(plugin.Ml2Plugin): return b_subnet def _create_bottom_subnet(self, t_ctx, q_ctx, t_subnet): - gateway_port = self._ensure_gateway_port(t_ctx, t_subnet) + if t_subnet['gateway_ip']: + gateway_port = self._ensure_gateway_port(t_ctx, t_subnet) + b_gateway_ip = gateway_port['fixed_ips'][0]['ip_address'] + else: + b_gateway_ip = None subnet_body = helper.NetworkHelper.get_create_subnet_body( - gateway_port['tenant_id'], t_subnet, t_subnet['network_id'], - gateway_port['fixed_ips'][0]['ip_address'])['subnet'] + t_subnet['tenant_id'], t_subnet, t_subnet['network_id'], + b_gateway_ip)['subnet'] t_subnet['gateway_ip'] = subnet_body['gateway_ip'] t_subnet['allocation_pools'] = subnet_body['allocation_pools'] diff --git a/tricircle/tempestplugin/smoke_test.sh b/tricircle/tempestplugin/smoke_test.sh index 48bf9ad8..deeb1bd4 100644 --- a/tricircle/tempestplugin/smoke_test.sh +++ b/tricircle/tempestplugin/smoke_test.sh @@ -73,6 +73,16 @@ $openstackpod2 server create --flavor 1 --image $image2_id --nic net-id=$net2_id echo attach subnet2 to router $openstacktop router add subnet router subnet2 +echo create network4 +net4_id=$($openstacktop network create net4 -c id -f value) + +echo create subnet4 that has no gateway +$openstacktop subnet create --subnet-range 10.0.4.0/24 --network net4 \ + --gateway None subnet4 + +echo create server3 +$openstackpod1 server create --flavor 1 --image $image1_id --nic net-id=$net4_id vm3 + sleep 20 TOP_DIR=$DEVSTACK_DIR diff --git a/tricircle/tempestplugin/smoke_test_validation.py b/tricircle/tempestplugin/smoke_test_validation.py index fbef968a..fc26dcc3 100644 --- a/tricircle/tempestplugin/smoke_test_validation.py +++ b/tricircle/tempestplugin/smoke_test_validation.py @@ -30,8 +30,10 @@ class ContainedString(object): CONDITIONS = { - '1': {'server': [{'Name': 'vm1', 'Status': 'ACTIVE'}], - 'subnet': [{'Subnet': '100.0.0.0/24'}, {'Subnet': '10.0.1.0/24'}], + '1': {'server': [{'Name': 'vm1', 'Status': 'ACTIVE'}, + {'Name': 'vm3', 'Status': 'ACTIVE'}], + 'subnet': [{'Subnet': '100.0.0.0/24'}, {'Subnet': '10.0.1.0/24'}, + {'Subnet': '10.0.4.0/24'}], 'router_port': [{'Fixed IP Addresses': ContainedString('10.0.1')}, {'Fixed IP Addresses': ContainedString('100.0.0')}], 'router': [ diff --git a/tricircle/tests/unit/network/test_local_plugin.py b/tricircle/tests/unit/network/test_local_plugin.py index 648c457f..6ec3709f 100644 --- a/tricircle/tests/unit/network/test_local_plugin.py +++ b/tricircle/tests/unit/network/test_local_plugin.py @@ -337,15 +337,23 @@ class PluginTest(unittest.TestCase): def ip_to_digit(ip): return int(ip[ip.rindex('.') + 1:]) - pool_range = list(range(ip_to_digit(t_gateway_ip), - ip_to_digit(pool['end']) + 1)) - # we include the top gateway ip in the bottom ip allocation pool - b_pool_range1 = list(range(ip_to_digit(b_pools[0]['start']), - ip_to_digit(b_pools[0]['end']) + 1)) - b_pool_range2 = list(range(ip_to_digit(b_pools[1]['start']), - ip_to_digit(b_pools[1]['end']) + 1)) - b_pool_range = b_pool_range1 + [ - ip_to_digit(b_gateway_ip)] + b_pool_range2 + if t_gateway_ip: + pool_range = list(range(ip_to_digit(t_gateway_ip), + ip_to_digit(pool['end']) + 1)) + # we include the top gateway ip in the bottom ip allocation pool + b_pool_range1 = list(range(ip_to_digit(b_pools[0]['start']), + ip_to_digit(b_pools[0]['end']) + 1)) + b_pool_range2 = list(range(ip_to_digit(b_pools[1]['start']), + ip_to_digit(b_pools[1]['end']) + 1)) + b_pool_range = b_pool_range1 + [ + ip_to_digit(b_gateway_ip)] + b_pool_range2 + else: + self.assertIsNone(t_gateway_ip) + self.assertIsNone(b_gateway_ip) + pool_range = list(range(ip_to_digit(pool['start']), + ip_to_digit(pool['end']))) + b_pool_range = list(range(ip_to_digit(b_pools[0]['start']), + ip_to_digit(b_pools[0]['end']))) port.pop('name') b_port.pop('name') self.assertDictEqual(net, b_net) @@ -376,6 +384,12 @@ class PluginTest(unittest.TestCase): t_net, t_subnet, t_port, _ = self._prepare_resource() self._validate(t_net, t_subnet, t_port) + @patch.object(t_context, 'get_context_from_neutron_context', new=mock.Mock) + def test_get_network_no_gateway(self): + t_net, t_subnet, t_port, _ = self._prepare_resource() + update_resource('subnet', True, t_subnet['id'], {'gateway_ip': None}) + self._validate(t_net, t_subnet, t_port) + @patch.object(t_context, 'get_context_from_neutron_context', new=mock.Mock) @patch.object(client.Client, 'get_admin_token', new=mock.Mock) def test_get_networks(self):