Merge ~bregeer-ctl/cloud-init:bugfix/centos7_resolv into cloud-init:master

Proposed by Bert JW Regeer
Status: Rejected
Rejected by: Scott Moser
Proposed branch: ~bregeer-ctl/cloud-init:bugfix/centos7_resolv
Merge into: cloud-init:master
Diff against target: 64 lines (+24/-3)
1 file modified
cloudinit/net/sysconfig.py (+24/-3)
Reviewer Review Type Date Requested Status
Bert JW Regeer (community) Disapprove
cloud-init Commiters Pending
Review via email: mp+305058@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Scott Moser (smoser) wrote :

This generally looks good.
cloud-init gets network configuration from a variety of sources (openstack config drive in 'interfaces(5)' format, config drive in network_data.json format, Nocloud in 'network_config' format....

Some of those have per-interface dns configuration, some do not. It'd be nice to render this per-interface information when we can.

And even in the openstack interfaces 5 format it *could* declare per-network-device dns entries (the parser supports maintaining that information).

The second thing, you need to sign the canonical contributors agreement (http://www.ubuntu.com/legal/contributors). Please do so, and mention to me that you have in irc or here. Let me know if you have any questions on that.

Revision history for this message
Lars Kellogg-Stedman (larsks) wrote :

This all looks fine as far as fixing the immediate problem.

I can't help but feel that there must be a better way to cooperate with networkmanager, because there are some situations in which it's really useful to have nm managing dns (vpn environments in which you want certain domains to resolve via one set of nameservers and everything else via another comes to mind), but this patch is certainly an improvement over the existing situation.

Revision history for this message
Lars Kellogg-Stedman (larsks) wrote :

Re: smoser's comment:

> Some of those have per-interface dns configuration, some do not. It'd be nice to render this
> per-interface information when we can.

If we tell networkmanager not to manage /etc/resolv.conf, those interface-specific nameservers are going to be effectively ignored. I think that this would only work in the case where there are no globally defined nameservers (everything is interface-specific).

Revision history for this message
Bert JW Regeer (bregeer-ctl) wrote :

I am not willing to sign the CLA as it currently stands. I have no issues with providing my changes under the GPLv3, I do have issues signing a document that states Canonical is allowed to re-license my changes under any license they deem necessary. I also refuse to agree to the patent clause further than what the GPLv3 requires.

Feel free to pull this change under the GPLv3 or re-implement.

For now I will have my fork available at https://gitlab.com/bertjwregeer/cloud-init, fixes are in the bugfix/centos7_resolv branch: https://gitlab.com/bertjwregeer/cloud-init/commits/bugfix/centos7_resolv

review: Disapprove

Unmerged commits

54ed5cd... by Bert JW Regeer

Add debug logging

2397d42... by Bert JW Regeer

CentOS/RHEL 7 has NetworkManager manage resolv.conf

When cloud-init first writes to /etc/resolv.conf it does so before
networking is active. As soon as networking is started, NetworkManager
sees that /etc/resolv.conf is a file (this behaviour is important...),
and will take over managing of it, replacing the entries we just added
with a blank configuration thereby removing all nameserver entries.

It expects to have the DNS entries be available on the ifcfg-eth* files,
however with the way network data is presented to cloud-init, instead we
get a listing of nameservers to use, and not what interface they belong
to.

We could blindly add the nameservers across the interface files that are
generated, but this may leave them on interfaces that are not related to
the DNS entries, and if the user has multiple interfaces and disables
one, we no longer have any nameservers.

Instead we write to /etc/resolv.conf.cloud, then we symlink
/etc/resolv.conf to /etc/resolv.conf.cloud. When NetworkManager
encounters a symlink to a file that is not within it's run state
directory it will not modify the existing file that is on disk.

This allows our cloud-init resolv.conf to stay in tact.

d8ce950... by Bert JW Regeer

Catch ValueError and log it, rather than unwinding entire cloud-init

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
2index c53acf7..a5d6611 100644
3--- a/cloudinit/net/sysconfig.py
4+++ b/cloudinit/net/sysconfig.py
5@@ -20,8 +20,11 @@ import six
6 from cloudinit.distros.parsers import resolv_conf
7 from cloudinit import util
8
9+from cloudinit import log as logging
10+
11 from . import renderer
12
13+LOG = logging.getLogger(__name__)
14
15 def _make_header(sep='#'):
16 lines = [
17@@ -206,6 +209,10 @@ class Renderer(renderer.Renderer):
18 self.netrules_path = config.get(
19 'netrules_path', 'etc/udev/rules.d/70-persistent-net.rules')
20 self.dns_path = config.get('dns_path', 'etc/resolv.conf')
21+ self.dns_symlink = config.get('dns_symlink', True)
22+
23+ self.dns_write_path = config.get('dns_write_path', 'etc/resolv.conf.cloud') \
24+ if self.dns_symlink else self.dns_path
25
26 @classmethod
27 def _render_iface_shared(cls, iface, iface_cfg):
28@@ -335,7 +342,10 @@ class Renderer(renderer.Renderer):
29 if existing_dns_path and os.path.isfile(existing_dns_path):
30 content = resolv_conf.ResolvConf(util.load_file(existing_dns_path))
31 for nameserver in network_state.dns_nameservers:
32- content.add_nameserver(nameserver)
33+ try:
34+ content.add_nameserver(nameserver)
35+ except ValueError as e:
36+ LOG.debug('Unable to add nameserver %s: %s', nameserver, e)
37 for searchdomain in network_state.dns_searchdomains:
38 content.add_search_domain(searchdomain)
39 return "\n".join([_make_header(';'), str(content)])
40@@ -389,11 +399,22 @@ class Renderer(renderer.Renderer):
41 for path, data in self._render_sysconfig(base_sysconf_dir,
42 network_state).items():
43 util.write_file(path, data)
44- if self.dns_path:
45+ if self.dns_write_path:
46 dns_path = os.path.join(target, self.dns_path)
47+ dns_write_path = os.path.join(target, self.dns_write_path)
48 resolv_content = self._render_dns(network_state,
49 existing_dns_path=dns_path)
50- util.write_file(dns_path, resolv_content)
51+ util.write_file(dns_write_path, resolv_content)
52+
53+ if self.dns_symlink:
54+ if os.path.exists(dns_path) and not os.path.islink(dns_path):
55+ LOG.debug('Removing existing resolv.conf')
56+ os.unlink(dns_path)
57+
58+ if not os.path.exists(dns_path):
59+ LOG.debug('Adding symlink from %s to %s', dns_path, dns_write_path)
60+ os.symlink(dns_write_path, dns_path)
61+
62 if self.netrules_path:
63 netrules_content = self._render_persistent_net(network_state)
64 netrules_path = os.path.join(target, self.netrules_path)

Subscribers

People subscribed via source and target branches