Merge ~harald-jensas/cloud-init:bug/1806014 into cloud-init:master
- Git
- lp:~harald-jensas/cloud-init
- bug/1806014
- Merge into master
Status: | Superseded |
---|---|
Proposed branch: | ~harald-jensas/cloud-init:bug/1806014 |
Merge into: | cloud-init:master |
Prerequisite: | ~harald-jensas/cloud-init:bug/1847517 |
Diff against target: |
357 lines (+206/-6) 5 files modified
cloudinit/net/eni.py (+10/-0) cloudinit/net/netplan.py (+4/-1) cloudinit/net/network_state.py (+8/-4) cloudinit/net/sysconfig.py (+9/-1) tests/unittests/test_net.py (+175/-0) |
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ryan Harper | Needs Fixing | ||
Review via email: mp+374138@code.launchpad.net |
Commit message
Description of the change
- b8ddae4... by Harald Jensås
-
net: fix subnet_is_ipv6() for stateless|stateful
Function return false for ipv6_dhcpv6-
stateless| stateful,
the eni renderer does not add '6' to 'inet' which is
incorrect.The subnet_is_ipv6() function is updated to also return
true if startswith('ipv6').LP: #1848690
Harald Jensås (harald-jensas) wrote : | # |
> Thanks for sorting this out. One more element here is related to kernel
> defaults; at least on Ubuntu the kernel defaults the interfaces accept_ra
> value to 1; which allows RAs on interfaces that aren't forwarding.
>
> The code below which defaults accept-ra to False is going to break that. I
> would like to suggest that only *if* accept-ra is set in the source config
> (for example in the openstack network_data.json where dhcp6 stateful is
> specified, then it makes sense to include accept-ra: true in the interface
> config.
>
> In the renderers, if accept-ra is *not* defined, I'd like to avoid emitting
> any RA related config at all.
>
> Now that cloud-init handles dhcp6-stateless
> enable RA on stateful dhcp6, otherwise we should leave the kernel defaults.
>
> Additionally, ifupdown (eni) can toggle these values as well:
>
> accept_ra [0, 1, 2]
> autoconf [0, 1]
Thanks Ryan, this all makes sense.
I have updated the patch to not just enable if accept-ra: true. But also disable if false. And it should not change the OS defaults if accept-ra is not defined at all.
Unmerged commits
- 577ccfa... by Harald Jensås
-
net: IPv6 implement accept-ra RA's
Router advertisements are required for the default route
to be set up.sysconf: IPV6_FORCE_
ACCEPT_ RA controls accept_ra sysctl.
eni: mode static and mode dhcp 'accept_ra' controls sysctl.Add 'accept-ra: true|false' parameter to config v1 and
v2. When True: accept_ra is set to '1'. When False:
accept_ra is set to '0'. When not defined in config the
value is left to the operating system default.Also, set IPV6_FORCE_
ACCEPT_ RA=yes in sysconfig when
using Openstack and dhcpv6-stateful. - b8ddae4... by Harald Jensås
-
net: fix subnet_is_ipv6() for stateless|stateful
Function return false for ipv6_dhcpv6-
stateless| stateful,
the eni renderer does not add '6' to 'inet' which is
incorrect.The subnet_is_ipv6() function is updated to also return
true if startswith('ipv6').LP: #1848690
Preview Diff
1 | diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py | |||
2 | index 530922b..458c229 100644 | |||
3 | --- a/cloudinit/net/eni.py | |||
4 | +++ b/cloudinit/net/eni.py | |||
5 | @@ -399,6 +399,7 @@ class Renderer(renderer.Renderer): | |||
6 | 399 | def _render_iface(self, iface, render_hwaddress=False): | 399 | def _render_iface(self, iface, render_hwaddress=False): |
7 | 400 | sections = [] | 400 | sections = [] |
8 | 401 | subnets = iface.get('subnets', {}) | 401 | subnets = iface.get('subnets', {}) |
9 | 402 | accept_ra = iface.pop('accept-ra', None) | ||
10 | 402 | if subnets: | 403 | if subnets: |
11 | 403 | for index, subnet in enumerate(subnets): | 404 | for index, subnet in enumerate(subnets): |
12 | 404 | ipv4_subnet_mtu = None | 405 | ipv4_subnet_mtu = None |
13 | @@ -415,9 +416,18 @@ class Renderer(renderer.Renderer): | |||
14 | 415 | subnet['type'] == 'ipv6_dhcpv6-stateful'): | 416 | subnet['type'] == 'ipv6_dhcpv6-stateful'): |
15 | 416 | # Configure network settings using DHCP or DHCPv6 | 417 | # Configure network settings using DHCP or DHCPv6 |
16 | 417 | iface['mode'] = 'dhcp' | 418 | iface['mode'] = 'dhcp' |
17 | 419 | if accept_ra is not None: | ||
18 | 420 | # Accept router advertisements (0=off, 1=on) | ||
19 | 421 | iface['accept_ra'] = ('1' if util.is_true(accept_ra) | ||
20 | 422 | else '0') | ||
21 | 418 | elif subnet['type'] == 'ipv6_dhcpv6-stateless': | 423 | elif subnet['type'] == 'ipv6_dhcpv6-stateless': |
22 | 419 | # Configure network settings using SLAAC from RAs | 424 | # Configure network settings using SLAAC from RAs |
23 | 420 | iface['mode'] = 'auto' | 425 | iface['mode'] = 'auto' |
24 | 426 | elif subnet_is_ipv6(subnet) and subnet['type'] == 'static': | ||
25 | 427 | if accept_ra is not None: | ||
26 | 428 | # Accept router advertisements (0=off, 1=on) | ||
27 | 429 | iface['accept_ra'] = ('1' if util.is_true(accept_ra) | ||
28 | 430 | else '0') | ||
29 | 421 | 431 | ||
30 | 422 | # do not emit multiple 'auto $IFACE' lines as older (precise) | 432 | # do not emit multiple 'auto $IFACE' lines as older (precise) |
31 | 423 | # ifupdown complains | 433 | # ifupdown complains |
32 | diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py | |||
33 | index e54a34e..78023f8 100644 | |||
34 | --- a/cloudinit/net/netplan.py | |||
35 | +++ b/cloudinit/net/netplan.py | |||
36 | @@ -51,7 +51,8 @@ def _extract_addresses(config, entry, ifname): | |||
37 | 51 | 'mtu': 1480, | 51 | 'mtu': 1480, |
38 | 52 | 'netmask': 64, | 52 | 'netmask': 64, |
39 | 53 | 'type': 'static'}], | 53 | 'type': 'static'}], |
41 | 54 | 'type: physical' | 54 | 'type: physical', |
42 | 55 | 'accept-ra': 'true' | ||
43 | 55 | } | 56 | } |
44 | 56 | 57 | ||
45 | 57 | An entry dictionary looks like: | 58 | An entry dictionary looks like: |
46 | @@ -144,6 +145,8 @@ def _extract_addresses(config, entry, ifname): | |||
47 | 144 | ns = entry.get('nameservers', {}) | 145 | ns = entry.get('nameservers', {}) |
48 | 145 | ns.update({'search': searchdomains}) | 146 | ns.update({'search': searchdomains}) |
49 | 146 | entry.update({'nameservers': ns}) | 147 | entry.update({'nameservers': ns}) |
50 | 148 | if 'accept-ra' in config: | ||
51 | 149 | entry.update({'accept-ra': config.get('accept-ra')}) | ||
52 | 147 | 150 | ||
53 | 148 | 151 | ||
54 | 149 | def _extract_bond_slaves_by_name(interfaces, entry, bond_master): | 152 | def _extract_bond_slaves_by_name(interfaces, entry, bond_master): |
55 | diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py | |||
56 | index c0c415d..cc8beaa 100644 | |||
57 | --- a/cloudinit/net/network_state.py | |||
58 | +++ b/cloudinit/net/network_state.py | |||
59 | @@ -22,7 +22,8 @@ NETWORK_STATE_REQUIRED_KEYS = { | |||
60 | 22 | } | 22 | } |
61 | 23 | NETWORK_V2_KEY_FILTER = [ | 23 | NETWORK_V2_KEY_FILTER = [ |
62 | 24 | 'addresses', 'dhcp4', 'dhcp6', 'gateway4', 'gateway6', 'interfaces', | 24 | 'addresses', 'dhcp4', 'dhcp6', 'gateway4', 'gateway6', 'interfaces', |
64 | 25 | 'match', 'mtu', 'nameservers', 'renderer', 'set-name', 'wakeonlan' | 25 | 'match', 'mtu', 'nameservers', 'renderer', 'set-name', 'wakeonlan', |
65 | 26 | 'accept-ra' | ||
66 | 26 | ] | 27 | ] |
67 | 27 | 28 | ||
68 | 28 | NET_CONFIG_TO_V2 = { | 29 | NET_CONFIG_TO_V2 = { |
69 | @@ -340,7 +341,8 @@ class NetworkStateInterpreter(object): | |||
70 | 340 | 'name': 'eth0', | 341 | 'name': 'eth0', |
71 | 341 | 'subnets': [ | 342 | 'subnets': [ |
72 | 342 | {'type': 'dhcp4'} | 343 | {'type': 'dhcp4'} |
74 | 343 | ] | 344 | ], |
75 | 345 | 'accept-ra': 'true' | ||
76 | 344 | } | 346 | } |
77 | 345 | ''' | 347 | ''' |
78 | 346 | 348 | ||
79 | @@ -370,6 +372,7 @@ class NetworkStateInterpreter(object): | |||
80 | 370 | 'address': None, | 372 | 'address': None, |
81 | 371 | 'gateway': None, | 373 | 'gateway': None, |
82 | 372 | 'subnets': subnets, | 374 | 'subnets': subnets, |
83 | 375 | 'accept-ra': command.get('accept-ra') | ||
84 | 373 | }) | 376 | }) |
85 | 374 | self._network_state['interfaces'].update({command.get('name'): iface}) | 377 | self._network_state['interfaces'].update({command.get('name'): iface}) |
86 | 375 | self.dump_network_state() | 378 | self.dump_network_state() |
87 | @@ -613,6 +616,7 @@ class NetworkStateInterpreter(object): | |||
88 | 613 | driver: ixgbe | 616 | driver: ixgbe |
89 | 614 | set-name: lom1 | 617 | set-name: lom1 |
90 | 615 | dhcp6: true | 618 | dhcp6: true |
91 | 619 | accept-ra: true | ||
92 | 616 | switchports: | 620 | switchports: |
93 | 617 | match: | 621 | match: |
94 | 618 | name: enp2* | 622 | name: enp2* |
95 | @@ -641,7 +645,7 @@ class NetworkStateInterpreter(object): | |||
96 | 641 | driver = match.get('driver', None) | 645 | driver = match.get('driver', None) |
97 | 642 | if driver: | 646 | if driver: |
98 | 643 | phy_cmd['params'] = {'driver': driver} | 647 | phy_cmd['params'] = {'driver': driver} |
100 | 644 | for key in ['mtu', 'match', 'wakeonlan']: | 648 | for key in ['mtu', 'match', 'wakeonlan', 'accept-ra']: |
101 | 645 | if key in cfg: | 649 | if key in cfg: |
102 | 646 | phy_cmd[key] = cfg[key] | 650 | phy_cmd[key] = cfg[key] |
103 | 647 | 651 | ||
104 | @@ -919,7 +923,7 @@ def is_ipv6_addr(address): | |||
105 | 919 | def subnet_is_ipv6(subnet): | 923 | def subnet_is_ipv6(subnet): |
106 | 920 | """Common helper for checking network_state subnets for ipv6.""" | 924 | """Common helper for checking network_state subnets for ipv6.""" |
107 | 921 | # 'static6' or 'dhcp6' | 925 | # 'static6' or 'dhcp6' |
109 | 922 | if subnet['type'].endswith('6'): | 926 | if subnet['type'].endswith('6') or subnet['type'].startswith('ipv6'): |
110 | 923 | # This is a request for DHCPv6. | 927 | # This is a request for DHCPv6. |
111 | 924 | return True | 928 | return True |
112 | 925 | elif subnet['type'] == 'static' and is_ipv6_addr(subnet.get('address')): | 929 | elif subnet['type'] == 'static' and is_ipv6_addr(subnet.get('address')): |
113 | diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py | |||
114 | index 4e65676..3fe5793 100644 | |||
115 | --- a/cloudinit/net/sysconfig.py | |||
116 | +++ b/cloudinit/net/sysconfig.py | |||
117 | @@ -333,6 +333,9 @@ class Renderer(renderer.Renderer): | |||
118 | 333 | if old_key == 'mac_address' and iface['type'] != 'physical': | 333 | if old_key == 'mac_address' and iface['type'] != 'physical': |
119 | 334 | continue | 334 | continue |
120 | 335 | iface_cfg[new_key] = old_value | 335 | iface_cfg[new_key] = old_value |
121 | 336 | accept_ra = iface.get('accept-ra', None) | ||
122 | 337 | if accept_ra is not None: | ||
123 | 338 | iface_cfg['IPV6_FORCE_ACCEPT_RA'] = util.is_true(accept_ra) | ||
124 | 336 | 339 | ||
125 | 337 | @classmethod | 340 | @classmethod |
126 | 338 | def _render_subnets(cls, iface_cfg, subnets, has_default_route): | 341 | def _render_subnets(cls, iface_cfg, subnets, has_default_route): |
127 | @@ -343,11 +346,16 @@ class Renderer(renderer.Renderer): | |||
128 | 343 | for i, subnet in enumerate(subnets, start=len(iface_cfg.children)): | 346 | for i, subnet in enumerate(subnets, start=len(iface_cfg.children)): |
129 | 344 | mtu_key = 'MTU' | 347 | mtu_key = 'MTU' |
130 | 345 | subnet_type = subnet.get('type') | 348 | subnet_type = subnet.get('type') |
132 | 346 | if subnet_type == 'dhcp6' or subnet_type == 'ipv6_dhcpv6-stateful': | 349 | if subnet_type == 'dhcp6': |
133 | 347 | # TODO need to set BOOTPROTO to dhcp6 on SUSE | 350 | # TODO need to set BOOTPROTO to dhcp6 on SUSE |
134 | 348 | iface_cfg['IPV6INIT'] = True | 351 | iface_cfg['IPV6INIT'] = True |
135 | 349 | # Configure network settings using DHCPv6 | 352 | # Configure network settings using DHCPv6 |
136 | 350 | iface_cfg['DHCPV6C'] = True | 353 | iface_cfg['DHCPV6C'] = True |
137 | 354 | elif subnet_type == 'ipv6_dhcpv6-stateful': | ||
138 | 355 | iface_cfg['IPV6INIT'] = True | ||
139 | 356 | # Configure network settings using DHCPv6 | ||
140 | 357 | iface_cfg['DHCPV6C'] = True | ||
141 | 358 | iface_cfg['IPV6_FORCE_ACCEPT_RA'] = True | ||
142 | 351 | elif subnet_type == 'ipv6_dhcpv6-stateless': | 359 | elif subnet_type == 'ipv6_dhcpv6-stateless': |
143 | 352 | iface_cfg['IPV6INIT'] = True | 360 | iface_cfg['IPV6INIT'] = True |
144 | 353 | # Configure network settings using SLAAC from RAs | 361 | # Configure network settings using SLAAC from RAs |
145 | diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py | |||
146 | index f5a9cae..62b6ac6 100644 | |||
147 | --- a/tests/unittests/test_net.py | |||
148 | +++ b/tests/unittests/test_net.py | |||
149 | @@ -1070,6 +1070,104 @@ NETWORK_CONFIGS = { | |||
150 | 1070 | """), | 1070 | """), |
151 | 1071 | }, | 1071 | }, |
152 | 1072 | }, | 1072 | }, |
153 | 1073 | 'dhcpv6_accept_ra': { | ||
154 | 1074 | 'expected_eni': textwrap.dedent("""\ | ||
155 | 1075 | auto lo | ||
156 | 1076 | iface lo inet loopback | ||
157 | 1077 | |||
158 | 1078 | auto iface0 | ||
159 | 1079 | iface iface0 inet6 dhcp | ||
160 | 1080 | accept_ra 1 | ||
161 | 1081 | """).rstrip(' '), | ||
162 | 1082 | 'expected_netplan': textwrap.dedent(""" | ||
163 | 1083 | network: | ||
164 | 1084 | version: 2 | ||
165 | 1085 | ethernets: | ||
166 | 1086 | iface0: | ||
167 | 1087 | accept-ra: 'true' | ||
168 | 1088 | dhcp6: true | ||
169 | 1089 | """).rstrip(' '), | ||
170 | 1090 | 'yaml_v1': textwrap.dedent("""\ | ||
171 | 1091 | version: 1 | ||
172 | 1092 | config: | ||
173 | 1093 | - type: 'physical' | ||
174 | 1094 | name: 'iface0' | ||
175 | 1095 | subnets: | ||
176 | 1096 | - {'type': 'dhcp6'} | ||
177 | 1097 | accept-ra: 'true' | ||
178 | 1098 | """).rstrip(' '), | ||
179 | 1099 | 'yaml_v2': textwrap.dedent("""\ | ||
180 | 1100 | version: 2 | ||
181 | 1101 | ethernets: | ||
182 | 1102 | iface0: | ||
183 | 1103 | dhcp6: true | ||
184 | 1104 | accept-ra: 'true' | ||
185 | 1105 | """).rstrip(' '), | ||
186 | 1106 | 'expected_sysconfig': { | ||
187 | 1107 | 'ifcfg-iface0': textwrap.dedent("""\ | ||
188 | 1108 | BOOTPROTO=none | ||
189 | 1109 | DEVICE=iface0 | ||
190 | 1110 | DHCPV6C=yes | ||
191 | 1111 | IPV6INIT=yes | ||
192 | 1112 | IPV6_FORCE_ACCEPT_RA=yes | ||
193 | 1113 | DEVICE=iface0 | ||
194 | 1114 | NM_CONTROLLED=no | ||
195 | 1115 | ONBOOT=yes | ||
196 | 1116 | STARTMODE=auto | ||
197 | 1117 | TYPE=Ethernet | ||
198 | 1118 | USERCTL=no | ||
199 | 1119 | """), | ||
200 | 1120 | }, | ||
201 | 1121 | }, | ||
202 | 1122 | 'dhcpv6_reject_ra': { | ||
203 | 1123 | 'expected_eni': textwrap.dedent("""\ | ||
204 | 1124 | auto lo | ||
205 | 1125 | iface lo inet loopback | ||
206 | 1126 | |||
207 | 1127 | auto iface0 | ||
208 | 1128 | iface iface0 inet6 dhcp | ||
209 | 1129 | accept_ra 0 | ||
210 | 1130 | """).rstrip(' '), | ||
211 | 1131 | 'expected_netplan': textwrap.dedent(""" | ||
212 | 1132 | network: | ||
213 | 1133 | version: 2 | ||
214 | 1134 | ethernets: | ||
215 | 1135 | iface0: | ||
216 | 1136 | accept-ra: 'false' | ||
217 | 1137 | dhcp6: true | ||
218 | 1138 | """).rstrip(' '), | ||
219 | 1139 | 'yaml_v1': textwrap.dedent("""\ | ||
220 | 1140 | version: 1 | ||
221 | 1141 | config: | ||
222 | 1142 | - type: 'physical' | ||
223 | 1143 | name: 'iface0' | ||
224 | 1144 | subnets: | ||
225 | 1145 | - {'type': 'dhcp6'} | ||
226 | 1146 | accept-ra: 'false' | ||
227 | 1147 | """).rstrip(' '), | ||
228 | 1148 | 'yaml_v2': textwrap.dedent("""\ | ||
229 | 1149 | version: 2 | ||
230 | 1150 | ethernets: | ||
231 | 1151 | iface0: | ||
232 | 1152 | dhcp6: true | ||
233 | 1153 | accept-ra: 'false' | ||
234 | 1154 | """).rstrip(' '), | ||
235 | 1155 | 'expected_sysconfig': { | ||
236 | 1156 | 'ifcfg-iface0': textwrap.dedent("""\ | ||
237 | 1157 | BOOTPROTO=none | ||
238 | 1158 | DEVICE=iface0 | ||
239 | 1159 | DHCPV6C=yes | ||
240 | 1160 | IPV6INIT=yes | ||
241 | 1161 | IPV6_FORCE_ACCEPT_RA=no | ||
242 | 1162 | DEVICE=iface0 | ||
243 | 1163 | NM_CONTROLLED=no | ||
244 | 1164 | ONBOOT=yes | ||
245 | 1165 | STARTMODE=auto | ||
246 | 1166 | TYPE=Ethernet | ||
247 | 1167 | USERCTL=no | ||
248 | 1168 | """), | ||
249 | 1169 | }, | ||
250 | 1170 | }, | ||
251 | 1073 | 'dhcpv6_stateless': { | 1171 | 'dhcpv6_stateless': { |
252 | 1074 | 'expected_eni': textwrap.dedent("""\ | 1172 | 'expected_eni': textwrap.dedent("""\ |
253 | 1075 | auto lo | 1173 | auto lo |
254 | @@ -1137,6 +1235,7 @@ NETWORK_CONFIGS = { | |||
255 | 1137 | DEVICE=iface0 | 1235 | DEVICE=iface0 |
256 | 1138 | DHCPV6C=yes | 1236 | DHCPV6C=yes |
257 | 1139 | IPV6INIT=yes | 1237 | IPV6INIT=yes |
258 | 1238 | IPV6_FORCE_ACCEPT_RA=yes | ||
259 | 1140 | DEVICE=iface0 | 1239 | DEVICE=iface0 |
260 | 1141 | NM_CONTROLLED=no | 1240 | NM_CONTROLLED=no |
261 | 1142 | ONBOOT=yes | 1241 | ONBOOT=yes |
262 | @@ -2857,6 +2956,34 @@ USERCTL=no | |||
263 | 2857 | self._compare_files_to_expected(entry[self.expected_name], found) | 2956 | self._compare_files_to_expected(entry[self.expected_name], found) |
264 | 2858 | self._assert_headers(found) | 2957 | self._assert_headers(found) |
265 | 2859 | 2958 | ||
266 | 2959 | def test_dhcpv6_accept_ra_config_v1(self): | ||
267 | 2960 | entry = NETWORK_CONFIGS['dhcpv6_accept_ra'] | ||
268 | 2961 | found = self._render_and_read(network_config=yaml.load( | ||
269 | 2962 | entry['yaml_v1'])) | ||
270 | 2963 | self._compare_files_to_expected(entry[self.expected_name], found) | ||
271 | 2964 | self._assert_headers(found) | ||
272 | 2965 | |||
273 | 2966 | def test_dhcpv6_accept_ra_config_v2(self): | ||
274 | 2967 | entry = NETWORK_CONFIGS['dhcpv6_accept_ra'] | ||
275 | 2968 | found = self._render_and_read(network_config=yaml.load( | ||
276 | 2969 | entry['yaml_v2'])) | ||
277 | 2970 | self._compare_files_to_expected(entry[self.expected_name], found) | ||
278 | 2971 | self._assert_headers(found) | ||
279 | 2972 | |||
280 | 2973 | def test_dhcpv6_reject_ra_config_v1(self): | ||
281 | 2974 | entry = NETWORK_CONFIGS['dhcpv6_reject_ra'] | ||
282 | 2975 | found = self._render_and_read(network_config=yaml.load( | ||
283 | 2976 | entry['yaml_v1'])) | ||
284 | 2977 | self._compare_files_to_expected(entry[self.expected_name], found) | ||
285 | 2978 | self._assert_headers(found) | ||
286 | 2979 | |||
287 | 2980 | def test_dhcpv6_reject_ra_config_v2(self): | ||
288 | 2981 | entry = NETWORK_CONFIGS['dhcpv6_reject_ra'] | ||
289 | 2982 | found = self._render_and_read(network_config=yaml.load( | ||
290 | 2983 | entry['yaml_v2'])) | ||
291 | 2984 | self._compare_files_to_expected(entry[self.expected_name], found) | ||
292 | 2985 | self._assert_headers(found) | ||
293 | 2986 | |||
294 | 2860 | def test_dhcpv6_stateless_config(self): | 2987 | def test_dhcpv6_stateless_config(self): |
295 | 2861 | entry = NETWORK_CONFIGS['dhcpv6_stateless'] | 2988 | entry = NETWORK_CONFIGS['dhcpv6_stateless'] |
296 | 2862 | found = self._render_and_read(network_config=yaml.load(entry['yaml'])) | 2989 | found = self._render_and_read(network_config=yaml.load(entry['yaml'])) |
297 | @@ -3918,6 +4045,22 @@ class TestNetplanRoundTrip(CiTestCase): | |||
298 | 3918 | entry['expected_netplan'].splitlines(), | 4045 | entry['expected_netplan'].splitlines(), |
299 | 3919 | files['/etc/netplan/50-cloud-init.yaml'].splitlines()) | 4046 | files['/etc/netplan/50-cloud-init.yaml'].splitlines()) |
300 | 3920 | 4047 | ||
301 | 4048 | def testsimple_render_dhcpv6_accept_ra(self): | ||
302 | 4049 | entry = NETWORK_CONFIGS['dhcpv6_accept_ra'] | ||
303 | 4050 | files = self._render_and_read(network_config=yaml.load( | ||
304 | 4051 | entry['yaml_v1'])) | ||
305 | 4052 | self.assertEqual( | ||
306 | 4053 | entry['expected_netplan'].splitlines(), | ||
307 | 4054 | files['/etc/netplan/50-cloud-init.yaml'].splitlines()) | ||
308 | 4055 | |||
309 | 4056 | def testsimple_render_dhcpv6_reject_ra(self): | ||
310 | 4057 | entry = NETWORK_CONFIGS['dhcpv6_reject_ra'] | ||
311 | 4058 | files = self._render_and_read(network_config=yaml.load( | ||
312 | 4059 | entry['yaml_v1'])) | ||
313 | 4060 | self.assertEqual( | ||
314 | 4061 | entry['expected_netplan'].splitlines(), | ||
315 | 4062 | files['/etc/netplan/50-cloud-init.yaml'].splitlines()) | ||
316 | 4063 | |||
317 | 3921 | def testsimple_render_all(self): | 4064 | def testsimple_render_all(self): |
318 | 3922 | entry = NETWORK_CONFIGS['all'] | 4065 | entry = NETWORK_CONFIGS['all'] |
319 | 3923 | files = self._render_and_read(network_config=yaml.load(entry['yaml'])) | 4066 | files = self._render_and_read(network_config=yaml.load(entry['yaml'])) |
320 | @@ -4048,6 +4191,38 @@ class TestEniRoundTrip(CiTestCase): | |||
321 | 4048 | entry['expected_eni'].splitlines(), | 4191 | entry['expected_eni'].splitlines(), |
322 | 4049 | files['/etc/network/interfaces'].splitlines()) | 4192 | files['/etc/network/interfaces'].splitlines()) |
323 | 4050 | 4193 | ||
324 | 4194 | def testsimple_render_dhcpv6_stateless(self): | ||
325 | 4195 | entry = NETWORK_CONFIGS['dhcpv6_stateless'] | ||
326 | 4196 | files = self._render_and_read(network_config=yaml.load( | ||
327 | 4197 | entry['yaml'])) | ||
328 | 4198 | self.assertEqual( | ||
329 | 4199 | entry['expected_eni'].splitlines(), | ||
330 | 4200 | files['/etc/network/interfaces'].splitlines()) | ||
331 | 4201 | |||
332 | 4202 | def testsimple_render_dhcpv6_stateful(self): | ||
333 | 4203 | entry = NETWORK_CONFIGS['dhcpv6_stateless'] | ||
334 | 4204 | files = self._render_and_read(network_config=yaml.load( | ||
335 | 4205 | entry['yaml'])) | ||
336 | 4206 | self.assertEqual( | ||
337 | 4207 | entry['expected_eni'].splitlines(), | ||
338 | 4208 | files['/etc/network/interfaces'].splitlines()) | ||
339 | 4209 | |||
340 | 4210 | def testsimple_render_dhcpv6_accept_ra(self): | ||
341 | 4211 | entry = NETWORK_CONFIGS['dhcpv6_accept_ra'] | ||
342 | 4212 | files = self._render_and_read(network_config=yaml.load( | ||
343 | 4213 | entry['yaml_v1'])) | ||
344 | 4214 | self.assertEqual( | ||
345 | 4215 | entry['expected_eni'].splitlines(), | ||
346 | 4216 | files['/etc/network/interfaces'].splitlines()) | ||
347 | 4217 | |||
348 | 4218 | def testsimple_render_dhcpv6_reject_ra(self): | ||
349 | 4219 | entry = NETWORK_CONFIGS['dhcpv6_reject_ra'] | ||
350 | 4220 | files = self._render_and_read(network_config=yaml.load( | ||
351 | 4221 | entry['yaml_v1'])) | ||
352 | 4222 | self.assertEqual( | ||
353 | 4223 | entry['expected_eni'].splitlines(), | ||
354 | 4224 | files['/etc/network/interfaces'].splitlines()) | ||
355 | 4225 | |||
356 | 4051 | def testsimple_render_manual(self): | 4226 | def testsimple_render_manual(self): |
357 | 4052 | """Test rendering of 'manual' for 'type' and 'control'. | 4227 | """Test rendering of 'manual' for 'type' and 'control'. |
358 | 4053 | 4228 |
Thanks for sorting this out. One more element here is related to kernel defaults; at least on Ubuntu the kernel defaults the interfaces accept_ra value to 1; which allows RAs on interfaces that aren't forwarding.
The code below which defaults accept-ra to False is going to break that. I would like to suggest that only *if* accept-ra is set in the source config (for example in the openstack network_data.json where dhcp6 stateful is specified, then it makes sense to include accept-ra: true in the interface config.
In the renderers, if accept-ra is *not* defined, I'd like to avoid emitting any RA related config at all.
Now that cloud-init handles dhcp6-stateless /stateful; I think we only want to enable RA on stateful dhcp6, otherwise we should leave the kernel defaults.
Additionally, ifupdown (eni) can toggle these values as well:
accept_ra [0, 1, 2]
autoconf [0, 1]