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
=== modified file 'curtin/commands/apply_net.py'
--- curtin/commands/apply_net.py 2015-10-02 18:30:42 +0000
+++ curtin/commands/apply_net.py 2016-03-11 16:05:00 +0000
@@ -18,11 +18,15 @@
18import os18import os
19import sys19import sys
2020
21from .. import log
21import curtin.net as net22import curtin.net as net
22import curtin.util as util23import curtin.util as util
23from . import populate_one_subcmd24from . import populate_one_subcmd
2425
2526
27LOG = log.LOG
28
29
26def apply_net(target, network_state=None, network_config=None):30def apply_net(target, network_state=None, network_config=None):
27 if network_state is None and network_config is None:31 if network_state is None and network_config is None:
28 msg = "Must provide at least config or state"32 msg = "Must provide at least config or state"
@@ -47,6 +51,8 @@
47 # [--net-config=/config/maas_net.yml]51 # [--net-config=/config/maas_net.yml]
48 state = util.load_command_environment()52 state = util.load_command_environment()
4953
54 log.basicConfig(stream=args.log_file, verbosity=1)
55
50 if args.target is not None:56 if args.target is not None:
51 state['target'] = args.target57 state['target'] = args.target
5258
@@ -65,10 +71,15 @@
65 sys.stderr.write("Must provide at least config or state\n")71 sys.stderr.write("Must provide at least config or state\n")
66 sys.exit(2)72 sys.exit(2)
6773
68 apply_net(target=state['target'],74 LOG.info('Applying network configuration')
69 network_state=state['network_state'],75 try:
70 network_config=state['network_config'])76 apply_net(target=state['target'],
77 network_state=state['network_state'],
78 network_config=state['network_config'])
79 except Exception:
80 LOG.exception('failed to apply network config')
7181
82 LOG.info('Applied network configuration successfully')
72 sys.exit(0)83 sys.exit(0)
7384
7485
7586
=== modified file 'curtin/net/__init__.py'
--- curtin/net/__init__.py 2016-02-24 17:49:17 +0000
+++ curtin/net/__init__.py 2016-03-11 16:05:00 +0000
@@ -286,11 +286,12 @@
286 content = ""286 content = ""
287 interfaces = network_state.get('interfaces')287 interfaces = network_state.get('interfaces')
288 for iface in interfaces.values():288 for iface in interfaces.values():
289 # for physical interfaces write out a persist net udev rule289 if iface['type'] == 'physical':
290 if iface['type'] == 'physical' and \290 ifname = iface.get('name', None)
291 'name' in iface and 'mac_address' in iface:291 mac = iface.get('mac_address', '')
292 content += generate_udev_rule(iface['name'],292 # len(macaddr) == 2 * 6 + 5 == 17
293 iface['mac_address'])293 if ifname and mac and len(mac) == 17:
294 content += generate_udev_rule(ifname, mac)
294295
295 return content296 return content
296297
@@ -382,11 +383,14 @@
382383
383 for iface in sorted(interfaces.values(),384 for iface in sorted(interfaces.values(),
384 key=lambda k: (order[k['type']], k['name'])):385 key=lambda k: (order[k['type']], k['name'])):
385 content += "auto {name}\n".format(**iface)
386386
387 if content[-2:] != "\n\n":
388 content += "\n"
387 subnets = iface.get('subnets', {})389 subnets = iface.get('subnets', {})
388 if subnets:390 if subnets:
389 for index, subnet in zip(range(0, len(subnets)), subnets):391 for index, subnet in zip(range(0, len(subnets)), subnets):
392 if content[-2:] != "\n\n":
393 content += "\n"
390 iface['index'] = index394 iface['index'] = index
391 iface['mode'] = subnet['type']395 iface['mode'] = subnet['type']
392 if iface['mode'].endswith('6'):396 if iface['mode'].endswith('6'):
@@ -397,19 +401,22 @@
397 iface['mode'] = 'dhcp'401 iface['mode'] = 'dhcp'
398402
399 if index == 0:403 if index == 0:
404 if subnet['type'] != 'manual':
405 content += "auto {name}\n".format(**iface)
400 content += "iface {name} {inet} {mode}\n".format(**iface)406 content += "iface {name} {inet} {mode}\n".format(**iface)
401 else:407 else:
402 content += "auto {name}:{index}\n".format(**iface)408 if subnet['type'] != 'manual':
409 content += "auto {name}:{index}\n".format(**iface)
403 content += \410 content += \
404 "iface {name}:{index} {inet} {mode}\n".format(**iface)411 "iface {name}:{index} {inet} {mode}\n".format(**iface)
405412
406 content += iface_add_subnet(iface, subnet)413 content += iface_add_subnet(iface, subnet)
407 content += iface_add_attrs(iface)414 content += iface_add_attrs(iface)
408 content += "\n"
409 else:415 else:
416 if 'bond-master' in iface:
417 content += "auto {name}\n".format(**iface)
410 content += "iface {name} {inet} {mode}\n".format(**iface)418 content += "iface {name} {inet} {mode}\n".format(**iface)
411 content += iface_add_attrs(iface)419 content += iface_add_attrs(iface)
412 content += "\n"
413420
414 for route in network_state.get('routes'):421 for route in network_state.get('routes'):
415 content += render_route(route)422 content += render_route(route)
@@ -426,11 +433,15 @@
426 eni = os.path.sep.join((target, eni,))433 eni = os.path.sep.join((target, eni,))
427 util.ensure_dir(os.path.dirname(eni))434 util.ensure_dir(os.path.dirname(eni))
428 with open(eni, 'w+') as f:435 with open(eni, 'w+') as f:
436 LOG.info('Writing ' + eni)
429 f.write(render_interfaces(network_state))437 f.write(render_interfaces(network_state))
430438
431 netrules = os.path.sep.join((target, netrules,))439 netrules = os.path.sep.join((target, netrules,))
432 util.ensure_dir(os.path.dirname(netrules))440 util.ensure_dir(os.path.dirname(netrules))
433 with open(netrules, 'w+') as f:441 persistent_net_rules = render_persistent_net(network_state)
434 f.write(render_persistent_net(network_state))442 if len(persistent_net_rules) > 0:
443 with open(netrules, 'w+') as f:
444 LOG.info('Writing ' + netrules)
445 f.write(persistent_net_rules)
435446
436# vi: ts=4 expandtab syntax=python447# vi: ts=4 expandtab syntax=python
437448
=== modified file 'tests/unittests/test_net.py'
--- tests/unittests/test_net.py 2016-02-24 17:49:17 +0000
+++ tests/unittests/test_net.py 2016-03-11 16:05:00 +0000
@@ -432,12 +432,14 @@
432 with open(self.config_f, 'w') as fp:432 with open(self.config_f, 'w') as fp:
433 fp.write(self.config)433 fp.write(self.config)
434434
435 def get_net_config(self):435 def get_net_config(self, config=None):
436 cfg = yaml.safe_load(self.config)436 if config is None:
437 config = self.config
438 cfg = yaml.safe_load(config)
437 return cfg.get('network')439 return cfg.get('network')
438440
439 def get_net_state(self):441 def get_net_state(self, config=None):
440 net_cfg = self.get_net_config()442 net_cfg = self.get_net_config(config)
441 version = net_cfg.get('version')443 version = net_cfg.get('version')
442 config = net_cfg.get('config')444 config = net_cfg.get('config')
443 ns = network_state.NetworkState(version=version, config=config)445 ns = network_state.NetworkState(version=version, config=config)
@@ -482,10 +484,41 @@
482 ' address 192.168.21.3/24\n' +484 ' address 192.168.21.3/24\n' +
483 ' dns-nameservers 8.8.8.8 8.8.4.4\n' +485 ' dns-nameservers 8.8.8.8 8.8.4.4\n' +
484 ' dns-search barley.maas sach.maas\n\n' +486 ' dns-search barley.maas sach.maas\n\n' +
485 'auto eth1\n' + 'iface eth1 inet manual\n\n')487 'iface eth1 inet manual\n\n')
486 net_ifaces = net.render_interfaces(ns.network_state)488 net_ifaces = net.render_interfaces(ns.network_state)
487 print(ns.network_state.get('interfaces'))489 print(ns.network_state.get('interfaces'))
488 self.assertEqual(sorted(ifaces.split('\n')),490 self.assertEqual(sorted(ifaces.split('\n')),
489 sorted(net_ifaces.split('\n')))491 sorted(net_ifaces.split('\n')))
492
493 def test_render_interfaces_bonds(self):
494 bond_config = open('examples/tests/bonding_network.yaml', 'r').read()
495
496 ns = self.get_net_state(bond_config)
497 ifaces = ('auto lo\n' +
498 'iface lo inet loopback\n\n' +
499 'auto eth0\n' +
500 'iface eth0 inet dhcp\n\n' +
501 'auto eth1\n' +
502 'iface eth1 inet manual\n' +
503 ' bond-master bond0\n' +
504 ' bond-mode active-backup\n\n' +
505 'auto eth2\n' +
506 'iface eth2 inet manual\n' +
507 ' bond-master bond0\n' +
508 ' bond-mode active-backup\n\n' +
509 'auto bond0\n' +
510 'iface bond0 inet static\n' +
511 ' address 10.23.23.2/24\n' +
512 ' bond-mode active-backup\n' +
513 ' hwaddress 52:54:00:12:34:06\n' +
514 ' bond-slaves none\n')
515 net_ifaces = net.render_interfaces(ns.network_state)
516 print("\n".join(sorted(ifaces.split('\n'))))
517 print("\n^^ LOCAL -- RENDER vv")
518 print("\n".join(sorted(net_ifaces.split('\n'))))
519 print(ns.network_state.get('interfaces'))
520 self.assertEqual(sorted(ifaces.split('\n')),
521 sorted(net_ifaces.split('\n')))
522
490523
491# vi: ts=4 expandtab syntax=python524# vi: ts=4 expandtab syntax=python

Subscribers

People subscribed via source and target branches