Merge lp:~raharper/curtin/trunk.net-manual into lp:~curtin-dev/curtin/trunk

Proposed by Ryan Harper
Status: Merged
Merged at revision: 374
Proposed branch: lp:~raharper/curtin/trunk.net-manual
Merge into: lp:~curtin-dev/curtin/trunk
Diff against target: 199 lines (+78/-23)
3 files modified
curtin/commands/apply_net.py (+14/-3)
curtin/net/__init__.py (+22/-11)
tests/unittests/test_net.py (+42/-9)
To merge this branch: bzr merge lp:~raharper/curtin/trunk.net-manual
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Needs Fixing
Scott Moser Pending
Review via email: mp+288799@code.launchpad.net

Commit message

net: clean up e/n/i and udev rules for manually configured interfaces

Don't emit auto $iface for manually configured interfaces, except when
enslaved to bridges or bonds.
Don't emit a udev rules for nics without a mac_address
Don't write a udev rules file unless we have at least one interface to map
Add unittest to validate bonding network config which uses iface manual but in the service of a bond

To post a comment you must log in.
Revision history for this message
Scott Moser (smoser) wrote :

It seems maybe arguable that you'd *want* to blank the udev rules if there were no interfaces to map.
ie, if you're fully declaritive we own that file.

thoughts?

Revision history for this message
Ryan Harper (raharper) wrote :

I'm not sure. My initial concern was if the file exists but was empty,
that systemd would not attempt to do persistent naming
as it expects that udev rules will handle it; then we don't do it, and the
system now has *no* persistent naming.

One could argue that's expected since user did not provide any matching
rules in the network.yaml.

If systemd applies persistent naming despite the presence of an empty udev
rules file, then that might make
the eni useless (iface eth0 does not exist).

However, if systemd doesn't do any naming because the file exists, then we
at least have a valid eni file despite
it not being persistent w.r.t which device shows up under the kname eth0.

I think I've convinced myself that we should at least ensure that systemd
doesn't do persistent naming. We could
emit a header in the file in case systemd checks for non-empty rules.

Shall I push an update or will you take in merge?

On Fri, Mar 11, 2016 at 1:17 PM, Scott Moser <email address hidden> wrote:

> It seems maybe arguable that you'd *want* to blank the udev rules if there
> were no interfaces to map.
> ie, if you're fully declaritive we own that file.
>
> thoughts?
> --
> https://code.launchpad.net/~raharper/curtin/trunk.net-manual/+merge/288799
> You are the owner of lp:~raharper/curtin/trunk.net-manual.
>

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'curtin/commands/apply_net.py'
2--- curtin/commands/apply_net.py 2015-10-02 18:30:42 +0000
3+++ curtin/commands/apply_net.py 2016-03-11 16:05:00 +0000
4@@ -18,11 +18,15 @@
5 import os
6 import sys
7
8+from .. import log
9 import curtin.net as net
10 import curtin.util as util
11 from . import populate_one_subcmd
12
13
14+LOG = log.LOG
15+
16+
17 def apply_net(target, network_state=None, network_config=None):
18 if network_state is None and network_config is None:
19 msg = "Must provide at least config or state"
20@@ -47,6 +51,8 @@
21 # [--net-config=/config/maas_net.yml]
22 state = util.load_command_environment()
23
24+ log.basicConfig(stream=args.log_file, verbosity=1)
25+
26 if args.target is not None:
27 state['target'] = args.target
28
29@@ -65,10 +71,15 @@
30 sys.stderr.write("Must provide at least config or state\n")
31 sys.exit(2)
32
33- apply_net(target=state['target'],
34- network_state=state['network_state'],
35- network_config=state['network_config'])
36+ LOG.info('Applying network configuration')
37+ try:
38+ apply_net(target=state['target'],
39+ network_state=state['network_state'],
40+ network_config=state['network_config'])
41+ except Exception:
42+ LOG.exception('failed to apply network config')
43
44+ LOG.info('Applied network configuration successfully')
45 sys.exit(0)
46
47
48
49=== modified file 'curtin/net/__init__.py'
50--- curtin/net/__init__.py 2016-02-24 17:49:17 +0000
51+++ curtin/net/__init__.py 2016-03-11 16:05:00 +0000
52@@ -286,11 +286,12 @@
53 content = ""
54 interfaces = network_state.get('interfaces')
55 for iface in interfaces.values():
56- # for physical interfaces write out a persist net udev rule
57- if iface['type'] == 'physical' and \
58- 'name' in iface and 'mac_address' in iface:
59- content += generate_udev_rule(iface['name'],
60- iface['mac_address'])
61+ if iface['type'] == 'physical':
62+ ifname = iface.get('name', None)
63+ mac = iface.get('mac_address', '')
64+ # len(macaddr) == 2 * 6 + 5 == 17
65+ if ifname and mac and len(mac) == 17:
66+ content += generate_udev_rule(ifname, mac)
67
68 return content
69
70@@ -382,11 +383,14 @@
71
72 for iface in sorted(interfaces.values(),
73 key=lambda k: (order[k['type']], k['name'])):
74- content += "auto {name}\n".format(**iface)
75
76+ if content[-2:] != "\n\n":
77+ content += "\n"
78 subnets = iface.get('subnets', {})
79 if subnets:
80 for index, subnet in zip(range(0, len(subnets)), subnets):
81+ if content[-2:] != "\n\n":
82+ content += "\n"
83 iface['index'] = index
84 iface['mode'] = subnet['type']
85 if iface['mode'].endswith('6'):
86@@ -397,19 +401,22 @@
87 iface['mode'] = 'dhcp'
88
89 if index == 0:
90+ if subnet['type'] != 'manual':
91+ content += "auto {name}\n".format(**iface)
92 content += "iface {name} {inet} {mode}\n".format(**iface)
93 else:
94- content += "auto {name}:{index}\n".format(**iface)
95+ if subnet['type'] != 'manual':
96+ content += "auto {name}:{index}\n".format(**iface)
97 content += \
98 "iface {name}:{index} {inet} {mode}\n".format(**iface)
99
100 content += iface_add_subnet(iface, subnet)
101 content += iface_add_attrs(iface)
102- content += "\n"
103 else:
104+ if 'bond-master' in iface:
105+ content += "auto {name}\n".format(**iface)
106 content += "iface {name} {inet} {mode}\n".format(**iface)
107 content += iface_add_attrs(iface)
108- content += "\n"
109
110 for route in network_state.get('routes'):
111 content += render_route(route)
112@@ -426,11 +433,15 @@
113 eni = os.path.sep.join((target, eni,))
114 util.ensure_dir(os.path.dirname(eni))
115 with open(eni, 'w+') as f:
116+ LOG.info('Writing ' + eni)
117 f.write(render_interfaces(network_state))
118
119 netrules = os.path.sep.join((target, netrules,))
120 util.ensure_dir(os.path.dirname(netrules))
121- with open(netrules, 'w+') as f:
122- f.write(render_persistent_net(network_state))
123+ persistent_net_rules = render_persistent_net(network_state)
124+ if len(persistent_net_rules) > 0:
125+ with open(netrules, 'w+') as f:
126+ LOG.info('Writing ' + netrules)
127+ f.write(persistent_net_rules)
128
129 # vi: ts=4 expandtab syntax=python
130
131=== modified file 'tests/unittests/test_net.py'
132--- tests/unittests/test_net.py 2016-02-24 17:49:17 +0000
133+++ tests/unittests/test_net.py 2016-03-11 16:05:00 +0000
134@@ -432,12 +432,14 @@
135 with open(self.config_f, 'w') as fp:
136 fp.write(self.config)
137
138- def get_net_config(self):
139- cfg = yaml.safe_load(self.config)
140+ def get_net_config(self, config=None):
141+ if config is None:
142+ config = self.config
143+ cfg = yaml.safe_load(config)
144 return cfg.get('network')
145
146- def get_net_state(self):
147- net_cfg = self.get_net_config()
148+ def get_net_state(self, config=None):
149+ net_cfg = self.get_net_config(config)
150 version = net_cfg.get('version')
151 config = net_cfg.get('config')
152 ns = network_state.NetworkState(version=version, config=config)
153@@ -482,10 +484,41 @@
154 ' address 192.168.21.3/24\n' +
155 ' dns-nameservers 8.8.8.8 8.8.4.4\n' +
156 ' dns-search barley.maas sach.maas\n\n' +
157- 'auto eth1\n' + 'iface eth1 inet manual\n\n')
158- net_ifaces = net.render_interfaces(ns.network_state)
159- print(ns.network_state.get('interfaces'))
160- self.assertEqual(sorted(ifaces.split('\n')),
161- sorted(net_ifaces.split('\n')))
162+ 'iface eth1 inet manual\n\n')
163+ net_ifaces = net.render_interfaces(ns.network_state)
164+ print(ns.network_state.get('interfaces'))
165+ self.assertEqual(sorted(ifaces.split('\n')),
166+ sorted(net_ifaces.split('\n')))
167+
168+ def test_render_interfaces_bonds(self):
169+ bond_config = open('examples/tests/bonding_network.yaml', 'r').read()
170+
171+ ns = self.get_net_state(bond_config)
172+ ifaces = ('auto lo\n' +
173+ 'iface lo inet loopback\n\n' +
174+ 'auto eth0\n' +
175+ 'iface eth0 inet dhcp\n\n' +
176+ 'auto eth1\n' +
177+ 'iface eth1 inet manual\n' +
178+ ' bond-master bond0\n' +
179+ ' bond-mode active-backup\n\n' +
180+ 'auto eth2\n' +
181+ 'iface eth2 inet manual\n' +
182+ ' bond-master bond0\n' +
183+ ' bond-mode active-backup\n\n' +
184+ 'auto bond0\n' +
185+ 'iface bond0 inet static\n' +
186+ ' address 10.23.23.2/24\n' +
187+ ' bond-mode active-backup\n' +
188+ ' hwaddress 52:54:00:12:34:06\n' +
189+ ' bond-slaves none\n')
190+ net_ifaces = net.render_interfaces(ns.network_state)
191+ print("\n".join(sorted(ifaces.split('\n'))))
192+ print("\n^^ LOCAL -- RENDER vv")
193+ print("\n".join(sorted(net_ifaces.split('\n'))))
194+ print(ns.network_state.get('interfaces'))
195+ self.assertEqual(sorted(ifaces.split('\n')),
196+ sorted(net_ifaces.split('\n')))
197+
198
199 # vi: ts=4 expandtab syntax=python

Subscribers

People subscribed via source and target branches