summaryrefslogtreecommitdiff
path: root/cloudinit/distros/rhel.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/distros/rhel.py')
-rw-r--r--cloudinit/distros/rhel.py287
1 files changed, 133 insertions, 154 deletions
diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py
index d81ee5fb..bc0877d5 100644
--- a/cloudinit/distros/rhel.py
+++ b/cloudinit/distros/rhel.py
@@ -23,6 +23,10 @@
import os
from cloudinit import distros
+
+from cloudinit.distros.parsers.resolv_conf import ResolvConf
+from cloudinit.distros.parsers.sys_conf import SysConf
+
from cloudinit import helpers
from cloudinit import log as logging
from cloudinit import util
@@ -31,32 +35,24 @@ from cloudinit.settings import PER_INSTANCE
LOG = logging.getLogger(__name__)
-NETWORK_FN_TPL = '/etc/sysconfig/network-scripts/ifcfg-%s'
-
-# See: http://tiny.cc/6r99fw
-# For what alot of these files that are being written
-# are and the format of them
-
-# This library is used to parse/write
-# out the various sysconfig files edited
-#
-# It has to be slightly modified though
-# to ensure that all values are quoted
-# since these configs are usually sourced into
-# bash scripts...
-from configobj import ConfigObj
-# See: http://tiny.cc/oezbgw
-D_QUOTE_CHARS = {
- "\"": "\\\"",
- "(": "\\(",
- ")": "\\)",
- "$": '\$',
- '`': '\`',
-}
+def _make_sysconfig_bool(val):
+ if val:
+ return 'yes'
+ else:
+ return 'no'
class Distro(distros.Distro):
+ # See: http://tiny.cc/6r99fw
+ clock_conf_fn = "/etc/sysconfig/clock"
+ locale_conf_fn = '/etc/sysconfig/i18n'
+ network_conf_fn = "/etc/sysconfig/network"
+ hostname_conf_fn = "/etc/sysconfig/network"
+ network_script_tpl = '/etc/sysconfig/network-scripts/ifcfg-%s'
+ resolve_conf_fn = "/etc/resolv.conf"
+ tz_local_fn = "/etc/localtime"
+ tz_zone_dir = "/usr/share/zoneinfo"
def __init__(self, name, cfg, paths):
distros.Distro.__init__(self, name, cfg, paths)
@@ -68,95 +64,110 @@ class Distro(distros.Distro):
def install_packages(self, pkglist):
self.package_command('install', pkglist)
+ def _adjust_resolve(self, dns_servers, search_servers):
+ try:
+ r_conf = ResolvConf(util.load_file(self.resolve_conf_fn))
+ r_conf.parse()
+ except IOError:
+ util.logexc(LOG,
+ "Failed at parsing %s reverting to an empty instance",
+ self.resolve_conf_fn)
+ r_conf = ResolvConf('')
+ r_conf.parse()
+ if dns_servers:
+ for s in dns_servers:
+ try:
+ r_conf.add_nameserver(s)
+ except ValueError:
+ util.logexc(LOG, "Failed at adding nameserver %s", s)
+ if search_servers:
+ for s in search_servers:
+ try:
+ r_conf.add_search_domain(s)
+ except ValueError:
+ util.logexc(LOG, "Failed at adding search domain %s", s)
+ util.write_file(self.resolve_conf_fn, str(r_conf), 0644)
+
def _write_network(self, settings):
# TODO(harlowja) fix this... since this is the ubuntu format
entries = translate_network(settings)
LOG.debug("Translated ubuntu style network settings %s into %s",
settings, entries)
# Make the intermediate format as the rhel format...
+ nameservers = []
+ searchservers = []
+ dev_names = entries.keys()
for (dev, info) in entries.iteritems():
- net_fn = NETWORK_FN_TPL % (dev)
- net_ro_fn = self._paths.join(True, net_fn)
- (prev_exist, net_cfg) = self._read_conf(net_ro_fn)
- net_cfg['DEVICE'] = dev
- boot_proto = info.get('bootproto')
- if boot_proto:
- net_cfg['BOOTPROTO'] = boot_proto
- net_mask = info.get('netmask')
- if net_mask:
- net_cfg["NETMASK"] = net_mask
- addr = info.get('address')
- if addr:
- net_cfg["IPADDR"] = addr
- if info.get('auto'):
- net_cfg['ONBOOT'] = 'yes'
- else:
- net_cfg['ONBOOT'] = 'no'
- gtway = info.get('gateway')
- if gtway:
- net_cfg["GATEWAY"] = gtway
- bcast = info.get('broadcast')
- if bcast:
- net_cfg["BROADCAST"] = bcast
- mac_addr = info.get('hwaddress')
- if mac_addr:
- net_cfg["MACADDR"] = mac_addr
- lines = net_cfg.write()
- if not prev_exist:
- lines.insert(0, '# Created by cloud-init')
- w_contents = "\n".join(lines)
- net_rw_fn = self._paths.join(False, net_fn)
- util.write_file(net_rw_fn, w_contents, 0644)
+ net_fn = self.network_script_tpl % (dev)
+ net_cfg = {
+ 'DEVICE': dev,
+ 'NETMASK': info.get('netmask'),
+ 'IPADDR': info.get('address'),
+ 'BOOTPROTO': info.get('bootproto'),
+ 'GATEWAY': info.get('gateway'),
+ 'BROADCAST': info.get('broadcast'),
+ 'MACADDR': info.get('hwaddress'),
+ 'ONBOOT': _make_sysconfig_bool(info.get('auto')),
+ }
+ self._update_sysconfig_file(net_fn, net_cfg)
+ if 'dns-nameservers' in info:
+ nameservers.extend(info['dns-nameservers'])
+ if 'dns-search' in info:
+ searchservers.extend(info['dns-search'])
+ if nameservers or searchservers:
+ self._adjust_resolve(nameservers, searchservers)
+ if dev_names:
+ net_cfg = {
+ 'NETWORKING': _make_sysconfig_bool(True),
+ }
+ self._update_sysconfig_file(self.network_conf_fn, net_cfg)
+ return dev_names
- def set_hostname(self, hostname):
- out_fn = self._paths.join(False, '/etc/sysconfig/network')
- self._write_hostname(hostname, out_fn)
- if out_fn == '/etc/sysconfig/network':
- # Only do this if we are running in non-adjusted root mode
- LOG.debug("Setting hostname to %s", hostname)
- util.subp(['hostname', hostname])
+ def _update_sysconfig_file(self, fn, adjustments, allow_empty=False):
+ if not adjustments:
+ return
+ (exists, contents) = self._read_conf(fn)
+ updated_am = 0
+ for (k, v) in adjustments.items():
+ if v is None:
+ continue
+ v = str(v)
+ if len(v) == 0 and not allow_empty:
+ continue
+ contents[k] = v
+ updated_am += 1
+ if updated_am:
+ lines = [
+ str(contents),
+ ]
+ if not exists:
+ lines.insert(0, util.make_header())
+ util.write_file(fn, "\n".join(lines), 0644)
def apply_locale(self, locale, out_fn=None):
if not out_fn:
- out_fn = self._paths.join(False, '/etc/sysconfig/i18n')
- ro_fn = self._paths.join(True, '/etc/sysconfig/i18n')
- (_exists, contents) = self._read_conf(ro_fn)
- contents['LANG'] = locale
- w_contents = "\n".join(contents.write())
- util.write_file(out_fn, w_contents, 0644)
+ out_fn = self.locale_conf_fn
+ locale_cfg = {
+ 'LANG': locale,
+ }
+ self._update_sysconfig_file(out_fn, locale_cfg)
def _write_hostname(self, hostname, out_fn):
- (_exists, contents) = self._read_conf(out_fn)
- contents['HOSTNAME'] = hostname
- w_contents = "\n".join(contents.write())
- util.write_file(out_fn, w_contents, 0644)
+ host_cfg = {
+ 'HOSTNAME': hostname,
+ }
+ self._update_sysconfig_file(out_fn, host_cfg)
+
+ def _select_hostname(self, hostname, fqdn):
+ # See: http://bit.ly/TwitgL
+ # Should be fqdn if we can use it
+ if fqdn:
+ return fqdn
+ return hostname
- def update_hostname(self, hostname, prev_file):
- hostname_prev = self._read_hostname(prev_file)
- read_fn = self._paths.join(True, "/etc/sysconfig/network")
- hostname_in_sys = self._read_hostname(read_fn)
- update_files = []
- if not hostname_prev or hostname_prev != hostname:
- update_files.append(prev_file)
- if (not hostname_in_sys or
- (hostname_in_sys == hostname_prev
- and hostname_in_sys != hostname)):
- write_fn = self._paths.join(False, "/etc/sysconfig/network")
- update_files.append(write_fn)
- for fn in update_files:
- try:
- self._write_hostname(hostname, fn)
- except:
- util.logexc(LOG, "Failed to write hostname %s to %s",
- hostname, fn)
- if (hostname_in_sys and hostname_prev and
- hostname_in_sys != hostname_prev):
- LOG.debug(("%s differs from /etc/sysconfig/network."
- " Assuming user maintained hostname."), prev_file)
- if "/etc/sysconfig/network" in update_files:
- # Only do this if we are running in non-adjusted root mode
- LOG.debug("Setting hostname to %s", hostname)
- util.subp(['hostname', hostname])
+ def _read_system_hostname(self):
+ return (self.network_conf_fn,
+ self._read_hostname(self.network_conf_fn))
def _read_hostname(self, filename, default=None):
(_exists, contents) = self._read_conf(filename)
@@ -167,27 +178,34 @@ class Distro(distros.Distro):
def _read_conf(self, fn):
exists = False
- if os.path.isfile(fn):
+ try:
contents = util.load_file(fn).splitlines()
exists = True
- else:
+ except IOError:
contents = []
- return (exists, QuotingConfigObj(contents))
+ return (exists,
+ SysConf(contents))
+
+ def _bring_up_interfaces(self, device_names):
+ if device_names and 'all' in device_names:
+ raise RuntimeError(('Distro %s can not translate '
+ 'the device name "all"') % (self.name))
+ return distros.Distro._bring_up_interfaces(self, device_names)
def set_timezone(self, tz):
- tz_file = os.path.join("/usr/share/zoneinfo", tz)
+ # TODO(harlowja): move this code into
+ # the parent distro...
+ tz_file = os.path.join(self.tz_zone_dir, str(tz))
if not os.path.isfile(tz_file):
raise RuntimeError(("Invalid timezone %s,"
" no file found at %s") % (tz, tz_file))
# Adjust the sysconfig clock zone setting
- read_fn = self._paths.join(True, "/etc/sysconfig/clock")
- (_exists, contents) = self._read_conf(read_fn)
- contents['ZONE'] = tz
- tz_contents = "\n".join(contents.write())
- write_fn = self._paths.join(False, "/etc/sysconfig/clock")
- util.write_file(write_fn, tz_contents)
+ clock_cfg = {
+ 'ZONE': str(tz),
+ }
+ self._update_sysconfig_file(self.clock_conf_fn, clock_cfg)
# This ensures that the correct tz will be used for the system
- util.copy(tz_file, self._paths.join(False, "/etc/localtime"))
+ util.copy(tz_file, self.tz_local_fn)
def package_command(self, command, args=None):
cmd = ['yum']
@@ -208,52 +226,7 @@ class Distro(distros.Distro):
def update_package_sources(self):
self._runner.run("update-sources", self.package_command,
- ["update"], freq=PER_INSTANCE)
-
-
-# This class helps adjust the configobj
-# writing to ensure that when writing a k/v
-# on a line, that they are properly quoted
-# and have no spaces between the '=' sign.
-# - This is mainly due to the fact that
-# the sysconfig scripts are often sourced
-# directly into bash/shell scripts so ensure
-# that it works for those types of use cases.
-class QuotingConfigObj(ConfigObj):
- def __init__(self, lines):
- ConfigObj.__init__(self, lines,
- interpolation=False,
- write_empty_values=True)
-
- def _quote_posix(self, text):
- if not text:
- return ''
- for (k, v) in D_QUOTE_CHARS.iteritems():
- text = text.replace(k, v)
- return '"%s"' % (text)
-
- def _quote_special(self, text):
- if text.lower() in ['yes', 'no', 'true', 'false']:
- return text
- else:
- return self._quote_posix(text)
-
- def _write_line(self, indent_string, entry, this_entry, comment):
- # Ensure it is formatted fine for
- # how these sysconfig scripts are used
- val = self._decode_element(self._quote(this_entry))
- # Single quoted strings should
- # always work.
- if not val.startswith("'"):
- # Perform any special quoting
- val = self._quote_special(val)
- key = self._decode_element(self._quote(entry, multiline=False))
- cmnt = self._decode_element(comment)
- return '%s%s%s%s%s' % (indent_string,
- key,
- "=",
- val,
- cmnt)
+ ["makecache"], freq=PER_INSTANCE)
# This is a util function to translate a ubuntu /etc/network/interfaces 'blob'
@@ -314,6 +287,12 @@ def translate_network(settings):
val = info[k].strip().lower()
if val:
iface_info[k] = val
+ # Name server info provided??
+ if 'dns-nameservers' in info:
+ iface_info['dns-nameservers'] = info['dns-nameservers'].split()
+ # Name server search info provided??
+ if 'dns-search' in info:
+ iface_info['dns-search'] = info['dns-search'].split()
# Is any mac address spoofing going on??
if 'hwaddress' in info:
hw_info = info['hwaddress'].lower().strip()