summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cloudinit/config/cc_resizefs.py2
-rw-r--r--cloudinit/distros/freebsd.py88
-rw-r--r--config/cloud.freebsd.cfg88
-rwxr-xr-xsetup.py52
-rwxr-xr-xsysvinit/freebsd/cloudconfig14
-rwxr-xr-xsysvinit/freebsd/cloudfinal14
-rwxr-xr-xsysvinit/freebsd/cloudinit14
-rwxr-xr-xsysvinit/freebsd/cloudinitlocal14
-rw-r--r--tests/unittests/test_distros/test_netconfig.py36
-rwxr-xr-xtools/build-on-freebsd44
10 files changed, 315 insertions, 51 deletions
diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py
index be406034..e290efe0 100644
--- a/cloudinit/config/cc_resizefs.py
+++ b/cloudinit/config/cc_resizefs.py
@@ -41,7 +41,7 @@ def _resize_xfs(mount_point, devpth): # pylint: disable=W0613
def _resize_ufs(mount_point, devpth): # pylint: disable=W0613
- return ('growfs', devpth)
+ return ('growfs', '-y', devpth)
# Do not use a dictionary as these commands should be able to be used
# for multiple filesystem types if possible, e.g. one command for
diff --git a/cloudinit/distros/freebsd.py b/cloudinit/distros/freebsd.py
index d98f9578..b4d841f8 100644
--- a/cloudinit/distros/freebsd.py
+++ b/cloudinit/distros/freebsd.py
@@ -26,6 +26,9 @@ from cloudinit import log as logging
from cloudinit import ssh_util
from cloudinit import util
+from cloudinit.distros import net_util
+from cloudinit.distros.parsers.resolv_conf import ResolvConf
+
LOG = logging.getLogger(__name__)
@@ -33,6 +36,7 @@ class Distro(distros.Distro):
rc_conf_fn = "/etc/rc.conf"
login_conf_fn = '/etc/login.conf'
login_conf_fn_bak = '/etc/login.conf.orig'
+ resolv_conf_fn = '/etc/resolv.conf'
def __init__(self, name, cfg, paths):
distros.Distro.__init__(self, name, cfg, paths)
@@ -44,30 +48,39 @@ class Distro(distros.Distro):
# Updates a key in /etc/rc.conf.
def updatercconf(self, key, value):
- LOG.debug("updatercconf: %s => %s", key, value)
+ LOG.debug("Checking %s for: %s = %s", self.rc_conf_fn, key, value)
conf = self.loadrcconf()
config_changed = False
for item in conf:
if item == key and conf[item] != value:
conf[item] = value
- LOG.debug("[rc.conf]: Value %s for key %s needs to be changed",
- value, key)
+ LOG.debug("Changing key in %s: %s = %s", self.rc_conf_fn, key,
+ value)
config_changed = True
if config_changed:
- LOG.debug("Writing new %s file", self.rc_conf_fn)
+ LOG.info("Writing %s", self.rc_conf_fn)
buf = StringIO()
for keyval in conf.items():
- buf.write("%s=%s\n" % keyval)
+ buf.write('%s="%s"\n' % keyval)
util.write_file(self.rc_conf_fn, buf.getvalue())
- # Load the contents of /etc/rc.conf and store all keys in a dict.
+ # Load the contents of /etc/rc.conf and store all keys in a dict. Make sure
+ # quotes are ignored:
+ # hostname="bla"
def loadrcconf(self):
conf = {}
lines = util.load_file(self.rc_conf_fn).splitlines()
for line in lines:
+ if not re.match(r'^(.+)=(.+)', line):
+ LOG.debug("Skipping line from /etc/rc.conf: %s", line)
+ continue
+
+ # TODO: just use the matches please...
tok = line.split('=')
- conf[tok[0]] = tok[1].rstrip()
+ key = tok[0]
+ val = re.sub(r'^"|"$', '', tok[1].rstrip())
+ conf[key] = val
return conf
def readrcconf(self, key):
@@ -218,7 +231,66 @@ class Distro(distros.Distro):
ssh_util.setup_user_keys(keys, name, options=None)
def _write_network(self, settings):
- return
+ entries = net_util.translate_network(settings)
+ LOG.debug("Translated network settings")
+ LOG.debug("\n========== UBUNTU FORMAT START ==========")
+ LOG.debug("%s\n========== UBUNTU FORMAT END ==========", settings)
+ LOG.debug("\n========== GENERIC FORMAT START ==========")
+ LOG.debug("%s\n========== GENERIC FORMAT END ==========", entries)
+
+ nameservers = []
+ searchdomains = []
+ dev_names = entries.keys()
+ for (dev, info) in entries.iteritems():
+ # Skip the loopback interface.
+ if dev == 'lo0':
+ continue
+
+ LOG.info('Configuring interface %s', dev)
+
+ if info.get('bootproto') == 'static':
+ LOG.debug('Configuring dev %s with %s / %s', dev, info.get('address'), info.get('netmask'))
+ # Configure an ipv4 address.
+ ifconfig = info.get('address') + ' netmask ' + info.get('netmask')
+
+ # Configure the gateway.
+ self.updatercconf('defaultrouter', info.get('gateway'))
+
+ if 'dns-nameservers' in info:
+ nameservers.extend(info['dns-nameservers'])
+ if 'dns-search' in info:
+ searchservers.extend(info['dns-search'])
+ else:
+ ifconfig = 'DHCP'
+
+ self.updatercconf('ifconfig_' + dev, ifconfig)
+
+ # Try to read the /etc/resolv.conf or just start from scratch if that
+ # fails.
+ try:
+ resolvconf = ResolvConf(util.load_file(self.resolv_conf_fn))
+ resolvconf.parse()
+ except IOError:
+ util.logexc(LOG, "Failed to parse %s, use new empty file", self.resolv_conf_fn)
+ resolvconf = ResolvConf('')
+ resolvconf.parse()
+
+ # Add some nameservers
+ for server in nameservers:
+ try:
+ resolvconf.add_nameserver(server)
+ except ValueError:
+ util.logexc(LOG, "Failed to add nameserver %s", server)
+
+ # And add any searchdomains.
+ for domain in searchdomains:
+ try:
+ resolvconf.add_search_domain(domain)
+ except ValueError:
+ util.logexc(LOG, "Failed to add search domain %s", domain)
+ util.write_file(self.resolv_conf_fn, str(resolvconf), 0644)
+
+ return dev_names
def apply_locale(self, locale, out_fn=None):
# Adjust the locals value to the new value
diff --git a/config/cloud.freebsd.cfg b/config/cloud.freebsd.cfg
new file mode 100644
index 00000000..bb3a4a51
--- /dev/null
+++ b/config/cloud.freebsd.cfg
@@ -0,0 +1,88 @@
+# The top level settings are used as module
+# and system configuration.
+
+syslog_fix_perms: root:wheel
+
+# This should not be required, but leave it in place until the real cause of
+# not beeing able to find -any- datasources is resolved.
+datasource_list: ['OpenStack']
+
+# A set of users which may be applied and/or used by various modules
+# when a 'default' entry is found it will reference the 'default_user'
+# from the distro configuration specified below
+users:
+ - default
+
+# If this is set, 'root' will not be able to ssh in and they
+# will get a message to login instead as the above $user (ubuntu)
+disable_root: false
+
+# This will cause the set+update hostname module to not operate (if true)
+preserve_hostname: false
+
+# Example datasource config
+# datasource:
+# Ec2:
+# metadata_urls: [ 'blah.com' ]
+# timeout: 5 # (defaults to 50 seconds)
+# max_wait: 10 # (defaults to 120 seconds)
+
+# The modules that run in the 'init' stage
+cloud_init_modules:
+# - migrator
+ - seed_random
+ - bootcmd
+# - write-files
+ - growpart
+ - resizefs
+ - set_hostname
+ - update_hostname
+# - update_etc_hosts
+# - ca-certs
+# - rsyslog
+ - users-groups
+ - ssh
+
+# The modules that run in the 'config' stage
+cloud_config_modules:
+# - disk_setup
+# - mounts
+ - ssh-import-id
+ - locale
+# - set-passwords
+# - package-update-upgrade-install
+# - landscape
+# - timezone
+# - puppet
+# - chef
+# - salt-minion
+# - mcollective
+ - disable-ec2-metadata
+ - runcmd
+# - byobu
+
+# The modules that run in the 'final' stage
+cloud_final_modules:
+ - rightscale_userdata
+ - scripts-vendor
+ - scripts-per-once
+ - scripts-per-boot
+ - scripts-per-instance
+ - scripts-user
+ - ssh-authkey-fingerprints
+ - keys-to-console
+ - phone-home
+ - final-message
+ - power-state-change
+
+# System and/or distro specific settings
+# (not accessible to handlers/transforms)
+system_info:
+ distro: freebsd
+ default_user:
+ name: beastie
+ lock_passwd: True
+ gecos: FreeBSD
+ groups: [wheel]
+ sudo: ["ALL=(ALL) NOPASSWD:ALL"]
+ shell: /bin/sh
diff --git a/setup.py b/setup.py
index 556103b9..0e609d6e 100755
--- a/setup.py
+++ b/setup.py
@@ -63,12 +63,14 @@ def systemd_unitdir():
INITSYS_FILES = {
'sysvinit': [f for f in glob('sysvinit/redhat/*') if is_f(f)],
+ 'sysvinit_freebsd': [f for f in glob('sysvinit/freebsd/*') if is_f(f)],
'sysvinit_deb': [f for f in glob('sysvinit/debian/*') if is_f(f)],
'systemd': [f for f in glob('systemd/*') if is_f(f)],
'upstart': [f for f in glob('upstart/*') if is_f(f)],
}
INITSYS_ROOTS = {
'sysvinit': '/etc/rc.d/init.d',
+ 'sysvinit_freebsd': '/usr/local/etc/rc.d',
'sysvinit_deb': '/etc/init.d',
'systemd': systemd_unitdir(),
'upstart': '/etc/init/',
@@ -88,6 +90,41 @@ def read_requires():
return str(deps).splitlines()
+# Install everything in the right location and take care of Linux (default) and
+# FreeBSD systems.
+def read_datafiles():
+ sysname = os.uname()[0]
+ if sysname == 'FreeBSD':
+ return [
+ ('/usr/local/etc/cloud', glob('config/*.cfg')),
+ ('/usr/local/etc/cloud/cloud.cfg.d', glob('config/cloud.cfg.d/*')),
+ ('/usr/local/etc/cloud/templates', glob('templates/*')),
+ ('/usr/local/lib/cloud-init',
+ ['tools/uncloud-init', 'tools/write-ssh-key-fingerprints']),
+ ('/usr/local/share/doc/cloud-init',
+ [f for f in glob('doc/*') if is_f(f)]),
+ ('/usr/local/share/doc/cloud-init/examples',
+ [f for f in glob('doc/examples/*') if is_f(f)]),
+ ('/usr/local/share/doc/cloud-init/examples/seed',
+ [f for f in glob('doc/examples/seed/*') if is_f(f)]),
+ ]
+ else:
+ return [
+ ('/etc/cloud', glob('config/*.cfg')),
+ ('/etc/cloud/cloud.cfg.d', glob('config/cloud.cfg.d/*')),
+ ('/etc/cloud/templates', glob('templates/*')),
+ ('/usr/share/cloud-init', []),
+ ('/usr/lib/cloud-init',
+ ['tools/uncloud-init', 'tools/write-ssh-key-fingerprints']),
+ ('/usr/share/doc/cloud-init',
+ [f for f in glob('doc/*') if is_f(f)]),
+ ('/usr/share/doc/cloud-init/examples',
+ [f for f in glob('doc/examples/*') if is_f(f)]),
+ ('/usr/share/doc/cloud-init/examples/seed',
+ [f for f in glob('doc/examples/seed/*') if is_f(f)]),
+ ]
+
+
# TODO: Is there a better way to do this??
class InitsysInstallData(install):
init_system = None
@@ -136,20 +173,7 @@ setuptools.setup(name='cloud-init',
'tools/cloud-init-per',
],
license='GPLv3',
- data_files=[('/etc/cloud', glob('config/*.cfg')),
- ('/etc/cloud/cloud.cfg.d', glob('config/cloud.cfg.d/*')),
- ('/etc/cloud/templates', glob('templates/*')),
- ('/usr/share/cloud-init', []),
- ('/usr/lib/cloud-init',
- ['tools/uncloud-init',
- 'tools/write-ssh-key-fingerprints']),
- ('/usr/share/doc/cloud-init',
- [f for f in glob('doc/*') if is_f(f)]),
- ('/usr/share/doc/cloud-init/examples',
- [f for f in glob('doc/examples/*') if is_f(f)]),
- ('/usr/share/doc/cloud-init/examples/seed',
- [f for f in glob('doc/examples/seed/*') if is_f(f)]),
- ],
+ data_files=read_datafiles(),
install_requires=read_requires(),
cmdclass={
# Use a subclass for install that handles
diff --git a/sysvinit/freebsd/cloudconfig b/sysvinit/freebsd/cloudconfig
index 15d7ab95..44c216b3 100755
--- a/sysvinit/freebsd/cloudconfig
+++ b/sysvinit/freebsd/cloudconfig
@@ -6,28 +6,28 @@
. /etc/rc.subr
+export CLOUD_CFG=/usr/local/etc/cloud/cloud.cfg
+
name="cloudconfig"
-command="/usr/bin/cloud-init"
+command="/usr/local/bin/cloud-init"
start_cmd="cloudconfig_start"
stop_cmd=":"
rcvar="cloudinit_enable"
start_precmd="cloudinit_override"
start_cmd="cloudconfig_start"
-: ${cloudinit_config:="/etc/cloud/cloud.cfg"}
-
cloudinit_override()
{
- # If there exist sysconfig/default variable override files use it...
- if [ -f /etc/default/cloud-init ]; then
- . /etc/default/cloud-init
+ # If there exist sysconfig/defaults variable override files use it...
+ if [ -f /etc/defaults/cloud-init ]; then
+ . /etc/defaults/cloud-init
fi
}
cloudconfig_start()
{
echo "${command} starting"
- ${command} ${cloudinit_config} modules --mode config
+ ${command} modules --mode config
}
load_rc_config $name
diff --git a/sysvinit/freebsd/cloudfinal b/sysvinit/freebsd/cloudfinal
index 49945ecd..f668e036 100755
--- a/sysvinit/freebsd/cloudfinal
+++ b/sysvinit/freebsd/cloudfinal
@@ -6,28 +6,28 @@
. /etc/rc.subr
+export CLOUD_CFG=/usr/local/etc/cloud/cloud.cfg
+
name="cloudfinal"
-command="/usr/bin/cloud_init"
+command="/usr/local/bin/cloud-init"
start_cmd="cloudfinal_start"
stop_cmd=":"
rcvar="cloudinit_enable"
start_precmd="cloudinit_override"
start_cmd="cloudfinal_start"
-: ${cloudinit_config:="/etc/cloud/cloud.cfg"}
-
cloudinit_override()
{
- # If there exist sysconfig/default variable override files use it...
- if [ -f /etc/default/cloud-init ]; then
- . /etc/default/cloud-init
+ # If there exist sysconfig/defaults variable override files use it...
+ if [ -f /etc/defaults/cloud-init ]; then
+ . /etc/defaults/cloud-init
fi
}
cloudfinal_start()
{
echo -n "${command} starting"
- ${command} ${cloudinit_config} modules --mode final
+ ${command} modules --mode final
}
load_rc_config $name
diff --git a/sysvinit/freebsd/cloudinit b/sysvinit/freebsd/cloudinit
index 8d5ff10e..c5478678 100755
--- a/sysvinit/freebsd/cloudinit
+++ b/sysvinit/freebsd/cloudinit
@@ -6,28 +6,28 @@
. /etc/rc.subr
+export CLOUD_CFG=/usr/local/etc/cloud/cloud.cfg
+
name="cloudinit"
-command="/usr/bin/cloud_init"
+command="/usr/local/bin/cloud-init"
start_cmd="cloudinit_start"
stop_cmd=":"
rcvar="cloudinit_enable"
start_precmd="cloudinit_override"
start_cmd="cloudinit_start"
-: ${cloudinit_config:="/etc/cloud/cloud.cfg"}
-
cloudinit_override()
{
- # If there exist sysconfig/default variable override files use it...
- if [ -f /etc/default/cloud-init ]; then
- . /etc/default/cloud-init
+ # If there exist sysconfig/defaults variable override files use it...
+ if [ -f /etc/defaults/cloud-init ]; then
+ . /etc/defaults/cloud-init
fi
}
cloudinit_start()
{
echo -n "${command} starting"
- ${command} ${cloudinit_config} init
+ ${command} init
}
load_rc_config $name
diff --git a/sysvinit/freebsd/cloudinitlocal b/sysvinit/freebsd/cloudinitlocal
index b55705c0..c340d5d0 100755
--- a/sysvinit/freebsd/cloudinitlocal
+++ b/sysvinit/freebsd/cloudinitlocal
@@ -6,28 +6,28 @@
. /etc/rc.subr
+export CLOUD_CFG=/usr/local/etc/cloud/cloud.cfg
+
name="cloudinitlocal"
-command="/usr/bin/cloud-init"
+command="/usr/local/bin/cloud-init"
start_cmd="cloudlocal_start"
stop_cmd=":"
rcvar="cloudinit_enable"
start_precmd="cloudinit_override"
start_cmd="cloudlocal_start"
-: ${cloudinit_config:="/etc/cloud/cloud.cfg"}
-
cloudinit_override()
{
- # If there exist sysconfig/default variable override files use it...
- if [ -f /etc/default/cloud-init ]; then
- . /etc/default/cloud-init
+ # If there exist sysconfig/defaults variable override files use it...
+ if [ -f /etc/defaults/cloud-init ]; then
+ . /etc/defaults/cloud-init
fi
}
cloudlocal_start()
{
echo -n "${command} starting"
- ${command} ${cloudinit_config} init --local
+ ${command} init --local
}
load_rc_config $name
diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py
index 9763b14b..96379025 100644
--- a/tests/unittests/test_distros/test_netconfig.py
+++ b/tests/unittests/test_distros/test_netconfig.py
@@ -173,3 +173,39 @@ NETWORKING=yes
'''
self.assertCfgEquals(expected_buf, str(write_buf))
self.assertEquals(write_buf.mode, 0644)
+
+ def test_simple_write_freebsd(self):
+ fbsd_distro = self._get_distro('freebsd')
+ util_mock = self.mocker.replace(util.write_file,
+ spec=False, passthrough=False)
+ exists_mock = self.mocker.replace(os.path.isfile,
+ spec=False, passthrough=False)
+
+ exists_mock(mocker.ARGS)
+ self.mocker.count(0, None)
+ self.mocker.result(False)
+
+ write_bufs = {}
+
+ def replace_write(filename, content, mode=0644, omode="wb"):
+ buf = WriteBuffer()
+ buf.mode = mode
+ buf.omode = omode
+ buf.write(content)
+ write_bufs[filename] = buf
+
+ util_mock(mocker.ARGS)
+ self.mocker.call(replace_write)
+ self.mocker.replay()
+ fbsd_distro.apply_network(BASE_NET_CFG, False)
+
+ self.assertIn('/etc/rc.conf', write_bufs)
+ write_buf = write_bufs['/etc/rc.conf']
+ expected_buf = '''
+ifconfig_eth0="192.168.1.5 netmask 255.255.255.0"
+ifconfig_eth1="DHCP"
+defaultrouter="192.168.1.254"
+'''
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEquals(write_buf.mode, 0644)
+
diff --git a/tools/build-on-freebsd b/tools/build-on-freebsd
new file mode 100755
index 00000000..6a3f38ec
--- /dev/null
+++ b/tools/build-on-freebsd
@@ -0,0 +1,44 @@
+#!/bin/sh
+# Since there is no official FreeBSD port yet, we need some way of building and
+# installing cloud-init. This script takes care of building and installing. It
+# will optionally make a first run at the end.
+
+# Check dependencies:
+[ ! -f /tmp/c-i.dependencieschecked ] && pkg install python py27-cheetah py27-Jinja2 py27-prettytable py27-oauth py27-serial py27-configobj py27-yaml py27-argparse py27-requests py27-six py27-boto gpart sudo dmidecode
+touch /tmp/c-i.dependencieschecked
+
+# Required but unavailable port/pkg: py27-jsonpatch py27-jsonpointer
+# Luckily, the install step will take care of this by installing it from pypi...
+
+# Build the code and install in /usr/local/:
+python setup.py build
+python setup.py install -O1 --skip-build --prefix /usr/local/ --init-system sysvinit_freebsd
+
+# Use the correct config file:
+mv /usr/local/etc/cloud/cloud.freebsd.cfg /usr/local/etc/cloud/cloud.cfg
+
+# Enable cloud-init in /etc/rc.conf:
+sed -i.bak -e "/cloudinit_enable=.*/d" /etc/rc.conf
+echo 'cloudinit_enable="YES"' >> /etc/rc.conf
+
+echo "Installation completed."
+
+if [ "$1" = "run" ]
+then
+ echo "Ok, now let's see if it works."
+
+ # Backup SSH keys
+ mv /etc/ssh/ssh_host_* /tmp/
+
+ # Remove old metadata
+ rm -rf /var/lib/cloud
+
+ # Just log everything, quick&dirty
+ rm /usr/local/etc/cloud/cloud.cfg.d/05_logging.cfg
+
+ # Start:
+ /usr/local/etc/rc.d/cloudinit start
+
+ # Restore SSH keys
+ mv /tmp/ssh_host_* /etc/ssh/
+fi