summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cloudinit/distros/__init__.py69
-rw-r--r--cloudinit/distros/helpers.py96
2 files changed, 118 insertions, 47 deletions
diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
index 3e9d934d..bf4317f1 100644
--- a/cloudinit/distros/__init__.py
+++ b/cloudinit/distros/__init__.py
@@ -34,6 +34,8 @@ from cloudinit import log as logging
from cloudinit import ssh_util
from cloudinit import util
+from cloudinit.distros import helpers
+
# TODO(harlowja): Make this via config??
IFACE_ACTIONS = {
'up': ['ifup', '--all'],
@@ -152,42 +154,43 @@ class Distro(object):
return "127.0.0.1"
def update_etc_hosts(self, hostname, fqdn):
- # Format defined at
- # http://unixhelp.ed.ac.uk/CGI/man-cgi?hosts
- header = "# Added by cloud-init"
- real_header = "%s on %s" % (header, util.time_rfc2822())
+ header = ''
+ if os.path.exists('/etc/hosts'):
+ eh = helpers.HostsConf(util.load_file("/etc/hosts"))
+ else:
+ eh = helpers.HostsConf('')
+ header = "# Added by cloud-init"
+ header = "%s on %s" % (header, util.time_rfc2822())
local_ip = self._get_localhost_ip()
- hosts_line = "%s\t%s %s" % (local_ip, fqdn, hostname)
- new_etchosts = StringIO()
- need_write = False
- need_change = True
- hosts_ro_fn = self._paths.join(True, "/etc/hosts")
- for line in util.load_file(hosts_ro_fn).splitlines():
- if line.strip().startswith(header):
- continue
- if not line.strip() or line.strip().startswith("#"):
- new_etchosts.write("%s\n" % (line))
- continue
- split_line = [s.strip() for s in line.split()]
- if len(split_line) < 2:
- new_etchosts.write("%s\n" % (line))
- continue
- (ip, hosts) = split_line[0], split_line[1:]
- if ip == local_ip:
- if sorted([hostname, fqdn]) == sorted(hosts):
- need_change = False
- if need_change:
- line = "%s\n%s" % (real_header, hosts_line)
+ prev_info = eh.get_entry(local_ip)
+ need_change = False
+ if not prev_info:
+ eh.add_entry(local_ip, fqdn, hostname)
+ need_change = True
+ else:
+ need_change = True
+ for entry in prev_info:
+ if sorted(entry) == sorted([fqdn, hostname]):
+ # Exists already, leave it be
need_change = False
- need_write = True
- new_etchosts.write("%s\n" % (line))
+ break
+ if need_change:
+ # Doesn't exist, change the first
+ # entry to be this entry
+ new_entries = list(prev_info)
+ new_entries[0] = [fqdn, hostname]
+ eh.del_entries(local_ip)
+ for entry in new_entries:
+ if len(entry) == 1:
+ eh.add_entry(local_ip, entry[0])
+ elif len(entry) >= 2:
+ eh.add_entry(local_ip, *entry)
if need_change:
- new_etchosts.write("%s\n%s\n" % (real_header, hosts_line))
- need_write = True
- if need_write:
- contents = new_etchosts.getvalue()
- util.write_file(self._paths.join(False, "/etc/hosts"),
- contents, mode=0644)
+ contents = StringIO()
+ if header:
+ contents.write("%s\n" % (header))
+ contents.write("%s\n" % (eh))
+ util.write_file("/etc/hosts", contents.getvalue(), mode=0644)
def _interface_action(self, action):
if action not in IFACE_ACTIONS:
diff --git a/cloudinit/distros/helpers.py b/cloudinit/distros/helpers.py
index 251d5051..916935eb 100644
--- a/cloudinit/distros/helpers.py
+++ b/cloudinit/distros/helpers.py
@@ -23,6 +23,87 @@ from StringIO import StringIO
from cloudinit import util
+def _chop_comment(text, comment_chars):
+ comment_locations = [text.find(c) for c in comment_chars]
+ comment_locations = [c for c in comment_locations if c != -1]
+ if not comment_locations:
+ return (text, '')
+ min_comment = min(comment_locations)
+ before_comment = text[0:min_comment]
+ comment = text[min_comment:]
+ return (before_comment, comment)
+
+
+# See: man hosts
+# or http://unixhelp.ed.ac.uk/CGI/man-cgi?hosts
+class HostsConf(object):
+ def __init__(self, text):
+ self._text = text
+ self._contents = None
+
+ def parse(self):
+ if self._contents is None:
+ self._contents = self._parse(self._text)
+
+ def get_entry(self, ip):
+ self.parse()
+ options = []
+ for (line_type, components) in self._contents:
+ if line_type == 'option':
+ (pieces, _tail) = components
+ if len(pieces) and pieces[0] == ip:
+ options.append(pieces[1:])
+ return options
+
+ def del_entries(self, ip):
+ self.parse()
+ n_entries = []
+ for (line_type, components) in self._contents:
+ if line_type != 'option':
+ n_entries.append((line_type, components))
+ continue
+ else:
+ (pieces, _tail) = components
+ if len(pieces) and pieces[0] == ip:
+ pass
+ elif len(pieces):
+ n_entries.append((line_type, list(components)))
+ self._contents = n_entries
+
+ def add_entry(self, ip, canonical_hostname, *aliases):
+ self.parse()
+ self._contents.append(('option',
+ ([ip, canonical_hostname] + list(aliases), '')))
+
+ def _parse(self, contents):
+ entries = []
+ for line in contents.splitlines():
+ if not len(line.strip()):
+ entries.append(('blank', [line]))
+ continue
+ (head, tail) = _chop_comment(line.strip(), '#')
+ if not len(head):
+ entries.append(('all_comment', [line]))
+ continue
+ entries.append(('option', [head.split(None), tail]))
+ return entries
+
+ def __str__(self):
+ self.parse()
+ contents = StringIO()
+ for (line_type, components) in self._contents:
+ if line_type == 'blank':
+ contents.write("%s\n")
+ elif line_type == 'all_comment':
+ contents.write("%s\n" % (components[0]))
+ elif line_type == 'option':
+ (pieces, tail) = components
+ pieces = [str(p) for p in pieces]
+ pieces = "\t".join(pieces)
+ contents.write("%s%s\n" % (pieces, tail))
+ return contents.getvalue()
+
+
# See: man resolv.conf
class ResolvConf(object):
def __init__(self, text):
@@ -151,20 +232,7 @@ class ResolvConf(object):
if not sline:
entries.append(('blank', [line]))
continue
- comment_s_loc = sline.find(";")
- comment_h_loc = sline.find("#")
- comment_loc = -1
- if comment_s_loc != -1 and comment_h_loc != -1:
- comment_loc = min(comment_h_loc, comment_s_loc)
- elif comment_s_loc != -1:
- comment_loc = comment_s_loc
- elif comment_h_loc != -1:
- comment_loc = comment_h_loc
- head = line
- tail = None
- if comment_loc != -1:
- head = line[:comment_loc]
- tail = line[comment_loc:]
+ (head, tail) = _chop_comment(line, ';#')
if not len(head.strip()):
entries.append(('all_comment', [line]))
continue