diff options
author | harlowja <harlowja@virtualbox.rhel> | 2012-06-17 13:38:58 -0700 |
---|---|---|
committer | harlowja <harlowja@virtualbox.rhel> | 2012-06-17 13:38:58 -0700 |
commit | 4e5e0dadf07d52d92711ee674d98ee0ef523ea6a (patch) | |
tree | d3280402b0ada068c9e71aaa1243f310242b05da /cloudinit/distros | |
parent | a730063635236c4343f73e55ec491e28c4a2acfe (diff) | |
download | vyos-cloud-init-4e5e0dadf07d52d92711ee674d98ee0ef523ea6a.tar.gz vyos-cloud-init-4e5e0dadf07d52d92711ee674d98ee0ef523ea6a.zip |
Added a new rhel distro that does the following.
1. Translate (some of) the ubuntu network format to the rhel format (until we have python netcf active) and
then write the different ifcfg files to /etc/sysconfig/network-scripts as needed to bring the network online
2. Apply the hostname to the write /etc/sysconfig/network file (appending to it or adjusting that file if it exists)
3. Write to the /etc/sysconfig/clock file to adjust the timezone (or adjust that file instead of appending)
4. Runs yum commands (to match the packaging commands in the apt-get flavor)
5. Update the hostname in /etc/sysconfig/network (or a previous file) for the update hostname transform
Diffstat (limited to 'cloudinit/distros')
-rw-r--r-- | cloudinit/distros/rhel.py | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py new file mode 100644 index 00000000..85283b23 --- /dev/null +++ b/cloudinit/distros/rhel.py @@ -0,0 +1,294 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2012 Canonical Ltd. +# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. +# Copyright (C) 2012 Yahoo! Inc. +# +# Author: Scott Moser <scott.moser@canonical.com> +# Author: Juerg Haefliger <juerg.haefliger@hp.com> +# Author: Joshua Harlow <harlowja@yahoo-inc.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os + +from cloudinit import distros +from cloudinit import log as logging +from cloudinit import util + +LOG = logging.getLogger(__name__) + +NETWORK_FN_TPL = '/etc/sysconfig/network-scripts/ifcfg-%s' + + +class Distro(distros.Distro): + + def __init__(self, name, cfg, paths): + distros.Distro.__init__(self, name, cfg, paths) + + def install_packages(self, pkglist): + self.package_command('install', pkglist) + + def _write_network(self, settings): + # TODO 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... + for (dev, info) in entries.iteritems(): + lines = [] + lines.append("DEVICE=%s" % (dev)) + boot_proto = info.get('bootproto') + if boot_proto: + lines.append("BOOTPROTO=%s" % (boot_proto)) + net_mask = info.get('netmask') + if net_mask: + lines.append("NETMASK=%s" % (net_mask)) + addr = info.get('address') + if addr: + lines.append("IPADDR=%s" % (addr)) + if info.get('auto'): + lines.append("ONBOOT=yes") + else: + lines.append("ONBOOT=no") + gtway = info.get('gateway') + if gtway: + lines.append("GATEWAY=%s" % (gtway)) + bcast = info.get('broadcast') + if bcast: + lines.append("BROADCAST=%s" % (bcast)) + mac_addr = info.get('hwaddress') + if mac_addr: + lines.append("MACADDR=%s" % (mac_addr)) + contents = "\n".join(lines) + net_fn = NETWORK_FN_TPL % (dev) + util.write_file(net_fn, contents, 0644) + + def set_hostname(self, hostname): + self._write_hostname(hostname, "/etc/sysconfig/network") + LOG.debug("Setting hostname to %s", hostname) + util.subp(['hostname', hostname]) + + def _write_hostname(self, hostname, out_fn): + old_contents = [] + if os.path.isfile(out_fn): + old_contents = self._read_conf(out_fn) + # Update the 'HOSTNAME' if it exists instead of appending + new_contents = [] + adjusted = False + for entry in old_contents: + if not entry: + continue + if len(entry) == 1: + new_contents.append(entry[0]) + continue + (cmd, args) = entry + cmd_c = cmd.strip().lower() + if cmd_c == 'hostname': + args = "%s" % (hostname) + adjusted = True + new_contents.append("=".join([cmd, args])) + # Guess not found, append it + if not adjusted: + new_contents.append("HOSTNAME=%s" % (hostname)) + contents = "\n".join(new_contents) + util.write_file(out_fn, contents, 0644) + + def update_hostname(self, hostname, prev_file): + hostname_prev = self._read_hostname(prev_file) + hostname_in_sys = self._read_hostname("/etc/sysconfig/network") + 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)): + update_files.append("/etc/sysconfig/network") + 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: + LOG.debug("Setting hostname to %s", hostname) + util.subp(['hostname', hostname]) + + def _read_hostname(self, filename, default=None): + contents = self._read_conf(filename) + for c in contents: + if len(c) != 2: + continue + (cmd, args) = c + cmd_c = cmd.lower().strip() + if cmd_c == 'hostname': + args_c = args.strip() + if args_c: + return args_c + return default + + def _read_conf(self, filename): + contents = util.load_file(filename, quiet=True) + conf_lines = [] + for line in contents.splitlines(): + c_line = line.strip() + if not c_line or c_line.startswith("#"): + conf_lines.append([line]) + continue + # Handle inline comments + c_pos = c_line.find("#") + if c_pos != -1: + c_line = c_line[0:c_pos].strip() + if not c_line: + conf_lines.append([line]) + continue + # Format should be CMD=ARG1 ARG2... + pieces = c_line.split("=", 1) + if not pieces or len(pieces) == 1: + conf_lines.append([line]) + continue + (cmd, args) = pieces + cmd = cmd.strip() + conf_lines.append([cmd, args]) + return conf_lines + + def set_timezone(self, tz): + tz_file = os.path.join("/usr/share/zoneinfo", tz) + if not os.path.isfile(tz_file): + raise Exception(("Invalid timezone %s," + " no file found at %s") % (tz, tz_file)) + # Adjust the sysconfig clock zone setting + old_contents = self._read_conf("/etc/sysconfig/clock") + new_contents = [] + zone_added = False + # Update the 'ZONE' if it exists instead of appending + for entry in old_contents: + if not entry: + continue + if len(entry) == 1: + new_contents.append(entry[0]) + continue + (cmd, args) = entry + cmd_c = cmd.lower().strip() + if cmd_c == 'zone': + args = '"%s"' % (tz) + zone_added = True + new_contents.append("=".join([cmd, args])) + # Guess not found, append it + if not zone_added: + new_contents.append('ZONE="%s"' % (tz)) + tz_contents = "\n".join(new_contents) + util.write_file("/etc/sysconfig/clock", tz_contents) + # This ensures that the correct tz will be used for the system + util.copy(tz_file, "/etc/localtime") + + def package_command(self, command, args=None): + cmd = ['yum'] + # If enabled, then yum will be tolerant of errors on the command line + # with regard to packages. + # For example: if you request to install foo, bar and baz and baz is + # installed; yum won't error out complaining that baz is already + # installed. + cmd.append("-t") + # Determines whether or not yum prompts for confirmation + # of critical actions. We don't want to prompt... + cmd.append("-y") + cmd.append(command) + if args: + cmd.extend(args) + # Allow the output of this to flow outwards (ie not be captured) + util.subp(cmd, capture=False) + + +# This is a util function to translate a ubuntu /etc/network/interfaces 'blob' +# to a rhel equiv. that can then be written to /etc/sysconfig/network-scripts/ +# TODO remove when we have python-netcf active... +def translate_network(settings): + # Get the standard cmd, args from the ubuntu format + entries = [] + for line in settings.splitlines(): + line = line.strip() + if not line or line.startswith("#"): + continue + split_up = line.split(None, 1) + if len(split_up) <= 1: + continue + entries.append(split_up) + # Figure out where each iface section is + ifaces = [] + consume = {} + for (cmd, args) in entries: + if cmd == 'iface': + if consume: + ifaces.append(consume) + consume = {} + consume[cmd] = args + else: + consume[cmd] = args + # Check if anything left over to consume + absorb = False + for (cmd, args) in consume.iteritems(): + if cmd == 'iface': + absorb = True + if absorb: + ifaces.append(consume) + # Now translate + real_ifaces = {} + for info in ifaces: + if 'iface' not in info: + continue + iface_details = info['iface'].split(None) + dev_name = None + if len(iface_details) >= 1: + dev = iface_details[0].strip().lower() + if dev: + dev_name = dev + if not dev_name: + continue + iface_info = {} + if len(iface_details) >= 3: + proto_type = iface_details[2].strip().lower() + # Seems like this can be 'loopback' which we don't + # really care about + if proto_type in ['dhcp', 'static']: + iface_info['bootproto'] = proto_type + # These can just be copied over + for k in ['netmask', 'address', 'gateway', 'broadcast']: + if k in info: + val = info[k].strip().lower() + if val: + iface_info[k] = val + # Is any mac address spoofing going on?? + if 'hwaddress' in info: + hw_info = info['hwaddress'].lower().strip() + hw_split = hw_info.split(None, 1) + if len(hw_split) == 2 and hw_split[0].startswith('ether'): + hw_addr = hw_split[1] + if hw_addr: + iface_info['hwaddress'] = hw_addr + real_ifaces[dev_name] = iface_info + # Check for those that should be started on boot via 'auto' + for (cmd, args) in entries: + if cmd == 'auto': + # Seems like auto can be like 'auto eth0 eth0:1' so just get the + # first part out as the device name + args = args.split(None) + if not args: + continue + dev_name = args[0].strip().lower() + if dev_name in real_ifaces: + real_ifaces[dev_name]['auto'] = True + return real_ifaces |