← Back to team overview

cloud-init-dev team mailing list archive

[Merge] ~smoser/cloud-init:feature/tpl-cloud-cfg into cloud-init:master

 

Scott Moser has proposed merging ~smoser/cloud-init:feature/tpl-cloud-cfg into cloud-init:master.

Requested reviews:
  cloud-init commiters (cloud-init-dev)

For more details, see:
https://code.launchpad.net/~smoser/cloud-init/+git/cloud-init/+merge/325192
-- 
Your team cloud-init commiters is requested to review the proposed merge of ~smoser/cloud-init:feature/tpl-cloud-cfg into cloud-init:master.
diff --git a/Makefile b/Makefile
index 09cd147..c17a8eb 100644
--- a/Makefile
+++ b/Makefile
@@ -7,6 +7,8 @@ noseopts ?= -v
 YAML_FILES=$(shell find cloudinit bin tests tools -name "*.yaml" -type f )
 YAML_FILES+=$(shell find doc/examples -name "cloud-config*.txt" -type f )
 
+INIT_SYSTEM ?= systemd
+
 PIP_INSTALL := pip install
 
 ifeq ($(PYVER),python3)
@@ -69,6 +71,9 @@ check_version:
 	    "not equal to code version '$(CODE_VERSION)'"; exit 2; \
 	    else true; fi
 
+config/cloud.cfg:
+	$(PYVER) ./tools/render-ud  config/cloud.cfg.tpl config/cloud.cfg
+
 clean_pyc:
 	@find . -type f -name "*.pyc" -delete
 
@@ -79,7 +84,10 @@ yaml:
 	@$(PYVER) $(CWD)/tools/validate-yaml.py $(YAML_FILES)
 
 rpm:
-	./packages/brpm --distro $(distro)
+	./packages/brpm --distro=$(distro) --boot=$(INIT_SYSTEM)
+
+srpm:
+	./packages/brpm --srpm --distro=$(distro) --boot=$(INIT_SYSTEM)
 
 deb:
 	@which debuild || \
@@ -88,6 +96,13 @@ deb:
 
 	./packages/bddeb
 
+deb-src:
+	@which debuild || \
+		{ echo "Missing devscripts dependency. Install with:"; \
+		  echo sudo apt-get install devscripts; exit 1; }
+	./packages/bddeb -S -d
+
+
 .PHONY: test pyflakes pyflakes3 clean pep8 rpm deb yaml check_version
 .PHONY: pip-test-requirements pip-requirements clean_pyc unittest unittest3
 .PHONY: style-check
diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
index f56c0cf..1fd48a7 100755
--- a/cloudinit/distros/__init__.py
+++ b/cloudinit/distros/__init__.py
@@ -32,7 +32,7 @@ from cloudinit.distros.parsers import hosts
 
 OSFAMILIES = {
     'debian': ['debian', 'ubuntu'],
-    'redhat': ['fedora', 'rhel'],
+    'redhat': ['centos', 'fedora', 'rhel'],
     'gentoo': ['gentoo'],
     'freebsd': ['freebsd'],
     'suse': ['sles'],
diff --git a/cloudinit/util.py b/cloudinit/util.py
index 135e460..b8c3e4e 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -592,13 +592,40 @@ def get_cfg_option_int(yobj, key, default=0):
 
 
 def system_info():
-    return {
+    info = {
         'platform': platform.platform(),
         'release': platform.release(),
         'python': platform.python_version(),
         'uname': platform.uname(),
         'dist': platform.linux_distribution(),  # pylint: disable=W1505
     }
+    plat = info['platform'].lower()
+    # Try to get more info about what it actually is, in a format
+    # that we can easily use across linux and variants...
+    if plat.startswith('darwin'):
+        info['variant'] = 'darwin'
+    elif plat.endswith("bsd"):
+        info['variant'] = 'bsd'
+    elif plat.startswith('win'):
+        info['variant'] = 'windows'
+    elif 'linux' in plat:
+        # Try to get a single string out of these...
+        linux_dist, _version, _id = info['dist']
+        linux_dist = linux_dist.lower()
+        if linux_dist in ('ubuntu', 'linuxmint', 'mint'):
+            info['variant'] = 'ubuntu'
+        else:
+            for prefix, variant in [('redhat', 'rhel'),
+                                    ('centos', 'centos'),
+                                    ('fedora', 'fedora'),
+                                    ('debian', 'debian')]:
+                if linux_dist.startswith(prefix):
+                    info['variant'] = variant
+        if 'variant' not in info:
+            info['variant'] = 'linux'
+    if 'variant' not in info:
+        info['variant'] = 'unknown'
+    return info
 
 
 def get_cfg_option_list(yobj, key, default=None):
diff --git a/config/cloud.cfg b/config/cloud.cfg
deleted file mode 100644
index 1b93e7f..0000000
--- a/config/cloud.cfg
+++ /dev/null
@@ -1,117 +0,0 @@
-# The top level settings are used as module
-# and system configuration.
-
-# 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: true
-
-# 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
- - ubuntu-init-switch
- - seed_random
- - bootcmd
- - write-files
- - growpart
- - resizefs
- - disk_setup
- - mounts
- - set_hostname
- - update_hostname
- - update_etc_hosts
- - ca-certs
- - rsyslog
- - users-groups
- - ssh
-
-# The modules that run in the 'config' stage
-cloud_config_modules:
-# Emit the cloud config ready event
-# this can be used by upstart jobs for 'start on cloud-config'.
- - emit_upstart
- - snap_config
- - ssh-import-id
- - locale
- - set-passwords
- - grub-dpkg
- - apt-pipelining
- - apt-configure
- - ntp
- - timezone
- - disable-ec2-metadata
- - runcmd
- - byobu
-
-# The modules that run in the 'final' stage
-cloud_final_modules:
- - snappy
- - package-update-upgrade-install
- - fan
- - landscape
- - lxd
- - puppet
- - chef
- - salt-minion
- - mcollective
- - 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:
-   # This will affect which distro class gets used
-   distro: ubuntu
-   # Default user name + that default users groups (if added/used)
-   default_user:
-     name: ubuntu
-     lock_passwd: True
-     gecos: Ubuntu
-     groups: [adm, audio, cdrom, dialout, dip, floppy, lxd, netdev, plugdev, sudo, video]
-     sudo: ["ALL=(ALL) NOPASSWD:ALL"]
-     shell: /bin/bash
-   # Other config here will be given to the distro class and/or path classes
-   paths:
-      cloud_dir: /var/lib/cloud/
-      templates_dir: /etc/cloud/templates/
-      upstart_dir: /etc/init/
-   package_mirrors:
-     - arches: [i386, amd64]
-       failsafe:
-         primary: http://archive.ubuntu.com/ubuntu
-         security: http://security.ubuntu.com/ubuntu
-       search:
-         primary:
-           - http://%(ec2_region)s.ec2.archive.ubuntu.com/ubuntu/
-           - http://%(availability_zone)s.clouds.archive.ubuntu.com/ubuntu/
-           - http://%(region)s.clouds.archive.ubuntu.com/ubuntu/
-         security: []
-     - arches: [armhf, armel, default]
-       failsafe:
-         primary: http://ports.ubuntu.com/ubuntu-ports
-         security: http://ports.ubuntu.com/ubuntu-ports
-   ssh_svcname: ssh
diff --git a/config/cloud.cfg-freebsd b/config/cloud.cfg-freebsd
deleted file mode 100644
index d666c39..0000000
--- a/config/cloud.cfg-freebsd
+++ /dev/null
@@ -1,88 +0,0 @@
-# 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: ['ConfigDrive', 'Azure', 'OpenStack', 'Ec2']
-
-# 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: freebsd
-     lock_passwd: True
-     gecos: FreeBSD
-     groups: [wheel]
-     sudo: ["ALL=(ALL) NOPASSWD:ALL"]
-     shell: /bin/tcsh
diff --git a/config/cloud.cfg.tpl b/config/cloud.cfg.tpl
new file mode 100644
index 0000000..5f7d6bc
--- /dev/null
+++ b/config/cloud.cfg.tpl
@@ -0,0 +1,200 @@
+## template:jinja
+
+# The top level settings are used as module
+# and system configuration.
+
+{% if platform.variant in ["bsd"] %}
+syslog_fix_perms: root:wheel
+{% endif %}
+
+# 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 default $user
+{% if platform.variant in ["bsd"] %}
+disable_root: false
+{% else %}
+disable_root: true
+{% endif %}
+
+{% if platform.variant in ["centos", "fedora", "rhel"] %}
+mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2']
+resize_rootfs_tmp: /dev
+ssh_deletekeys:   0
+ssh_genkeytypes:  ~
+ssh_pwauth:   0
+{% endif %}
+
+# 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)
+
+{% if platform.variant in ["bsd"] %}
+# 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: ['ConfigDrive', 'OpenStack', 'Ec2']
+{% endif %}
+
+# The modules that run in the 'init' stage
+cloud_init_modules:
+ - migrator
+{% if platform.variant in ["ubuntu", "unknown", "debian"] %}
+ - ubuntu-init-switch
+{% endif %}
+ - seed_random
+ - bootcmd
+# Setup disks and filesystems ... before we do much else.
+ - growpart
+ - resizefs
+ - disk_setup
+ - mounts
+# (end block of disk/fs modules)
+{% if platform.variant not in ["bsd"] %}
+ - write-files
+{% endif %}
+ - set_hostname
+ - update_hostname
+{% if platform.variant not in ["bsd"] %}
+ - update_etc_hosts
+ - ca-certs
+ - rsyslog
+{% endif %}
+ - users-groups
+ - ssh
+
+# The modules that run in the 'config' stage
+cloud_config_modules:
+{% if platform.variant in ["ubuntu", "unknown", "debian"] %}
+# Emit the cloud config ready event
+# this can be used by upstart jobs for 'start on cloud-config'.
+ - emit_upstart
+{% endif %}
+ - ssh-import-id
+ - locale
+ - set-passwords
+{% if platform.variant in ["rhel", "fedora"] %}
+ - spacewalk
+ - yum-add-repo
+{% endif %}
+{% if platform.variant in ["ubuntu", "unknown", "debian"] %}
+ - grub-dpkg
+ - apt-pipelining
+ - apt-configure
+{% endif %}
+ - ntp
+ - timezone
+ - disable-ec2-metadata
+ - runcmd
+{% if platform.variant in ["ubuntu", "unknown", "debian"] %}
+ - byobu
+{% endif %}
+
+# The modules that run in the 'final' stage
+cloud_final_modules:
+{% if platform.variant in ["ubuntu", "unknown", "debian"] %}
+ - snappy
+{% endif %}
+ - package-update-upgrade-install
+{% if platform.variant in ["ubuntu", "unknown", "debian"] %}
+ - fan
+ - landscape
+ - lxd
+{% endif %}
+{% if platform.variant not in ["bsd"] %}
+ # These are all grouped together (but typically only one of them
+ # actually is doing anything, since most people don't run many of these
+ # package/configuration management systems at the same time).
+ - puppet
+ - chef
+ - salt-minion
+ - mcollective
+ # (end block of configuration management 'like' modules)
+{% endif %}
+ - 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:
+   # This will affect which distro class gets used...
+{% if platform.variant in ["centos", "debian", "fedora", "rhel", "ubuntu"] %}
+   distro: {{ platform.variant }}
+{% elif platform.variant in ["bsd"] %}
+   distro: freebsd
+{% else %}
+   # Unknown/fallback distro.
+   distro: ubuntu
+{% endif %}
+{% if platform.variant in ["ubuntu", "unknown", "debian"] %}
+   # Default user name + that default users groups (if added/used)
+   default_user:
+     name: ubuntu
+     lock_passwd: True
+     gecos: Ubuntu
+     groups: [adm, audio, cdrom, dialout, dip, floppy, lxd, netdev, plugdev, sudo, video]
+     sudo: ["ALL=(ALL) NOPASSWD:ALL"]
+     shell: /bin/bash
+   # Other config here will be given to the distro class and/or path classes
+   paths:
+      cloud_dir: /var/lib/cloud/
+      templates_dir: /etc/cloud/templates/
+      upstart_dir: /etc/init/
+   package_mirrors:
+     - arches: [i386, amd64]
+       failsafe:
+         primary: http://archive.ubuntu.com/ubuntu
+         security: http://security.ubuntu.com/ubuntu
+       search:
+         primary:
+           - http://%(ec2_region)s.ec2.archive.ubuntu.com/ubuntu/
+           - http://%(availability_zone)s.clouds.archive.ubuntu.com/ubuntu/
+           - http://%(region)s.clouds.archive.ubuntu.com/ubuntu/
+         security: []
+     - arches: [armhf, armel, default]
+       failsafe:
+         primary: http://ports.ubuntu.com/ubuntu-ports
+         security: http://ports.ubuntu.com/ubuntu-ports
+   ssh_svcname: ssh
+{% elif platform.variant in ["centos", "rhel", "fedora"] %}
+   # Default user name + that default users groups (if added/used)
+   default_user:
+     name: {{ platform.variant }}
+     lock_passwd: True
+     gecos: {{ platform.variant}} Cloud User
+     groups: [wheel, adm, systemd-journal]
+     sudo: ["ALL=(ALL) NOPASSWD:ALL"]
+     shell: /bin/bash
+   # Other config here will be given to the distro class and/or path classes
+   paths:
+      cloud_dir: /var/lib/cloud/
+      templates_dir: /etc/cloud/templates/
+   ssh_svcname: sshd
+{% elif platform.variant in ["bsd"] %}
+    # Default user name + that default users groups (if added/used)
+    default_user:
+      name: freebsd
+      lock_passwd: True
+      gecos: FreeBSD
+      groups: [wheel]
+      sudo: ["ALL=(ALL) NOPASSWD:ALL"]
+      shell: /bin/tcsh
+{% endif %}
diff --git a/packages/redhat/cloud-init.spec.in b/packages/redhat/cloud-init.spec.in
index fd3cf93..f1fba94 100644
--- a/packages/redhat/cloud-init.spec.in
+++ b/packages/redhat/cloud-init.spec.in
@@ -23,6 +23,9 @@ BuildRequires:  python-setuptools
 BuildRequires:  python-cheetah
 
 # System util packages needed
+%ifarch %{?ix86} x86_64 ia64
+Requires:       dmidecode
+%endif
 Requires:       shadow-utils
 Requires:       rsyslog
 Requires:       iproute
diff --git a/setup.py b/setup.py
index 4616599..4e02590 100755
--- a/setup.py
+++ b/setup.py
@@ -10,8 +10,11 @@
 
 from glob import glob
 
+import atexit
 import os
+import shutil
 import sys
+import tempfile
 
 import setuptools
 from setuptools.command.install import install
@@ -53,47 +56,15 @@ def pkg_config_read(library, var):
     cmd = ['pkg-config', '--variable=%s' % var, library]
     try:
         (path, err) = tiny_p(cmd)
+        path = path.strip()
     except Exception:
-        return fallbacks[library][var]
-    return str(path).strip()
+        path = fallbacks[library][var]
+    if path.startswith("/"):
+        path = path[1:]
 
+    return path
 
-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)],
-    'sysvinit_openrc': [f for f in glob('sysvinit/gentoo/*') if is_f(f)],
-    'systemd': [f for f in (glob('systemd/*.service') +
-                            glob('systemd/*.target')) if is_f(f)],
-    'systemd.generators': [f for f in glob('systemd/*-generator') 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',
-    'sysvinit_openrc': '/etc/init.d',
-    'systemd': pkg_config_read('systemd', 'systemdsystemunitdir'),
-    'systemd.generators': pkg_config_read('systemd',
-                                          'systemdsystemgeneratordir'),
-    'upstart': '/etc/init/',
-}
-INITSYS_TYPES = sorted([f.partition(".")[0] for f in INITSYS_ROOTS.keys()])
-
-# Install everything in the right location and take care of Linux (default) and
-# FreeBSD systems.
-USR = "/usr"
-ETC = "/etc"
-USR_LIB_EXEC = "/usr/lib"
-LIB = "/lib"
-if os.uname()[0] == 'FreeBSD':
-    USR = "/usr/local"
-    USR_LIB_EXEC = "/usr/local/lib"
-elif os.path.isfile('/etc/redhat-release'):
-    USR_LIB_EXEC = "/usr/libexec"
 
-
-# Avoid having datafiles installed in a virtualenv...
 def in_virtualenv():
     try:
         if sys.real_prefix == sys.prefix:
@@ -116,6 +87,55 @@ def read_requires():
     return str(deps).splitlines()
 
 
+def render_cloud_cfg():
+    # render cloud.cfg into a tmpdir under same dir as setup.py
+    topdir = os.path.dirname(sys.argv[0])
+    tmpd = tempfile.mkdtemp(dir=topdir)
+    atexit.register(shutil.rmtree, tmpd)
+    fpath = os.path.join(tmpd, 'cloud.cfg')
+    tiny_p([sys.executable, './tools/render-ud', 'config/cloud.cfg.tpl',
+            fpath])
+    # relpath is relative to setup.py
+    relpath = os.path.join(os.path.basename(tmpd), 'cloud.cfg')
+    return relpath
+
+
+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)],
+    'sysvinit_openrc': [f for f in glob('sysvinit/gentoo/*') if is_f(f)],
+    'systemd': [f for f in (glob('systemd/*.service') +
+                            glob('systemd/*.target')) if is_f(f)],
+    'systemd.generators': [f for f in glob('systemd/*-generator') 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',
+    'sysvinit_openrc': 'etc/init.d',
+    'systemd': pkg_config_read('systemd', 'systemdsystemunitdir'),
+    'systemd.generators': pkg_config_read('systemd',
+                                          'systemdsystemgeneratordir'),
+    'upstart': 'etc/init/',
+}
+INITSYS_TYPES = sorted([f.partition(".")[0] for f in INITSYS_ROOTS.keys()])
+
+
+# Install everything in the right location and take care of Linux (default) and
+# FreeBSD systems.
+USR = "usr"
+ETC = "etc"
+USR_LIB_EXEC = "usr/lib"
+LIB = "lib"
+if os.uname()[0] == 'FreeBSD':
+    USR = "usr/local"
+    USR_LIB_EXEC = "usr/local/lib"
+elif os.path.isfile('etc/redhat-release'):
+    USR_LIB_EXEC = "usr/libexec"
+
+
 # TODO: Is there a better way to do this??
 class InitsysInstallData(install):
     init_system = None
@@ -155,36 +175,40 @@ class InitsysInstallData(install):
         self.distribution.reinitialize_command('install_data', True)
 
 
-if in_virtualenv():
-    data_files = []
-    cmdclass = {}
-else:
-    data_files = [
-        (ETC + '/cloud', glob('config/*.cfg')),
-        (ETC + '/cloud/cloud.cfg.d', glob('config/cloud.cfg.d/*')),
-        (ETC + '/cloud/templates', glob('templates/*')),
-        (USR_LIB_EXEC + '/cloud-init', ['tools/ds-identify',
-                                        '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)]),
-    ]
-    if os.uname()[0] != 'FreeBSD':
-        data_files.extend([
-            (ETC + '/NetworkManager/dispatcher.d/',
-             ['tools/hook-network-manager']),
-            (ETC + '/dhcp/dhclient-exit-hooks.d/', ['tools/hook-dhclient']),
-            (LIB + '/udev/rules.d', [f for f in glob('udev/*.rules')])
-        ])
-    # Use a subclass for install that handles
-    # adding on the right init system configuration files
-    cmdclass = {
-        'install': InitsysInstallData,
-    }
-
+# Avoid having datafiles installed in a virtualenv...
+if not in_virtualenv():
+    USR = "/" + USR
+    ETC = "/" + ETC
+    USR_LIB_EXEC = "/" + LIB
+    LIB = "/" + LIB
+    for k in INITSYS_ROOTS.keys():
+        INITSYS_ROOTS[k] = "/" + INITSYS_ROOTS[k]
+
+data_files = [
+    (ETC + '/cloud', [render_cloud_cfg()]),
+    (ETC + '/cloud/cloud.cfg.d', glob('config/cloud.cfg.d/*')),
+    (ETC + '/cloud/templates', glob('templates/*')),
+    (USR_LIB_EXEC + '/cloud-init', ['tools/ds-identify',
+                                    '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)]),
+]
+if os.uname()[0] != 'FreeBSD':
+    data_files.extend([
+        (ETC + '/NetworkManager/dispatcher.d/',
+         ['tools/hook-network-manager']),
+        (ETC + '/dhcp/dhclient-exit-hooks.d/', ['tools/hook-dhclient']),
+        (LIB + '/udev/rules.d', [f for f in glob('udev/*.rules')])
+    ])
+# Use a subclass for install that handles
+# adding on the right init system configuration files
+cmdclass = {
+    'install': InitsysInstallData,
+}
 
 requirements = read_requires()
 if sys.version_info < (3,):
diff --git a/tools/render-ud b/tools/render-ud
new file mode 100755
index 0000000..2c9aebd
--- /dev/null
+++ b/tools/render-ud
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+if "avoid-pep8-E402-import-not-top-of-file":
+    _tdir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
+    sys.path.insert(0, _tdir)
+    from cloudinit import templater
+    from cloudinit import util
+    from cloudinit.atomic_helper import write_file
+
+
+def main():
+    tmpl_fn = sys.argv[1]
+    output_fn = "-"
+    if len(sys.argv) > 2:
+        output_fn = sys.argv[2]
+    tpl_params = {
+        'platform': util.system_info(),
+    }
+    with open(tmpl_fn, 'r') as fh:
+        contents = fh.read()
+    contents = (templater.render_string(contents, tpl_params))
+    util.load_yaml(contents)
+    if output_fn == "-":
+        print(contents)
+    else:
+        write_file(output_fn, contents, omode="w")
+
+
+if __name__ == '__main__':
+    main()

Follow ups