Merge ~larsks/cloud-init:bug/sysconfig-ipv6 into cloud-init:master

Proposed by Lars Kellogg-Stedman
Status: Merged
Merged at revision: 1d751a6f46f044e3c3827f3cef0e4a2e71d50fe7
Proposed branch: ~larsks/cloud-init:bug/sysconfig-ipv6
Merge into: cloud-init:master
Diff against target: 200 lines (+118/-9)
2 files modified
cloudinit/net/sysconfig.py (+24/-9)
tests/unittests/test_net.py (+94/-0)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Scott Moser Needs Fixing
Joshua Harlow Pending
Review via email: mp+318800@code.launchpad.net

Commit message

net: support both ipv4 and ipv6 gateways

Previously, cloud-init would throw an exception if an interface had
both ipv4 and ipv6 addresses and a default gateway for each address
family. This change allows cloud-init to correctly configure
interfaces in this situation.

LP: #1669504

To post a comment you must log in.
Revision history for this message
Joshua Harlow (harlowja) :
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

Fix the flake8 errors, and this looks good.

review: Needs Fixing
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)

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 6e7739f..06de660 100644
3--- a/cloudinit/net/sysconfig.py
4+++ b/cloudinit/net/sysconfig.py
5@@ -87,7 +87,8 @@ class Route(ConfigMap):
6 def __init__(self, route_name, base_sysconf_dir):
7 super(Route, self).__init__()
8 self.last_idx = 1
9- self.has_set_default = False
10+ self.has_set_default_ipv4 = False
11+ self.has_set_default_ipv6 = False
12 self._route_name = route_name
13 self._base_sysconf_dir = base_sysconf_dir
14
15@@ -95,7 +96,8 @@ class Route(ConfigMap):
16 r = Route(self._route_name, self._base_sysconf_dir)
17 r._conf = self._conf.copy()
18 r.last_idx = self.last_idx
19- r.has_set_default = self.has_set_default
20+ r.has_set_default_ipv4 = self.has_set_default_ipv4
21+ r.has_set_default_ipv6 = self.has_set_default_ipv6
22 return r
23
24 @property
25@@ -119,10 +121,10 @@ class NetInterface(ConfigMap):
26 super(NetInterface, self).__init__()
27 self.children = []
28 self.routes = Route(iface_name, base_sysconf_dir)
29- self._kind = kind
30+ self.kind = kind
31+
32 self._iface_name = iface_name
33 self._conf['DEVICE'] = iface_name
34- self._conf['TYPE'] = self.iface_types[kind]
35 self._base_sysconf_dir = base_sysconf_dir
36
37 @property
38@@ -140,6 +142,8 @@ class NetInterface(ConfigMap):
39
40 @kind.setter
41 def kind(self, kind):
42+ if kind not in self.iface_types:
43+ raise ValueError(kind)
44 self._kind = kind
45 self._conf['TYPE'] = self.iface_types[kind]
46
47@@ -173,7 +177,7 @@ class Renderer(renderer.Renderer):
48 ('BOOTPROTO', 'none'),
49 ])
50
51- # If these keys exist, then there values will be used to form
52+ # If these keys exist, then their values will be used to form
53 # a BONDING_OPTS grouping; otherwise no grouping will be set.
54 bond_tpl_opts = tuple([
55 ('bond_mode', "mode=%s"),
56@@ -199,6 +203,7 @@ class Renderer(renderer.Renderer):
57 def _render_iface_shared(cls, iface, iface_cfg):
58 for k, v in cls.iface_defaults:
59 iface_cfg[k] = v
60+
61 for (old_key, new_key) in [('mac_address', 'HWADDR'), ('mtu', 'MTU')]:
62 old_value = iface.get(old_key)
63 if old_value is not None:
64@@ -227,10 +232,20 @@ class Renderer(renderer.Renderer):
65 if 'netmask' in subnet:
66 iface_cfg['NETMASK'] = subnet['netmask']
67 for route in subnet.get('routes', []):
68+ if subnet.get('ipv6'):
69+ gw_cfg = 'IPV6_DEFAULTGW'
70+ else:
71+ gw_cfg = 'GATEWAY'
72+
73 if _is_default_route(route):
74- if route_cfg.has_set_default:
75- raise ValueError("Duplicate declaration of default"
76- " route found for interface '%s'"
77+ if (
78+ (subnet.get('ipv4') and
79+ route_cfg.has_set_default_ipv4) or
80+ (subnet.get('ipv6') and
81+ route_cfg.has_set_default_ipv6)
82+ ):
83+ raise ValueError("Duplicate declaration of default "
84+ "route found for interface '%s'"
85 % (iface_cfg.name))
86 # NOTE(harlowja): ipv6 and ipv4 default gateways
87 gw_key = 'GATEWAY0'
88@@ -242,7 +257,7 @@ class Renderer(renderer.Renderer):
89 # also provided the default route?
90 iface_cfg['DEFROUTE'] = True
91 if 'gateway' in route:
92- iface_cfg['GATEWAY'] = route['gateway']
93+ iface_cfg[gw_cfg] = route['gateway']
94 route_cfg.has_set_default = True
95 else:
96 gw_key = 'GATEWAY%s' % route_cfg.last_idx
97diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
98index 4b03ff7..8d25310 100644
99--- a/tests/unittests/test_net.py
100+++ b/tests/unittests/test_net.py
101@@ -248,6 +248,100 @@ nameserver 172.19.0.12
102 ('etc/udev/rules.d/70-persistent-net.rules',
103 "".join(['SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ',
104 'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n']))]
105+ },
106+ {
107+ 'in_data': {
108+ "services": [{"type": "dns", "address": "172.19.0.12"}],
109+ "networks": [{
110+ "network_id": "public-ipv4",
111+ "type": "ipv4", "netmask": "255.255.252.0",
112+ "link": "tap1a81968a-79",
113+ "routes": [{
114+ "netmask": "0.0.0.0",
115+ "network": "0.0.0.0",
116+ "gateway": "172.19.3.254",
117+ }],
118+ "ip_address": "172.19.1.34", "id": "network0"
119+ }, {
120+ "network_id": "public-ipv6",
121+ "type": "ipv6", "netmask": "",
122+ "link": "tap1a81968a-79",
123+ "routes": [
124+ {
125+ "gateway": "2001:DB8::1",
126+ "netmask": "::",
127+ "network": "::"
128+ }
129+ ],
130+ "ip_address": "2001:DB8::10", "id": "network1"
131+ }],
132+ "links": [
133+ {
134+ "ethernet_mac_address": "fa:16:3e:ed:9a:59",
135+ "mtu": None, "type": "bridge", "id":
136+ "tap1a81968a-79",
137+ "vif_id": "1a81968a-797a-400f-8a80-567f997eb93f"
138+ },
139+ ],
140+ },
141+ 'in_macs': {
142+ 'fa:16:3e:ed:9a:59': 'eth0',
143+ },
144+ 'out_sysconfig': [
145+ ('etc/sysconfig/network-scripts/ifcfg-eth0',
146+ """
147+# Created by cloud-init on instance boot automatically, do not edit.
148+#
149+BOOTPROTO=none
150+DEVICE=eth0
151+HWADDR=fa:16:3e:ed:9a:59
152+NM_CONTROLLED=no
153+ONBOOT=yes
154+TYPE=Ethernet
155+USERCTL=no
156+""".lstrip()),
157+ ('etc/sysconfig/network-scripts/ifcfg-eth0:0',
158+ """
159+# Created by cloud-init on instance boot automatically, do not edit.
160+#
161+BOOTPROTO=static
162+DEFROUTE=yes
163+DEVICE=eth0:0
164+GATEWAY=172.19.3.254
165+HWADDR=fa:16:3e:ed:9a:59
166+IPADDR=172.19.1.34
167+NETMASK=255.255.252.0
168+NM_CONTROLLED=no
169+ONBOOT=yes
170+TYPE=Ethernet
171+USERCTL=no
172+""".lstrip()),
173+ ('etc/sysconfig/network-scripts/ifcfg-eth0:1',
174+ """
175+# Created by cloud-init on instance boot automatically, do not edit.
176+#
177+BOOTPROTO=static
178+DEFROUTE=yes
179+DEVICE=eth0:1
180+HWADDR=fa:16:3e:ed:9a:59
181+IPV6ADDR=2001:DB8::10
182+IPV6INIT=yes
183+IPV6_DEFAULTGW=2001:DB8::1
184+NETMASK=
185+NM_CONTROLLED=no
186+ONBOOT=yes
187+TYPE=Ethernet
188+USERCTL=no
189+""".lstrip()),
190+ ('etc/resolv.conf',
191+ """
192+; Created by cloud-init on instance boot automatically, do not edit.
193+;
194+nameserver 172.19.0.12
195+""".lstrip()),
196+ ('etc/udev/rules.d/70-persistent-net.rules',
197+ "".join(['SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ',
198+ 'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n']))]
199 }
200 ]
201

Subscribers

People subscribed via source and target branches