Merge lp:~hexta/curtin/fix-static-routes-with-multiple-interfaces into lp:~curtin-dev/curtin/trunk

Proposed by Artur Molchanov
Status: Work in progress
Proposed branch: lp:~hexta/curtin/fix-static-routes-with-multiple-interfaces
Merge into: lp:~curtin-dev/curtin/trunk
Diff against target: 173 lines (+58/-59)
2 files modified
curtin/net/__init__.py (+40/-47)
tests/unittests/test_net.py (+18/-12)
To merge this branch: bzr merge lp:~hexta/curtin/fix-static-routes-with-multiple-interfaces
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Scott Moser Pending
curtin developers Pending
Review via email: mp+318788@code.launchpad.net

Description of the change

Currently, curtin creates /etc/network/interfaces with all static routes passed after last interface entry. As a result, all routes will be added only after the last interface turned on.

Example /etc/network/interfaces:
auto lo
iface lo inet loopback
    dns-search maas synesis
    dns-nameservers 10.1.254.254

auto boot
iface boot inet static
    gateway 10.1.255.254
    dns-nameservers 10.1.254.254
    address 10.1.2.255/16
    mtu 9000

auto private
iface private inet static
    address 10.7.0.201/16
    mtu 9000

auto public
iface public inet static
    dns-nameservers 10.1.254.254
    address 10.9.0.201/16
    mtu 9000

auto virt-inet
iface virt-inet inet static
    address 10.88.0.1/16
    mtu 1500
post-up route add -net 10.8.0.0 netmask 255.255.0.0 gw 10.9.255.254 metric 0 || true
pre-down route del -net 10.8.0.0 netmask 255.255.0.0 gw 10.9.255.254 metric 0 || true
post-up route add -net 192.168.0.0 netmask 255.255.0.0 gw 10.9.255.254 metric 0 || true
pre-down route del -net 192.168.0.0 netmask 255.255.0.0 gw 10.9.255.254 metric 0 || true
post-up route add -net 10.13.0.0 netmask 255.255.0.0 gw 10.9.255.254 metric 0 || true
pre-down route del -net 10.13.0.0 netmask 255.255.0.0 gw 10.9.255.254 metric 0 || true
post-up route add -net 10.4.0.0 netmask 255.255.0.0 gw 10.9.255.254 metric 0 || true
pre-down route del -net 10.4.0.0 netmask 255.255.0.0 gw 10.9.255.254 metric 0 || true
post-up route add -net 172.20.0.0 netmask 255.255.0.0 gw 10.9.255.254 metric 0 || true
pre-down route del -net 172.20.0.0 netmask 255.255.0.0 gw 10.9.255.254 metric 0 || true

source /etc/network/interfaces.d/*.cfg

All static routes applied to 'virt-inet' interface section. Restarting interface 'public' will not trigger adding routes.

To post a comment you must log in.
472. By Artur Molchanov <email address hidden>

Remove unused 'pprint' module

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (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
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) wrote :

Hi, can you share your curtin network configuration?

maas <session> machine get-curtin-config <system-id>

Revision history for this message
Artur Molchanov (hexta) wrote :

> Hi, can you share your curtin network configuration?
>
> maas <session> machine get-curtin-config <system-id>

network:
  config:
  - id: boot
    mac_address: 0c:c4:7a:53:4d:6c
    mtu: 9000
    name: boot
    subnets:
    - address: 10.1.2.255/16
      dns_nameservers:
      - 10.1.254.254
      gateway: 10.1.255.254
      type: static
    type: physical
  - id: private
    mac_address: a0:36:9f:c6:37:c0
    mtu: 9000
    name: private
    subnets:
    - address: 10.7.0.201/16
      dns_nameservers: []
      type: static
    type: physical
  - id: public
    mac_address: a0:36:9f:c6:37:c2
    mtu: 9000
    name: public
    subnets:
    - address: 10.9.0.201/16
      dns_nameservers:
      - 10.1.254.254
      type: static
    type: physical
  - id: virt-inet
    mac_address: 0c:c4:7a:53:4d:6d
    mtu: 1500
    name: virt-inet
    subnets:
    - address: 10.88.0.1/16
      dns_nameservers: []
      type: static
    type: physical
  - destination: 10.8.0.0/16
    gateway: 10.9.255.254
    id: 8
    metric: 0
    type: route
  - destination: 172.20.0.0/16
    gateway: 10.9.255.254
    id: 9
    metric: 0
    type: route
  - destination: 192.168.0.0/16
    gateway: 10.9.255.254
    id: 11
    metric: 0
    type: route
  - destination: 10.13.0.0/16
    gateway: 10.9.255.254
    id: 12
    metric: 0
    type: route
  - destination: 10.4.0.0/16
    gateway: 10.9.255.254
    id: 21
    metric: 0
    type: route
  - address:
    - 10.1.254.254
    search:
    - maas
    - BY
    - SNS
    - synesis
    type: nameserver
  version: 1

Revision history for this message
Artur Molchanov (hexta) wrote :

ping

Revision history for this message
Scott Moser (smoser) wrote :

Hi,
sorry for being so slow to respond.
Youv'e done a good job of explaining the bug in the current implementation and explaining your fix.

Ryan and I have talked about this before... there isn't really a great way to solve it, and your solution may well be as good as any.

Another option would be to look through each of these "global" routes and find which subnet would have a route to the gateway and put it there.

Another complexity is that right now we basically have ENI rendering bits of code (cloud-init and curtin), with a goal of getting rid of that in curtin... at some point curtin will be able to just "pass through" the configuration to cloud-init and let it render it.

So your branch hasn't been completely ignored. Sorry for the crickets.

Revision history for this message
Scott Moser (smoser) wrote :

Hi Arthur,
I'm sorry that this has never really gotten looked at.
If you still are having a problem with it, could you re-submit via git?

I'm going to mark this 'Rejected' as no other state really makes sense since we've mvoed from bzr.

Revision history for this message
Scott Moser (smoser) wrote :

decided to move it to work in progress.

Unmerged revisions

472. By Artur Molchanov <email address hidden>

Remove unused 'pprint' module

471. By Artur Molchanov <email address hidden>

Fix static routes in case of multiple interfaces.

Currently, curtin creates /etc/network/interfaces with all static routes passed
after last interface entry. As a result, all routes will be added only after the
last interface turned on.

Changes:
- Create script /etc/network/if-up.d/routes.
  This script will invoke 'route' command with
  appropriate arguments after any interface turned on.
- Get rid of adding static routes inside /etc/network/interfaces.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'curtin/net/__init__.py'
2--- curtin/net/__init__.py 2017-02-06 20:58:37 +0000
3+++ curtin/net/__init__.py 2017-03-02 16:03:09 +0000
4@@ -377,47 +377,41 @@
5 return content
6
7
8-def render_route(route, indent=""):
9- """When rendering routes for an iface, in some cases applying a route
10- may result in the route command returning non-zero which produces
11- some confusing output for users manually using ifup/ifdown[1]. To
12- that end, we will optionally include an '|| true' postfix to each
13- route line allowing users to work with ifup/ifdown without using
14- --force option.
15-
16- We may at somepoint not want to emit this additional postfix, and
17- add a 'strict' flag to this function. When called with strict=True,
18- then we will not append the postfix.
19-
20- 1. http://askubuntu.com/questions/168033/
21- how-to-set-static-routes-in-ubuntu-server
22- """
23- content = []
24- up = indent + "post-up route add"
25- down = indent + "pre-down route del"
26- or_true = " || true"
27- mapping = {
28- 'network': '-net',
29- 'netmask': 'netmask',
30- 'gateway': 'gw',
31- 'metric': 'metric',
32- }
33- if route['network'] == '0.0.0.0' and route['netmask'] == '0.0.0.0':
34- default_gw = " default gw %s" % route['gateway']
35- content.append(up + default_gw + or_true)
36- content.append(down + default_gw + or_true)
37- elif route['network'] == '::' and route['netmask'] == 0:
38- # ipv6!
39- default_gw = " -A inet6 default gw %s" % route['gateway']
40- content.append(up + default_gw + or_true)
41- content.append(down + default_gw + or_true)
42- else:
43- route_line = ""
44- for k in ['network', 'netmask', 'gateway', 'metric']:
45- if k in route:
46- route_line += " %s %s" % (mapping[k], route[k])
47- content.append(up + route_line + or_true)
48- content.append(down + route_line + or_true)
49+def render_routes(network_state, indent=""):
50+ content = ['#!/bin/sh', '']
51+
52+ def render_route(route):
53+ up = indent + "route add"
54+ mapping = {
55+ 'network': '-net',
56+ 'netmask': 'netmask',
57+ 'gateway': 'gw',
58+ 'metric': 'metric',
59+ }
60+ if route['network'] == '0.0.0.0' and route['netmask'] == '0.0.0.0':
61+ default_gw = " default gw %s" % route['gateway']
62+ content.append(up + default_gw)
63+ elif route['network'] == '::' and route['netmask'] == 0:
64+ # ipv6!
65+ default_gw = " -A inet6 default gw %s" % route['gateway']
66+ content.append(up + default_gw)
67+ else:
68+ route_line = ""
69+ for k in ['network', 'netmask', 'gateway', 'metric']:
70+ if k in route:
71+ route_line += " %s %s" % (mapping[k], route[k])
72+ content.append(up + route_line)
73+
74+ for route in network_state.get('routes'):
75+ render_route(route)
76+
77+ for iface in network_state.get('interfaces', {}).values():
78+ for subnet in iface.get('subnets', []):
79+ for route in subnet.get('routes', []):
80+ render_route(route)
81+
82+ content.append('exit 0')
83+
84 return "\n".join(content) + "\n"
85
86
87@@ -498,9 +492,6 @@
88 content += iface_add_subnet(iface, subnet)
89 content += iface_add_attrs(iface, index)
90
91- for route in subnet.get('routes', []):
92- content += render_route(route, indent=" ") + '\n'
93-
94 else:
95 # ifenslave docs say to auto the slave devices
96 if 'bond-master' in iface or 'bond-slaves' in iface:
97@@ -508,9 +499,6 @@
98 content += "iface {name} {inet} {mode}\n".format(**iface)
99 content += iface_add_attrs(iface, 0)
100
101- for route in network_state.get('routes'):
102- content += render_route(route)
103-
104 # global replacements until v2 format
105 content = content.replace('mac_address', 'hwaddress ether')
106
107@@ -524,6 +512,7 @@
108 eni = 'etc/network/interfaces'
109 netrules = 'etc/udev/rules.d/70-persistent-net.rules'
110 cc = 'etc/cloud/cloud.cfg.d/curtin-disable-cloudinit-networking.cfg'
111+ routes_file = 'etc/network/if-up.d/routes'
112
113 eni = os.path.sep.join((target, eni,))
114 LOG.info('Writing ' + eni)
115@@ -537,6 +526,10 @@
116 LOG.info('Writing ' + cc_disable)
117 util.write_file(cc_disable, content='network: {config: disabled}\n')
118
119+ routes_file = os.path.sep.join((target, routes_file,))
120+ LOG.info('Writing ' + routes_file)
121+ util.write_file(routes_file, content=render_routes(network_state), mode=0o755)
122+
123
124 def get_interface_mac(ifname):
125 """Returns the string value of an interface's MAC Address"""
126
127=== modified file 'tests/unittests/test_net.py'
128--- tests/unittests/test_net.py 2017-02-09 16:58:23 +0000
129+++ tests/unittests/test_net.py 2017-03-02 16:03:09 +0000
130@@ -680,18 +680,7 @@
131 'iface eth0 inet static',
132 ' address 172.23.31.42/26',
133 ' gateway 172.23.31.2',
134- ('post-up route add -net 10.0.0.0 netmask 255.240.0.0 gw '
135- '172.23.31.1 metric 0 || true'),
136- ('pre-down route del -net 10.0.0.0 netmask 255.240.0.0 gw '
137- '172.23.31.1 metric 0 || true'),
138- ('post-up route add -net 192.168.2.0 netmask 255.255.255.0 gw '
139- '172.23.31.1 metric 0 || true'),
140- ('pre-down route del -net 192.168.2.0 netmask 255.255.255.0 gw '
141- '172.23.31.1 metric 0 || true'),
142- ('post-up route add -net 10.0.200.0 netmask 255.255.0.0 gw '
143- '172.23.31.1 metric 1 || true'),
144- ('pre-down route del -net 10.0.200.0 netmask 255.255.0.0 gw '
145- '172.23.31.1 metric 1 || true'),
146+ '',
147 'source /etc/network/interfaces.d/*.cfg',
148 ]
149
150@@ -702,6 +691,23 @@
151 sorted([line for line in expected if line]),
152 sorted([line for line in found if line]))
153
154+ expected = [
155+ '#!/bin/sh',
156+ '',
157+ ('route add -net 10.0.0.0 netmask 255.240.0.0 gw '
158+ '172.23.31.1 metric 0'),
159+ ('route add -net 192.168.2.0 netmask 255.255.255.0 gw '
160+ '172.23.31.1 metric 0'),
161+ ('route add -net 10.0.200.0 netmask 255.255.0.0 gw '
162+ '172.23.31.1 metric 1'),
163+ 'exit 0',
164+ ]
165+
166+ found = net.render_routes(ns.network_state).split('\n')
167+ self.assertEqual(
168+ sorted([line for line in expected if line]),
169+ sorted([line for line in found if line]))
170+
171 def test_render_interfaces_bridges(self):
172 bridge_config = open(
173 'examples/tests/bridging_network.yaml', 'r').read()

Subscribers

People subscribed via source and target branches