summaryrefslogtreecommitdiff
path: root/cloudinit/distros
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/distros')
-rw-r--r--cloudinit/distros/__init__.py163
-rw-r--r--cloudinit/distros/debian.py149
-rw-r--r--cloudinit/distros/fedora.py31
-rw-r--r--cloudinit/distros/rhel.py337
-rw-r--r--cloudinit/distros/ubuntu.py31
5 files changed, 711 insertions, 0 deletions
diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
new file mode 100644
index 00000000..da4d0180
--- /dev/null
+++ b/cloudinit/distros/__init__.py
@@ -0,0 +1,163 @@
+# 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/>.
+
+from StringIO import StringIO
+
+import abc
+
+from cloudinit import importer
+from cloudinit import log as logging
+from cloudinit import util
+
+# TODO: Make this via config??
+IFACE_ACTIONS = {
+ 'up': ['ifup', '--all'],
+ 'down': ['ifdown', '--all'],
+}
+
+LOG = logging.getLogger(__name__)
+
+
+class Distro(object):
+
+ __metaclass__ = abc.ABCMeta
+
+ def __init__(self, name, cfg, paths):
+ self._paths = paths
+ self._cfg = cfg
+ self.name = name
+
+ @abc.abstractmethod
+ def install_packages(self, pkglist):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def _write_network(self, settings):
+ # In the future use the http://fedorahosted.org/netcf/
+ # to write this blob out in a distro format
+ raise NotImplementedError()
+
+ def get_option(self, opt_name, default=None):
+ return self._cfg.get(opt_name, default)
+
+ @abc.abstractmethod
+ def set_hostname(self, hostname):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def update_hostname(self, hostname, prev_hostname_fn):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def package_command(self, cmd, args=None):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def update_package_sources(self):
+ raise NotImplementedError()
+
+ def get_package_mirror(self):
+ return self.get_option('package_mirror')
+
+ def apply_network(self, settings, bring_up=True):
+ # Write it out
+ self._write_network(settings)
+ # Now try to bring them up
+ if bring_up:
+ return self._interface_action('up')
+ return False
+
+ @abc.abstractmethod
+ def apply_locale(self, locale, out_fn=None):
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def set_timezone(self, tz):
+ raise NotImplementedError()
+
+ def _get_localhost_ip(self):
+ 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())
+ 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)
+ need_change = False
+ need_write = True
+ new_etchosts.write("%s\n" % (line))
+ 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)
+
+ def _interface_action(self, action):
+ if action not in IFACE_ACTIONS:
+ raise NotImplementedError("Unknown interface action %s" % (action))
+ cmd = IFACE_ACTIONS[action]
+ try:
+ LOG.debug("Attempting to run %s interface action using command %s",
+ action, cmd)
+ (_out, err) = util.subp(cmd)
+ if len(err):
+ LOG.warn("Running %s resulted in stderr output: %s", cmd, err)
+ return True
+ except util.ProcessExecutionError:
+ util.logexc(LOG, "Running interface command %s failed", cmd)
+ return False
+
+
+def fetch(name):
+ locs = importer.find_module(name,
+ ['', __name__],
+ ['Distro'])
+ if not locs:
+ raise ImportError("No distribution found for distro %s"
+ % (name))
+ mod = importer.import_module(locs[0])
+ cls = getattr(mod, 'Distro')
+ return cls
diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py
new file mode 100644
index 00000000..3247d7ce
--- /dev/null
+++ b/cloudinit/distros/debian.py
@@ -0,0 +1,149 @@
+# 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 helpers
+from cloudinit import log as logging
+from cloudinit import util
+
+from cloudinit.settings import PER_INSTANCE
+
+LOG = logging.getLogger(__name__)
+
+
+class Distro(distros.Distro):
+
+ def __init__(self, name, cfg, paths):
+ distros.Distro.__init__(self, name, cfg, paths)
+ # This will be used to restrict certain
+ # calls from repeatly happening (when they
+ # should only happen say once per instance...)
+ self._runner = helpers.Runners(paths)
+
+ def apply_locale(self, locale, out_fn=None):
+ if not out_fn:
+ out_fn = self._paths.join(False, '/etc/default/locale')
+ util.subp(['locale-gen', locale], capture=False)
+ util.subp(['update-locale', locale], capture=False)
+ contents = [
+ "# Created by cloud-init",
+ 'LANG="%s"' % (locale),
+ ]
+ util.write_file(out_fn, "\n".join(contents))
+
+ def install_packages(self, pkglist):
+ self.update_package_sources()
+ self.package_command('install', pkglist)
+
+ def _write_network(self, settings):
+ net_fn = self._paths.join(False, "/etc/network/interfaces")
+ util.write_file(net_fn, settings)
+
+ def set_hostname(self, hostname):
+ out_fn = self._paths.join(False, "/etc/hostname")
+ self._write_hostname(hostname, out_fn)
+ if out_fn == '/etc/hostname':
+ # Only do this if we are running in non-adjusted root mode
+ LOG.debug("Setting hostname to %s", hostname)
+ util.subp(['hostname', hostname])
+
+ def _write_hostname(self, hostname, out_fn):
+ lines = []
+ lines.append("# Created by cloud-init")
+ lines.append(str(hostname))
+ contents = "\n".join(lines)
+ util.write_file(out_fn, contents, 0644)
+
+ def update_hostname(self, hostname, prev_fn):
+ hostname_prev = self._read_hostname(prev_fn)
+ read_fn = self._paths.join(True, "/etc/hostname")
+ hostname_in_etc = self._read_hostname(read_fn)
+ update_files = []
+ if not hostname_prev or hostname_prev != hostname:
+ update_files.append(prev_fn)
+ if (not hostname_in_etc or
+ (hostname_in_etc == hostname_prev and
+ hostname_in_etc != hostname)):
+ write_fn = self._paths.join(False, "/etc/hostname")
+ 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_etc and hostname_prev and
+ hostname_in_etc != hostname_prev):
+ LOG.debug(("%s differs from /etc/hostname."
+ " Assuming user maintained hostname."), prev_fn)
+ if "/etc/hostname" 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_hostname(self, filename, default=None):
+ contents = util.load_file(filename, quiet=True)
+ for line in contents.splitlines():
+ c_pos = line.find("#")
+ # Handle inline comments
+ if c_pos != -1:
+ line = line[0:c_pos]
+ line_c = line.strip()
+ if line_c:
+ return line_c
+ return default
+
+ def _get_localhost_ip(self):
+ # Note: http://www.leonardoborda.com/blog/127-0-1-1-ubuntu-debian/
+ return "127.0.1.1"
+
+ def set_timezone(self, tz):
+ tz_file = os.path.join("/usr/share/zoneinfo", tz)
+ if not os.path.isfile(tz_file):
+ raise RuntimeError(("Invalid timezone %s,"
+ " no file found at %s") % (tz, tz_file))
+ tz_lines = [
+ "# Created by cloud-init",
+ str(tz),
+ ]
+ tz_contents = "\n".join(tz_lines)
+ tz_fn = self._paths.join(False, "/etc/timezone")
+ util.write_file(tz_fn, tz_contents)
+ util.copy(tz_file, self._paths.join(False, "/etc/localtime"))
+
+ def package_command(self, command, args=None):
+ e = os.environ.copy()
+ # See: http://tiny.cc/kg91fw
+ # Or: http://tiny.cc/mh91fw
+ e['DEBIAN_FRONTEND'] = 'noninteractive'
+ cmd = ['apt-get', '--option', 'Dpkg::Options::=--force-confold',
+ '--assume-yes', '--quiet', command]
+ if args:
+ cmd.extend(args)
+ # Allow the output of this to flow outwards (ie not be captured)
+ util.subp(cmd, env=e, capture=False)
+
+ def update_package_sources(self):
+ self._runner.run("update-sources", self.package_command,
+ ["update"], freq=PER_INSTANCE)
diff --git a/cloudinit/distros/fedora.py b/cloudinit/distros/fedora.py
new file mode 100644
index 00000000..c777845d
--- /dev/null
+++ b/cloudinit/distros/fedora.py
@@ -0,0 +1,31 @@
+# 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/>.
+
+from cloudinit.distros import rhel
+
+from cloudinit import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+class Distro(rhel.Distro):
+ pass
diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py
new file mode 100644
index 00000000..7fa69f03
--- /dev/null
+++ b/cloudinit/distros/rhel.py
@@ -0,0 +1,337 @@
+# 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 helpers
+from cloudinit import log as logging
+from cloudinit import util
+
+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 = {
+ "\"": "\\\"",
+ "(": "\\(",
+ ")": "\\)",
+ "$": '\$',
+ '`': '\`',
+}
+
+
+class Distro(distros.Distro):
+
+ def __init__(self, name, cfg, paths):
+ distros.Distro.__init__(self, name, cfg, paths)
+ # This will be used to restrict certain
+ # calls from repeatly happening (when they
+ # should only happen say once per instance...)
+ self._runner = helpers.Runners(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():
+ 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)
+
+ 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 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)
+
+ 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)
+
+ 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_hostname(self, filename, default=None):
+ (_exists, contents) = self._read_conf(filename)
+ if 'HOSTNAME' in contents:
+ return contents['HOSTNAME']
+ else:
+ return default
+
+ def _read_conf(self, fn):
+ exists = False
+ if os.path.isfile(fn):
+ contents = util.load_file(fn).splitlines()
+ exists = True
+ else:
+ contents = []
+ return (exists, QuotingConfigObj(contents))
+
+ def set_timezone(self, tz):
+ tz_file = os.path.join("/usr/share/zoneinfo", 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)
+ # This ensures that the correct tz will be used for the system
+ util.copy(tz_file, self._paths.join(False, "/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)
+
+ 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)
+
+
+# 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
diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py
new file mode 100644
index 00000000..77c2aff4
--- /dev/null
+++ b/cloudinit/distros/ubuntu.py
@@ -0,0 +1,31 @@
+# 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/>.
+
+from cloudinit.distros import debian
+
+from cloudinit import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+class Distro(debian.Distro):
+ pass