Merge ~akaris/cloud-init:bug1679817-c into cloud-init:master
- Git
- lp:~akaris/cloud-init
- bug1679817-c
- Merge into master
Status: | Merged | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Merge reported by: | Scott Moser | ||||||||||||
Merged at revision: | 1a401c57978855b5d56ac23976252e2506d206fa | ||||||||||||
Proposed branch: | ~akaris/cloud-init:bug1679817-c | ||||||||||||
Merge into: | cloud-init:master | ||||||||||||
Diff against target: |
453 lines (+194/-132) 3 files modified
cloudinit/net/sysconfig.py (+169/-70) tests/unittests/test_distros/test_netconfig.py (+3/-5) tests/unittests/test_net.py (+22/-57) |
||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Server Team CI bot | continuous-integration | Approve | |
cloud-init Commiters | Pending | ||
Review via email: mp+324195@code.launchpad.net |
This proposal has been superseded by a proposal from 2017-05-17.
Commit message
Fix dual stack IPv4/IPv6 configuration for RHEL
Dual stack IPv4/IPv6 configuration via config drive is broken for RHEL7.
This patch fixes several scenarios for IPv4/IPv6/dual stack with multiple IP assignment
Removes unpopular IPv4 alias files and invalid IPv6 alias files
Also fixes associated unit tests
Description of the change
Andreas Karis (akaris) wrote : | # |
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:1a401c57978
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:48bf21bcb60
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Scott Moser (smoser) wrote : | # |
Hi,
I'm marking this as 'merged' based on the fact that the new merge proposal *is* merged.
(https:/
Please move back to 'Needs Review' (and explain) if you think otherwise.
Preview Diff
1 | diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py | |||
2 | index d981277..d04ae8f 100644 | |||
3 | --- a/cloudinit/net/sysconfig.py | |||
4 | +++ b/cloudinit/net/sysconfig.py | |||
5 | @@ -59,6 +59,9 @@ class ConfigMap(object): | |||
6 | 59 | def __setitem__(self, key, value): | 59 | def __setitem__(self, key, value): |
7 | 60 | self._conf[key] = value | 60 | self._conf[key] = value |
8 | 61 | 61 | ||
9 | 62 | def __getitem__(self, key): | ||
10 | 63 | return self._conf[key] | ||
11 | 64 | |||
12 | 62 | def drop(self, key): | 65 | def drop(self, key): |
13 | 63 | self._conf.pop(key, None) | 66 | self._conf.pop(key, None) |
14 | 64 | 67 | ||
15 | @@ -83,7 +86,8 @@ class ConfigMap(object): | |||
16 | 83 | class Route(ConfigMap): | 86 | class Route(ConfigMap): |
17 | 84 | """Represents a route configuration.""" | 87 | """Represents a route configuration.""" |
18 | 85 | 88 | ||
20 | 86 | route_fn_tpl = '%(base)s/network-scripts/route-%(name)s' | 89 | route_fn_tpl_ipv4 = '%(base)s/network-scripts/route-%(name)s' |
21 | 90 | route_fn_tpl_ipv6 = '%(base)s/network-scripts/route6-%(name)s' | ||
22 | 87 | 91 | ||
23 | 88 | def __init__(self, route_name, base_sysconf_dir): | 92 | def __init__(self, route_name, base_sysconf_dir): |
24 | 89 | super(Route, self).__init__() | 93 | super(Route, self).__init__() |
25 | @@ -102,9 +106,53 @@ class Route(ConfigMap): | |||
26 | 102 | return r | 106 | return r |
27 | 103 | 107 | ||
28 | 104 | @property | 108 | @property |
32 | 105 | def path(self): | 109 | def path_ipv4(self): |
33 | 106 | return self.route_fn_tpl % ({'base': self._base_sysconf_dir, | 110 | return self.route_fn_tpl_ipv4 % ({'base': self._base_sysconf_dir, |
34 | 107 | 'name': self._route_name}) | 111 | 'name': self._route_name}) |
35 | 112 | |||
36 | 113 | @property | ||
37 | 114 | def path_ipv6(self): | ||
38 | 115 | return self.route_fn_tpl_ipv6 % ({'base': self._base_sysconf_dir, | ||
39 | 116 | 'name': self._route_name}) | ||
40 | 117 | |||
41 | 118 | def is_ipv6_route(self, address): | ||
42 | 119 | return ':' in address | ||
43 | 120 | |||
44 | 121 | def to_string(self, proto="ipv4"): | ||
45 | 122 | buf = six.StringIO() | ||
46 | 123 | buf.write(_make_header()) | ||
47 | 124 | if self._conf: | ||
48 | 125 | buf.write("\n") | ||
49 | 126 | # need to reindex IPv4 addresses | ||
50 | 127 | # (because Route can contain a mix of IPv4 and IPv6) | ||
51 | 128 | reindex = -1 | ||
52 | 129 | for key in sorted(self._conf.keys()): | ||
53 | 130 | if 'ADDRESS' in key: | ||
54 | 131 | index = key.replace('ADDRESS', '') | ||
55 | 132 | address_value = str(self._conf[key]) | ||
56 | 133 | # only accept combinations: | ||
57 | 134 | # ipv6 route and proto ipv6 | ||
58 | 135 | # ipv4 route and proto ipv4 | ||
59 | 136 | # do not add any other routes | ||
60 | 137 | if proto == "ipv4" and not self.is_ipv6_route(address_value): | ||
61 | 138 | netmask_value = str(self._conf['NETMASK' + index]) | ||
62 | 139 | gateway_value = str(self._conf['GATEWAY' + index]) | ||
63 | 140 | # increase IPv4 index | ||
64 | 141 | reindex = reindex + 1 | ||
65 | 142 | buf.write("%s=%s\n" % ('ADDRESS' + str(reindex), | ||
66 | 143 | _quote_value(address_value))) | ||
67 | 144 | buf.write("%s=%s\n" % ('GATEWAY' + str(reindex), | ||
68 | 145 | _quote_value(gateway_value))) | ||
69 | 146 | buf.write("%s=%s\n" % ('NETMASK' + str(reindex), | ||
70 | 147 | _quote_value(netmask_value))) | ||
71 | 148 | elif proto == "ipv6" and self.is_ipv6_route(address_value): | ||
72 | 149 | netmask_value = str(self._conf['NETMASK' + index]) | ||
73 | 150 | gateway_value = str(self._conf['GATEWAY' + index]) | ||
74 | 151 | buf.write("%s/%s via %s\n" % (address_value, | ||
75 | 152 | netmask_value, | ||
76 | 153 | gateway_value)) | ||
77 | 154 | |||
78 | 155 | return buf.getvalue() | ||
79 | 108 | 156 | ||
80 | 109 | 157 | ||
81 | 110 | class NetInterface(ConfigMap): | 158 | class NetInterface(ConfigMap): |
82 | @@ -211,65 +259,119 @@ class Renderer(renderer.Renderer): | |||
83 | 211 | iface_cfg[new_key] = old_value | 259 | iface_cfg[new_key] = old_value |
84 | 212 | 260 | ||
85 | 213 | @classmethod | 261 | @classmethod |
98 | 214 | def _render_subnet(cls, iface_cfg, route_cfg, subnet): | 262 | def _render_subnets(cls, iface_cfg, subnets): |
99 | 215 | subnet_type = subnet.get('type') | 263 | # setting base values |
100 | 216 | if subnet_type == 'dhcp6': | 264 | iface_cfg['BOOTPROTO'] = 'none' |
101 | 217 | iface_cfg['DHCPV6C'] = True | 265 | |
102 | 218 | iface_cfg['IPV6INIT'] = True | 266 | # modifying base values according to subnets |
103 | 219 | iface_cfg['BOOTPROTO'] = 'dhcp' | 267 | for i, subnet in enumerate(subnets, start=len(iface_cfg.children)): |
104 | 220 | elif subnet_type in ['dhcp4', 'dhcp']: | 268 | subnet_type = subnet.get('type') |
105 | 221 | iface_cfg['BOOTPROTO'] = 'dhcp' | 269 | if subnet_type == 'dhcp6': |
94 | 222 | elif subnet_type == 'static': | ||
95 | 223 | iface_cfg['BOOTPROTO'] = 'static' | ||
96 | 224 | if subnet_is_ipv6(subnet): | ||
97 | 225 | iface_cfg['IPV6ADDR'] = subnet['address'] | ||
106 | 226 | iface_cfg['IPV6INIT'] = True | 270 | iface_cfg['IPV6INIT'] = True |
107 | 271 | iface_cfg['DHCPV6C'] = True | ||
108 | 272 | iface_cfg['BOOTPROTO'] = 'dhcp' | ||
109 | 273 | elif subnet_type in ['dhcp4', 'dhcp']: | ||
110 | 274 | iface_cfg['BOOTPROTO'] = 'dhcp' | ||
111 | 275 | elif subnet_type == 'static': | ||
112 | 276 | # grep BOOTPROTO sysconfig.txt -A2 | head -3 | ||
113 | 277 | # BOOTPROTO=none|bootp|dhcp | ||
114 | 278 | # 'bootp' or 'dhcp' cause a DHCP client | ||
115 | 279 | # to run on the device. Any other | ||
116 | 280 | # value causes any static configuration | ||
117 | 281 | # in the file to be applied. | ||
118 | 282 | # ==> the following should not be set to 'static' | ||
119 | 283 | # but should remain 'none' | ||
120 | 284 | # if iface_cfg['BOOTPROTO'] == 'none': | ||
121 | 285 | # iface_cfg['BOOTPROTO'] = 'static' | ||
122 | 286 | if subnet_is_ipv6(subnet): | ||
123 | 287 | iface_cfg['IPV6INIT'] = True | ||
124 | 227 | else: | 288 | else: |
157 | 228 | iface_cfg['IPADDR'] = subnet['address'] | 289 | raise ValueError("Unknown subnet type '%s' found" |
158 | 229 | else: | 290 | " for interface '%s'" % (subnet_type, |
159 | 230 | raise ValueError("Unknown subnet type '%s' found" | 291 | iface_cfg.name)) |
160 | 231 | " for interface '%s'" % (subnet_type, | 292 | |
161 | 232 | iface_cfg.name)) | 293 | # set IPv4 and IPv6 static addresses |
162 | 233 | if 'netmask' in subnet: | 294 | ipv4_index = -1 |
163 | 234 | iface_cfg['NETMASK'] = subnet['netmask'] | 295 | ipv6_index = -1 |
164 | 235 | is_ipv6 = subnet.get('ipv6') | 296 | for i, subnet in enumerate(subnets, start=len(iface_cfg.children)): |
165 | 236 | for route in subnet.get('routes', []): | 297 | subnet_type = subnet.get('type') |
166 | 237 | if _is_default_route(route): | 298 | if subnet_type == 'dhcp6': |
167 | 238 | if ( | 299 | continue |
168 | 239 | (subnet.get('ipv4') and | 300 | elif subnet_type in ['dhcp4', 'dhcp']: |
169 | 240 | route_cfg.has_set_default_ipv4) or | 301 | continue |
170 | 241 | (subnet.get('ipv6') and | 302 | elif subnet_type == 'static': |
171 | 242 | route_cfg.has_set_default_ipv6) | 303 | if subnet_is_ipv6(subnet): |
172 | 243 | ): | 304 | ipv6_index = ipv6_index + 1 |
173 | 244 | raise ValueError("Duplicate declaration of default " | 305 | if 'netmask' in subnet and str(subnet['netmask']) != "": |
174 | 245 | "route found for interface '%s'" | 306 | ipv6_cidr = (subnet['address'] + |
175 | 246 | % (iface_cfg.name)) | 307 | '/' + |
176 | 247 | # NOTE(harlowja): ipv6 and ipv4 default gateways | 308 | str(subnet['netmask'])) |
145 | 248 | gw_key = 'GATEWAY0' | ||
146 | 249 | nm_key = 'NETMASK0' | ||
147 | 250 | addr_key = 'ADDRESS0' | ||
148 | 251 | # The owning interface provides the default route. | ||
149 | 252 | # | ||
150 | 253 | # TODO(harlowja): add validation that no other iface has | ||
151 | 254 | # also provided the default route? | ||
152 | 255 | iface_cfg['DEFROUTE'] = True | ||
153 | 256 | if 'gateway' in route: | ||
154 | 257 | if is_ipv6: | ||
155 | 258 | iface_cfg['IPV6_DEFAULTGW'] = route['gateway'] | ||
156 | 259 | route_cfg.has_set_default_ipv6 = True | ||
177 | 260 | else: | 309 | else: |
190 | 261 | iface_cfg['GATEWAY'] = route['gateway'] | 310 | ipv6_cidr = subnet['address'] |
191 | 262 | route_cfg.has_set_default_ipv4 = True | 311 | if ipv6_index == 0: |
192 | 263 | else: | 312 | iface_cfg['IPV6ADDR'] = ipv6_cidr |
193 | 264 | gw_key = 'GATEWAY%s' % route_cfg.last_idx | 313 | elif ipv6_index == 1: |
194 | 265 | nm_key = 'NETMASK%s' % route_cfg.last_idx | 314 | iface_cfg['IPV6ADDR_SECONDARIES'] = ipv6_cidr |
195 | 266 | addr_key = 'ADDRESS%s' % route_cfg.last_idx | 315 | else: |
196 | 267 | route_cfg.last_idx += 1 | 316 | iface_cfg['IPV6ADDR_SECONDARIES'] = ( |
197 | 268 | for (old_key, new_key) in [('gateway', gw_key), | 317 | iface_cfg['IPV6ADDR_SECONDARIES'] + |
198 | 269 | ('netmask', nm_key), | 318 | " " + ipv6_cidr) |
199 | 270 | ('network', addr_key)]: | 319 | else: |
200 | 271 | if old_key in route: | 320 | ipv4_index = ipv4_index + 1 |
201 | 272 | route_cfg[new_key] = route[old_key] | 321 | if ipv4_index == 0: |
202 | 322 | iface_cfg['IPADDR'] = subnet['address'] | ||
203 | 323 | if 'netmask' in subnet: | ||
204 | 324 | iface_cfg['NETMASK'] = subnet['netmask'] | ||
205 | 325 | else: | ||
206 | 326 | iface_cfg['IPADDR' + str(ipv4_index)] = \ | ||
207 | 327 | subnet['address'] | ||
208 | 328 | if 'netmask' in subnet: | ||
209 | 329 | iface_cfg['NETMASK' + str(ipv4_index)] = \ | ||
210 | 330 | subnet['netmask'] | ||
211 | 331 | |||
212 | 332 | @classmethod | ||
213 | 333 | def _render_subnet_routes(cls, iface_cfg, route_cfg, subnets): | ||
214 | 334 | for i, subnet in enumerate(subnets, start=len(iface_cfg.children)): | ||
215 | 335 | for route in subnet.get('routes', []): | ||
216 | 336 | is_ipv6 = subnet.get('ipv6') | ||
217 | 337 | |||
218 | 338 | if _is_default_route(route): | ||
219 | 339 | if ( | ||
220 | 340 | (subnet.get('ipv4') and | ||
221 | 341 | route_cfg.has_set_default_ipv4) or | ||
222 | 342 | (subnet.get('ipv6') and | ||
223 | 343 | route_cfg.has_set_default_ipv6) | ||
224 | 344 | ): | ||
225 | 345 | raise ValueError("Duplicate declaration of default " | ||
226 | 346 | "route found for interface '%s'" | ||
227 | 347 | % (iface_cfg.name)) | ||
228 | 348 | # NOTE(harlowja): ipv6 and ipv4 default gateways | ||
229 | 349 | gw_key = 'GATEWAY0' | ||
230 | 350 | nm_key = 'NETMASK0' | ||
231 | 351 | addr_key = 'ADDRESS0' | ||
232 | 352 | # The owning interface provides the default route. | ||
233 | 353 | # | ||
234 | 354 | # TODO(harlowja): add validation that no other iface has | ||
235 | 355 | # also provided the default route? | ||
236 | 356 | iface_cfg['DEFROUTE'] = True | ||
237 | 357 | if 'gateway' in route: | ||
238 | 358 | if is_ipv6: | ||
239 | 359 | iface_cfg['IPV6_DEFAULTGW'] = route['gateway'] | ||
240 | 360 | route_cfg.has_set_default_ipv6 = True | ||
241 | 361 | else: | ||
242 | 362 | iface_cfg['GATEWAY'] = route['gateway'] | ||
243 | 363 | route_cfg.has_set_default_ipv4 = True | ||
244 | 364 | |||
245 | 365 | else: | ||
246 | 366 | gw_key = 'GATEWAY%s' % route_cfg.last_idx | ||
247 | 367 | nm_key = 'NETMASK%s' % route_cfg.last_idx | ||
248 | 368 | addr_key = 'ADDRESS%s' % route_cfg.last_idx | ||
249 | 369 | route_cfg.last_idx += 1 | ||
250 | 370 | for (old_key, new_key) in [('gateway', gw_key), | ||
251 | 371 | ('netmask', nm_key), | ||
252 | 372 | ('network', addr_key)]: | ||
253 | 373 | if old_key in route: | ||
254 | 374 | route_cfg[new_key] = route[old_key] | ||
255 | 273 | 375 | ||
256 | 274 | @classmethod | 376 | @classmethod |
257 | 275 | def _render_bonding_opts(cls, iface_cfg, iface): | 377 | def _render_bonding_opts(cls, iface_cfg, iface): |
258 | @@ -295,15 +397,9 @@ class Renderer(renderer.Renderer): | |||
259 | 295 | iface_subnets = iface.get("subnets", []) | 397 | iface_subnets = iface.get("subnets", []) |
260 | 296 | iface_cfg = iface_contents[iface_name] | 398 | iface_cfg = iface_contents[iface_name] |
261 | 297 | route_cfg = iface_cfg.routes | 399 | route_cfg = iface_cfg.routes |
271 | 298 | if len(iface_subnets) == 1: | 400 | |
272 | 299 | cls._render_subnet(iface_cfg, route_cfg, iface_subnets[0]) | 401 | cls._render_subnets(iface_cfg, iface_subnets) |
273 | 300 | elif len(iface_subnets) > 1: | 402 | cls._render_subnet_routes(iface_cfg, route_cfg, iface_subnets) |
265 | 301 | for i, isubnet in enumerate(iface_subnets, | ||
266 | 302 | start=len(iface_cfg.children)): | ||
267 | 303 | iface_sub_cfg = iface_cfg.copy() | ||
268 | 304 | iface_sub_cfg.name = "%s:%s" % (iface_name, i) | ||
269 | 305 | iface_cfg.children.append(iface_sub_cfg) | ||
270 | 306 | cls._render_subnet(iface_sub_cfg, route_cfg, isubnet) | ||
274 | 307 | 403 | ||
275 | 308 | @classmethod | 404 | @classmethod |
276 | 309 | def _render_bond_interfaces(cls, network_state, iface_contents): | 405 | def _render_bond_interfaces(cls, network_state, iface_contents): |
277 | @@ -387,7 +483,10 @@ class Renderer(renderer.Renderer): | |||
278 | 387 | if iface_cfg: | 483 | if iface_cfg: |
279 | 388 | contents[iface_cfg.path] = iface_cfg.to_string() | 484 | contents[iface_cfg.path] = iface_cfg.to_string() |
280 | 389 | if iface_cfg.routes: | 485 | if iface_cfg.routes: |
282 | 390 | contents[iface_cfg.routes.path] = iface_cfg.routes.to_string() | 486 | contents[iface_cfg.routes.path_ipv4] = \ |
283 | 487 | iface_cfg.routes.to_string("ipv4") | ||
284 | 488 | contents[iface_cfg.routes.path_ipv6] = \ | ||
285 | 489 | iface_cfg.routes.to_string("ipv6") | ||
286 | 391 | return contents | 490 | return contents |
287 | 392 | 491 | ||
288 | 393 | def render_network_state(self, network_state, target=None): | 492 | def render_network_state(self, network_state, target=None): |
289 | diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py | |||
290 | index 1e10a33..fd7c051 100644 | |||
291 | --- a/tests/unittests/test_distros/test_netconfig.py | |||
292 | +++ b/tests/unittests/test_distros/test_netconfig.py | |||
293 | @@ -476,7 +476,7 @@ NETWORKING=yes | |||
294 | 476 | expected_buf = ''' | 476 | expected_buf = ''' |
295 | 477 | # Created by cloud-init on instance boot automatically, do not edit. | 477 | # Created by cloud-init on instance boot automatically, do not edit. |
296 | 478 | # | 478 | # |
298 | 479 | BOOTPROTO=static | 479 | BOOTPROTO=none |
299 | 480 | DEVICE=eth0 | 480 | DEVICE=eth0 |
300 | 481 | IPADDR=192.168.1.5 | 481 | IPADDR=192.168.1.5 |
301 | 482 | NETMASK=255.255.255.0 | 482 | NETMASK=255.255.255.0 |
302 | @@ -533,7 +533,6 @@ NETWORKING=yes | |||
303 | 533 | mock.patch.object(util, 'load_file', return_value='')) | 533 | mock.patch.object(util, 'load_file', return_value='')) |
304 | 534 | mocks.enter_context( | 534 | mocks.enter_context( |
305 | 535 | mock.patch.object(os.path, 'isfile', return_value=False)) | 535 | mock.patch.object(os.path, 'isfile', return_value=False)) |
306 | 536 | |||
307 | 537 | rh_distro.apply_network(BASE_NET_CFG_IPV6, False) | 536 | rh_distro.apply_network(BASE_NET_CFG_IPV6, False) |
308 | 538 | 537 | ||
309 | 539 | self.assertEqual(len(write_bufs), 4) | 538 | self.assertEqual(len(write_bufs), 4) |
310 | @@ -626,11 +625,10 @@ IPV6_AUTOCONF=no | |||
311 | 626 | expected_buf = ''' | 625 | expected_buf = ''' |
312 | 627 | # Created by cloud-init on instance boot automatically, do not edit. | 626 | # Created by cloud-init on instance boot automatically, do not edit. |
313 | 628 | # | 627 | # |
315 | 629 | BOOTPROTO=static | 628 | BOOTPROTO=none |
316 | 630 | DEVICE=eth0 | 629 | DEVICE=eth0 |
318 | 631 | IPV6ADDR=2607:f0d0:1002:0011::2 | 630 | IPV6ADDR=2607:f0d0:1002:0011::2/64 |
319 | 632 | IPV6INIT=yes | 631 | IPV6INIT=yes |
320 | 633 | NETMASK=64 | ||
321 | 634 | NM_CONTROLLED=no | 632 | NM_CONTROLLED=no |
322 | 635 | ONBOOT=yes | 633 | ONBOOT=yes |
323 | 636 | TYPE=Ethernet | 634 | TYPE=Ethernet |
324 | diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py | |||
325 | index d36d0e7..cf4cedc 100644 | |||
326 | --- a/tests/unittests/test_net.py | |||
327 | +++ b/tests/unittests/test_net.py | |||
328 | @@ -137,7 +137,7 @@ OS_SAMPLES = [ | |||
329 | 137 | """ | 137 | """ |
330 | 138 | # Created by cloud-init on instance boot automatically, do not edit. | 138 | # Created by cloud-init on instance boot automatically, do not edit. |
331 | 139 | # | 139 | # |
333 | 140 | BOOTPROTO=static | 140 | BOOTPROTO=none |
334 | 141 | DEFROUTE=yes | 141 | DEFROUTE=yes |
335 | 142 | DEVICE=eth0 | 142 | DEVICE=eth0 |
336 | 143 | GATEWAY=172.19.3.254 | 143 | GATEWAY=172.19.3.254 |
337 | @@ -205,38 +205,14 @@ nameserver 172.19.0.12 | |||
338 | 205 | # Created by cloud-init on instance boot automatically, do not edit. | 205 | # Created by cloud-init on instance boot automatically, do not edit. |
339 | 206 | # | 206 | # |
340 | 207 | BOOTPROTO=none | 207 | BOOTPROTO=none |
341 | 208 | DEVICE=eth0 | ||
342 | 209 | HWADDR=fa:16:3e:ed:9a:59 | ||
343 | 210 | NM_CONTROLLED=no | ||
344 | 211 | ONBOOT=yes | ||
345 | 212 | TYPE=Ethernet | ||
346 | 213 | USERCTL=no | ||
347 | 214 | """.lstrip()), | ||
348 | 215 | ('etc/sysconfig/network-scripts/ifcfg-eth0:0', | ||
349 | 216 | """ | ||
350 | 217 | # Created by cloud-init on instance boot automatically, do not edit. | ||
351 | 218 | # | ||
352 | 219 | BOOTPROTO=static | ||
353 | 220 | DEFROUTE=yes | 208 | DEFROUTE=yes |
355 | 221 | DEVICE=eth0:0 | 209 | DEVICE=eth0 |
356 | 222 | GATEWAY=172.19.3.254 | 210 | GATEWAY=172.19.3.254 |
357 | 223 | HWADDR=fa:16:3e:ed:9a:59 | 211 | HWADDR=fa:16:3e:ed:9a:59 |
358 | 224 | IPADDR=172.19.1.34 | 212 | IPADDR=172.19.1.34 |
359 | 213 | IPADDR1=10.0.0.10 | ||
360 | 225 | NETMASK=255.255.252.0 | 214 | NETMASK=255.255.252.0 |
375 | 226 | NM_CONTROLLED=no | 215 | NETMASK1=255.255.255.0 |
362 | 227 | ONBOOT=yes | ||
363 | 228 | TYPE=Ethernet | ||
364 | 229 | USERCTL=no | ||
365 | 230 | """.lstrip()), | ||
366 | 231 | ('etc/sysconfig/network-scripts/ifcfg-eth0:1', | ||
367 | 232 | """ | ||
368 | 233 | # Created by cloud-init on instance boot automatically, do not edit. | ||
369 | 234 | # | ||
370 | 235 | BOOTPROTO=static | ||
371 | 236 | DEVICE=eth0:1 | ||
372 | 237 | HWADDR=fa:16:3e:ed:9a:59 | ||
373 | 238 | IPADDR=10.0.0.10 | ||
374 | 239 | NETMASK=255.255.255.0 | ||
376 | 240 | NM_CONTROLLED=no | 216 | NM_CONTROLLED=no |
377 | 241 | ONBOOT=yes | 217 | ONBOOT=yes |
378 | 242 | TYPE=Ethernet | 218 | TYPE=Ethernet |
379 | @@ -266,7 +242,7 @@ nameserver 172.19.0.12 | |||
380 | 266 | }], | 242 | }], |
381 | 267 | "ip_address": "172.19.1.34", "id": "network0" | 243 | "ip_address": "172.19.1.34", "id": "network0" |
382 | 268 | }, { | 244 | }, { |
384 | 269 | "network_id": "public-ipv6", | 245 | "network_id": "public-ipv6-a", |
385 | 270 | "type": "ipv6", "netmask": "", | 246 | "type": "ipv6", "netmask": "", |
386 | 271 | "link": "tap1a81968a-79", | 247 | "link": "tap1a81968a-79", |
387 | 272 | "routes": [ | 248 | "routes": [ |
388 | @@ -277,6 +253,20 @@ nameserver 172.19.0.12 | |||
389 | 277 | } | 253 | } |
390 | 278 | ], | 254 | ], |
391 | 279 | "ip_address": "2001:DB8::10", "id": "network1" | 255 | "ip_address": "2001:DB8::10", "id": "network1" |
392 | 256 | }, { | ||
393 | 257 | "network_id": "public-ipv6-b", | ||
394 | 258 | "type": "ipv6", "netmask": "64", | ||
395 | 259 | "link": "tap1a81968a-79", | ||
396 | 260 | "routes": [ | ||
397 | 261 | ], | ||
398 | 262 | "ip_address": "2001:DB9::10", "id": "network2" | ||
399 | 263 | }, { | ||
400 | 264 | "network_id": "public-ipv6-c", | ||
401 | 265 | "type": "ipv6", "netmask": "64", | ||
402 | 266 | "link": "tap1a81968a-79", | ||
403 | 267 | "routes": [ | ||
404 | 268 | ], | ||
405 | 269 | "ip_address": "2001:DB10::10", "id": "network3" | ||
406 | 280 | }], | 270 | }], |
407 | 281 | "links": [ | 271 | "links": [ |
408 | 282 | { | 272 | { |
409 | @@ -296,41 +286,16 @@ nameserver 172.19.0.12 | |||
410 | 296 | # Created by cloud-init on instance boot automatically, do not edit. | 286 | # Created by cloud-init on instance boot automatically, do not edit. |
411 | 297 | # | 287 | # |
412 | 298 | BOOTPROTO=none | 288 | BOOTPROTO=none |
413 | 299 | DEVICE=eth0 | ||
414 | 300 | HWADDR=fa:16:3e:ed:9a:59 | ||
415 | 301 | NM_CONTROLLED=no | ||
416 | 302 | ONBOOT=yes | ||
417 | 303 | TYPE=Ethernet | ||
418 | 304 | USERCTL=no | ||
419 | 305 | """.lstrip()), | ||
420 | 306 | ('etc/sysconfig/network-scripts/ifcfg-eth0:0', | ||
421 | 307 | """ | ||
422 | 308 | # Created by cloud-init on instance boot automatically, do not edit. | ||
423 | 309 | # | ||
424 | 310 | BOOTPROTO=static | ||
425 | 311 | DEFROUTE=yes | 289 | DEFROUTE=yes |
427 | 312 | DEVICE=eth0:0 | 290 | DEVICE=eth0 |
428 | 313 | GATEWAY=172.19.3.254 | 291 | GATEWAY=172.19.3.254 |
429 | 314 | HWADDR=fa:16:3e:ed:9a:59 | 292 | HWADDR=fa:16:3e:ed:9a:59 |
430 | 315 | IPADDR=172.19.1.34 | 293 | IPADDR=172.19.1.34 |
431 | 316 | NETMASK=255.255.252.0 | ||
432 | 317 | NM_CONTROLLED=no | ||
433 | 318 | ONBOOT=yes | ||
434 | 319 | TYPE=Ethernet | ||
435 | 320 | USERCTL=no | ||
436 | 321 | """.lstrip()), | ||
437 | 322 | ('etc/sysconfig/network-scripts/ifcfg-eth0:1', | ||
438 | 323 | """ | ||
439 | 324 | # Created by cloud-init on instance boot automatically, do not edit. | ||
440 | 325 | # | ||
441 | 326 | BOOTPROTO=static | ||
442 | 327 | DEFROUTE=yes | ||
443 | 328 | DEVICE=eth0:1 | ||
444 | 329 | HWADDR=fa:16:3e:ed:9a:59 | ||
445 | 330 | IPV6ADDR=2001:DB8::10 | 294 | IPV6ADDR=2001:DB8::10 |
446 | 295 | IPV6ADDR_SECONDARIES="2001:DB9::10/64 2001:DB10::10/64" | ||
447 | 331 | IPV6INIT=yes | 296 | IPV6INIT=yes |
448 | 332 | IPV6_DEFAULTGW=2001:DB8::1 | 297 | IPV6_DEFAULTGW=2001:DB8::1 |
450 | 333 | NETMASK= | 298 | NETMASK=255.255.252.0 |
451 | 334 | NM_CONTROLLED=no | 299 | NM_CONTROLLED=no |
452 | 335 | ONBOOT=yes | 300 | ONBOOT=yes |
453 | 336 | TYPE=Ethernet | 301 | TYPE=Ethernet |
Fix dual stack IPv4/IPv6 configuration for RHEL
Dual stack IPv4/IPv6 configuration via config drive is broken for RHEL7.
This patch fixes several scenarios for IPv4/IPv6/dual stack with multiple IP assignment
Removes unpopular IPv4 alias files and invalid IPv6 alias files
Also fixes associated unit tests
LP: #1679817
LP: #1685534
LP: #1685532