Merge ~chad.smith/cloud-init:sysconfig-has-default into cloud-init:master

Proposed by Chad Smith on 2017-05-10
Status: Merged
Merged at revision: dd03bb411c9a6f10854a3bbc3223b204c3d4d174
Proposed branch: ~chad.smith/cloud-init:sysconfig-has-default
Merge into: cloud-init:master
Diff against target: 120 lines (+83/-7)
2 files modified
cloudinit/net/sysconfig.py (+7/-7)
tests/unittests/test_net.py (+76/-0)
Reviewer Review Type Date Requested Status
Scott Moser 2017-05-10 Needs Fixing on 2017-05-10
Server Team CI bot continuous-integration Approve on 2017-05-10
Review via email: mp+323819@code.launchpad.net

Commit Message

sysconfig: Raise ValueError when multiple default gateways are present.

Properly set Route.has_set_default_ipv6 or *_ipv4 to track whether a route already has a default gateway defined already for ipv6 vs ipv4. This was a regression introduced as part of the fix for lp:1669504 when Route.has_set_default was deprecated in favor of granular has_set_default_ipv4 or *_ipv6.

The code was setting a now unused instance attribute Route.has_set_default which was not checked when raising ValueError( used after the fix for lp:1669504 which dropped has_set_default in favor of has_set_default_(ipv4|ipv6). As a result the gateway duplication check no longer raised ValueErrors when seeing ipv4 or ipv6 default gateways. Added unit tests to exercise this expected raised ValueError. Also moved is_ipv6 = subnet.get('ipv6') logic out of a for loop because we don't need to recalculate the same value every route iteration.

LP: # 1687485

Description of the Change

sysconfig: Raise ValueError when multiple default gateways are present.

Fixed setting Route.has_set_default_ipv6 or *_ipv4 to track whether a
route already has a default gateway defined. The code was setting
Route.has_set_default which wasn't checked when raising "duplicate
gateway" ValueErrors. Added unit tests to exercise this expected raised
ValueError. Also moved is_ipv6 = subnet.get('ipv6') logic out of a for
loop because we don't need to recalculate the same value every route
iteration.

To post a comment you must log in.
Scott Moser (smoser) wrote :

Change commit message to be

 Subject less than 75 chars
 <blank line>
 longer description

Per your message, you fixed something, but then didn't add a unit test to show that that thing was fixed, right?

review: Needs Fixing
Chad Smith (chad.smith) :
Chad Smith (chad.smith) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
2index 504e4d0..d981277 100644
3--- a/cloudinit/net/sysconfig.py
4+++ b/cloudinit/net/sysconfig.py
5@@ -232,12 +232,8 @@ class Renderer(renderer.Renderer):
6 iface_cfg.name))
7 if 'netmask' in subnet:
8 iface_cfg['NETMASK'] = subnet['netmask']
9+ is_ipv6 = subnet.get('ipv6')
10 for route in subnet.get('routes', []):
11- if subnet.get('ipv6'):
12- gw_cfg = 'IPV6_DEFAULTGW'
13- else:
14- gw_cfg = 'GATEWAY'
15-
16 if _is_default_route(route):
17 if (
18 (subnet.get('ipv4') and
19@@ -258,8 +254,12 @@ class Renderer(renderer.Renderer):
20 # also provided the default route?
21 iface_cfg['DEFROUTE'] = True
22 if 'gateway' in route:
23- iface_cfg[gw_cfg] = route['gateway']
24- route_cfg.has_set_default = True
25+ if is_ipv6:
26+ iface_cfg['IPV6_DEFAULTGW'] = route['gateway']
27+ route_cfg.has_set_default_ipv6 = True
28+ else:
29+ iface_cfg['GATEWAY'] = route['gateway']
30+ route_cfg.has_set_default_ipv4 = True
31 else:
32 gw_key = 'GATEWAY%s' % route_cfg.last_idx
33 nm_key = 'NETMASK%s' % route_cfg.last_idx
34diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
35index 89e7536..eec4159 100644
36--- a/tests/unittests/test_net.py
37+++ b/tests/unittests/test_net.py
38@@ -880,6 +880,82 @@ USERCTL=no
39 """.lstrip()
40 self.assertEqual(expected_content, content)
41
42+ def test_multiple_ipv4_default_gateways(self):
43+ """ValueError is raised when duplicate ipv4 gateways exist."""
44+ net_json = {
45+ "services": [{"type": "dns", "address": "172.19.0.12"}],
46+ "networks": [{
47+ "network_id": "dacd568d-5be6-4786-91fe-750c374b78b4",
48+ "type": "ipv4", "netmask": "255.255.252.0",
49+ "link": "tap1a81968a-79",
50+ "routes": [{
51+ "netmask": "0.0.0.0",
52+ "network": "0.0.0.0",
53+ "gateway": "172.19.3.254",
54+ }, {
55+ "netmask": "0.0.0.0", # A second default gateway
56+ "network": "0.0.0.0",
57+ "gateway": "172.20.3.254",
58+ }],
59+ "ip_address": "172.19.1.34", "id": "network0"
60+ }],
61+ "links": [
62+ {
63+ "ethernet_mac_address": "fa:16:3e:ed:9a:59",
64+ "mtu": None, "type": "bridge", "id":
65+ "tap1a81968a-79",
66+ "vif_id": "1a81968a-797a-400f-8a80-567f997eb93f"
67+ },
68+ ],
69+ }
70+ macs = {'fa:16:3e:ed:9a:59': 'eth0'}
71+ render_dir = self.tmp_dir()
72+ network_cfg = openstack.convert_net_json(net_json, known_macs=macs)
73+ ns = network_state.parse_net_config_data(network_cfg,
74+ skip_broken=False)
75+ renderer = sysconfig.Renderer()
76+ with self.assertRaises(ValueError):
77+ renderer.render_network_state(ns, render_dir)
78+ self.assertEqual([], os.listdir(render_dir))
79+
80+ def test_multiple_ipv6_default_gateways(self):
81+ """ValueError is raised when duplicate ipv6 gateways exist."""
82+ net_json = {
83+ "services": [{"type": "dns", "address": "172.19.0.12"}],
84+ "networks": [{
85+ "network_id": "public-ipv6",
86+ "type": "ipv6", "netmask": "",
87+ "link": "tap1a81968a-79",
88+ "routes": [{
89+ "gateway": "2001:DB8::1",
90+ "netmask": "::",
91+ "network": "::"
92+ }, {
93+ "gateway": "2001:DB9::1",
94+ "netmask": "::",
95+ "network": "::"
96+ }],
97+ "ip_address": "2001:DB8::10", "id": "network1"
98+ }],
99+ "links": [
100+ {
101+ "ethernet_mac_address": "fa:16:3e:ed:9a:59",
102+ "mtu": None, "type": "bridge", "id":
103+ "tap1a81968a-79",
104+ "vif_id": "1a81968a-797a-400f-8a80-567f997eb93f"
105+ },
106+ ],
107+ }
108+ macs = {'fa:16:3e:ed:9a:59': 'eth0'}
109+ render_dir = self.tmp_dir()
110+ network_cfg = openstack.convert_net_json(net_json, known_macs=macs)
111+ ns = network_state.parse_net_config_data(network_cfg,
112+ skip_broken=False)
113+ renderer = sysconfig.Renderer()
114+ with self.assertRaises(ValueError):
115+ renderer.render_network_state(ns, render_dir)
116+ self.assertEqual([], os.listdir(render_dir))
117+
118 def test_openstack_rendering_samples(self):
119 for os_sample in OS_SAMPLES:
120 render_dir = self.tmp_dir()

Subscribers

People subscribed via source and target branches