diff --git a/cdist/conf/type/__network_interface/files/debian/00000-wait-for-ip b/cdist/conf/type/__network_interface/files/debian/00000-wait-for-ip
new file mode 100755
index 00000000..3103ef62
--- /dev/null
+++ b/cdist/conf/type/__network_interface/files/debian/00000-wait-for-ip
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# workaround the bloody upstart race conditions
+# by delaying the emission of the net-device-up signal until the interface is
+# really up and configured.
+#
+# environment variables:
+# METHOD=dhcp
+# MODE=start
+# LOGICAL=eth0
+# PHASE=post-up
+# ADDRFAM=inet
+# VERBOSITY=0
+# PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+# IF_METRIC=100
+# IFACE=eth0
+# PWD=/root
+
+# nothing to do for loopback
+[ "$IFACE" = lo ] && exit 0
+
+LOG_FILE="/tmp/wait-for-ip-${IFACE}.log"
+cp /dev/null $LOG_FILE
+RETRY=20
+index=0
+
+if [ "$ADDRFAM" = "inet" -a "$METHOD" = "dhcp" ]; then
+ until [ -n "$ip" -o $index -eq $RETRY ]; do
+ ip=$(ip -o -family inet addr show dev $IFACE | awk '{split($4, a, "/"); print a[1]}')
+ index=$((index+1))
+ sleep 0.5
+ done
+ if [ -n "$ip" ]; then
+ echo "Interface $IFACE is up with ip $ip after $index of $RETRY tries." >> $LOG_FILE
+ else
+ echo "Interface $IFACE failed to come up with an ip address, giving up after $RETRY tries." >> $LOG_FILE
+ fi
+fi
diff --git a/cdist/conf/type/__network_interface/files/debian/ifupdown-symmetric-routing b/cdist/conf/type/__network_interface/files/debian/ifupdown-symmetric-routing
new file mode 100755
index 00000000..cdb528ab
--- /dev/null
+++ b/cdist/conf/type/__network_interface/files/debian/ifupdown-symmetric-routing
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+# See 'IFACE OPTIONS' in interfaces(5) for available variables.
+#
+
+DEBUG=
+#DEBUG=1
+debug() {
+ if [ "$DEBUG" ]; then
+ echo "[DEBUG] $@" >&2
+ fi
+}
+
+interface="$IFACE"
+
+# noop for loopback
+[ "$interface" = "lo" ] && exit 0
+
+# only work with ipv4
+[ "$ADDRFAM" = "inet" ] || exit 0
+
+# Interface must be explicitly configured to do symmetric routing.
+[ "${IF_SYMMETRIC_ROUTING:-no}" = "no" ] && exit 0
+
+
+case "$MODE" in
+ start)
+ action="up"
+ ;;
+ stop)
+ action="down"
+ ;;
+esac
+
+case "$METHOD" in
+ dhcp)
+ LEASEFILE="/var/lib/dhcp/dhclient.${interface}.leases"
+ ip_address="$(awk '/fixed-address/ {sub(/;$/,""); print $2}' "$LEASEFILE" | tail -1)"
+ subnet_mask_or_prefix="$(awk '/option subnet-mask/ {sub(/;$/,""); print $3}' "$LEASEFILE" | tail -1)"
+ gateway="$(awk '/option routers/ {sub(/;$/,""); print $3}' "$LEASEFILE" | tail -1)"
+ ;;
+ static)
+ [ -n "$IF_ADDRESS" ] && ip_address="$IF_ADDRESS"
+ [ -n "$IF_NETMASK" ] && subnet_mask_or_prefix="$IF_NETMASK"
+ [ -n "$IF_GATEWAY" ] && gateway="$IF_GATEWAY"
+ ;;
+ *)
+ echo "Unknown/unsupported METHOD: $METHOD" >&2
+ exit 1
+ ;;
+esac
+
+debug "$interface -----"
+debug "action: $action"
+debug "interface: $interface"
+debug "ip_address: $ip_address"
+debug "subnet_mask_or_prefix: $subnet_mask_or_prefix"
+debug "gateway: $gateway"
+debug "/$interface -----"
+
+if [ -n "$action" -a -n "$interface" -a -n "$ip_address" -a -n "$subnet_mask_or_prefix" ]; then
+ symmetric-routing "$action" "$interface" "$ip_address" "$subnet_mask_or_prefix" "$gateway"
+fi
+
diff --git a/cdist/conf/type/__network_interface/files/debian/interfaces b/cdist/conf/type/__network_interface/files/debian/interfaces
new file mode 100644
index 00000000..95fdb011
--- /dev/null
+++ b/cdist/conf/type/__network_interface/files/debian/interfaces
@@ -0,0 +1,9 @@
+# Generated by cdist __network_interface
+# Changes will be overwritten.
+
+# loopback
+auto lo
+iface lo inet loopback
+
+# include per interface configurations
+source /etc/network/interfaces.d/*.conf
diff --git a/cdist/conf/type/__network_interface/files/debian/manifest b/cdist/conf/type/__network_interface/files/debian/manifest
new file mode 100755
index 00000000..1e16c771
--- /dev/null
+++ b/cdist/conf/type/__network_interface/files/debian/manifest
@@ -0,0 +1,233 @@
+#!/bin/sh -e
+#
+# 2012-2018 Steven Armstrong (steven-cdist at armstrong.cc)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist 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 cdist. If not, see .
+#
+
+__package ifupdown
+# Use cumulus ifupdown2 instead of ifupown and ifenslave
+# ifupdown2 is currently not compatible with network-wait-online.
+#__package ifupdown \
+# --name ifupdown2
+
+type_files="$__type/files/debian"
+mkdir "$__object/files"
+interface_filename="${__object_id}.conf"
+
+(
+cat << DONE
+# Created by cdist ${__type##*/}
+# Do not change. Changes will be overwritten.
+#
+
+DONE
+
+if [ -f "$__object/parameter/comment" ]; then
+ cat "$__object/parameter/comment" | awk '{ print "# "$0 }'
+fi
+
+if [ -f "$__object/parameter/onboot" ]; then
+ printf "auto %s\n" "$name"
+elif [ -f "$__object/parameter/hotplug" ]; then
+ printf "allow-hotplug %s\n" "$name"
+fi
+
+ignored_parameters="linkdelay"
+manually_handled_parameters="name comment extra-config state method onboot hotplug nodns noroute no-network-wait-online symmetric-routing bond-slaves"
+case "$method" in
+ dhcp)
+ printf "iface %s inet %s\n" "$name" "$method"
+ ignored_parameters="$ignored_parameters address broadcast gateway netmask"
+ ;;
+ static|manual)
+ printf "iface %s inet %s\n" "$name" "$method"
+ ;;
+ *)
+ echo "Unsupported value for parameter --method. Got '$method'. See man page for supported values." >&2
+ exit 1
+ ;;
+esac
+
+for param in $(ls "$__object/parameter/"); do
+ if echo "$ignored_parameters" | grep -w -q "$param"; then
+ continue
+ fi
+ if echo "$manually_handled_parameters" | grep -w -q "$param"; then
+ continue
+ fi
+
+ if [ -f "$type_files/name-map" ]; then
+ key="$(awk -v param=$param '{ if ($1 == param) {print $2;} else { print param;} }' "$type_files/name-map")"
+ else
+ key="$param"
+ fi
+ printf " %s %s\n" "$key" "$(cat "$__object/parameter/$param")"
+done
+
+if [ -f "$__object/parameter/bond-mode" -o -f "$__object/parameter/bond-primary" ]; then
+ # Note: ifenslave is not needed when using ifupdown2
+ # install package required for bonding
+ __package ifenslave
+ if [ -f "$__object/parameter/bond-slaves" ]; then
+ printf ' bond-slaves %s\n' "$(cat "$__object/parameter/bond-slaves")"
+ else
+ # need this or the slave tries to bring the master up, but the master hangs waiting for a slave
+ printf ' bond-slaves none\n'
+ fi
+fi
+
+if [ -f "$__object/parameter/no-network-wait-online" ]; then
+ # Do not consider this interface in network-wait-online.service
+ printf ' no-network-wait-online yes\n'
+fi
+
+if [ -f "$__object/parameter/symmetric-routing" ]; then
+ # Deploy scripts that implement the feature ...
+ __file /sbin/symmetric-routing \
+ --owner root --group root --mode 0755 \
+ --source "$__type/files/symmetric-routing"
+ require="__package/ifupdown __file/sbin/symmetric-routing" \
+ __file /etc/network/if-up.d/symmetric-routing \
+ --owner root --group root --mode 0755 \
+ --source "$__type/files/debian/ifupdown-symmetric-routing"
+ require="__package/ifupdown __file/etc/network/if-up.d/symmetric-routing" \
+ __link /etc/network/if-down.d/symmetric-routing \
+ --type symbolic \
+ --source ../if-up.d/symmetric-routing
+ # ... then enable it in interface stanza file.
+ printf ' symmetric-routing yes\n'
+fi
+
+if [ -n "$vlan" -a -n "$device" ]; then
+ # Explicit parent interface for vlans
+ printf ' vlan-raw-device %s\n' "$device"
+fi
+
+if [ -f "$__object/parameter/extra-config" ]; then
+ extra_config="$(cat "$__object/parameter/extra-config")"
+ if [ "$extra_config" = "-" ]; then
+ extra_config="$__object/stdin"
+ fi
+ awk '{print " " $0}' "$extra_config"
+fi
+
+) >> "$__object/files/$interface_filename"
+
+__directory /etc/network \
+ --state present \
+ --owner root \
+ --group root \
+ --mode 755
+
+require="__directory/etc/network" \
+ __directory /etc/network/interfaces.d \
+ --state present \
+ --owner root \
+ --group root \
+ --mode 755
+
+require="__directory/etc/network" \
+ __file /etc/network/interfaces \
+ --source "$type_files/interfaces" \
+ --owner root \
+ --group root \
+ --mode 644
+
+require="__file/etc/network/interfaces __directory/etc/network/interfaces.d" \
+ __file "/etc/network/interfaces.d/$interface_filename" \
+ --owner root \
+ --group root \
+ --mode 644 \
+ --source "$__object/files/$interface_filename" \
+ --state "$state"
+
+
+if [ "$method" = "dhcp" -a -f "$__object/parameter/noroute" ]; then
+(
+cat << DONE
+# Created by cdist ${__type##*/}
+# Do not change. Changes will be overwritten.
+#
+
+if [ "\$interface" = "$name" ]; then
+
+case "\$reason" in
+ BOUND|RENEW|REBIND|REBOOT)
+ # prevent default gateway to be set by this interface
+ unset new_routers
+ ;;
+esac
+
+fi
+DONE
+) | \
+__file "/etc/dhcp/dhclient-enter-hooks.d/cdist-__network_interface-${name}-noroute" \
+ --owner root \
+ --group root \
+ --mode 644 \
+ --source - \
+ --state "$state"
+fi # end noroute
+
+if [ "$method" = "dhcp" -a -f "$__object/parameter/nodns" ]; then
+(
+cat << DONE
+# Created by cdist ${__type##*/}
+# Do not change. Changes will be overwritten.
+#
+
+if [ "\$interface" = "$name" ]; then
+
+# Prevent /etc/resolv.conf from being changed by this interface
+# by overriding the default 'make_resolv_conf' function.
+make_resolv_conf(){
+ :
+}
+
+fi
+DONE
+) | \
+__file "/etc/dhcp/dhclient-enter-hooks.d/cdist-__network_interface-${name}-nodns" \
+ --owner root \
+ --group root \
+ --mode 644 \
+ --source - \
+ --state "$state"
+fi # end nodns
+
+
+os=$(cat "$__global/explorer/os")
+if [ "$os" = "ubuntu" ]; then
+ # workaround the bloody upstart race conditions
+ # by deploying a script that delays the emission of the net-device-up
+ # signal until the interface is really up and configured.
+ #script_name="00000-wait-for-ip"
+ #__file "/etc/network/if-up.d/$script_name" \
+ # --owner root --group root --mode 755 \
+ # --source "$type_files/$script_name"
+
+ # Deal with systemd network-online.target race conditions
+ require="__package/ifupdown" \
+ __file /etc/network/if-pre-up.d/network-online \
+ --owner root --group root --mode 0755 \
+ --source "$__type/files/debian/network-online"
+ require="__file/etc/network/if-pre-up.d/network-online" \
+ __link /etc/network/if-up.d/network-online \
+ --type symbolic \
+ --source ../if-pre-up.d/network-online
+fi
+
diff --git a/cdist/conf/type/__network_interface/files/debian/network-online b/cdist/conf/type/__network_interface/files/debian/network-online
new file mode 100644
index 00000000..c7e4836d
--- /dev/null
+++ b/cdist/conf/type/__network_interface/files/debian/network-online
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# See 'IFACE OPTIONS' in interfaces(5) for available variables.
+#
+
+DEBUG=
+#DEBUG=1
+debug() {
+ if [ "$DEBUG" ]; then
+ echo "[DEBUG] $@" >&2
+ fi
+}
+
+interface="$IFACE"
+
+# noop for loopback
+[ "$interface" = "lo" ] && exit 0
+
+# nothing usefull we could do for '--all'
+[ "$interface" = "--all" ] && exit 0
+
+# Interface is configured to not be considered by network-wait-online.service
+[ "${IF_NO_NETWORK_WAIT_ONLINE:-no}" = "yes" ] && exit 0
+
+
+case "$MODE" in
+ start)
+ action="up"
+ ;;
+ stop)
+ action="down"
+ ;;
+esac
+
+state_dir=/run/network-online-interfaces
+mkdir -p "$state_dir"
+
+case "$PHASE" in
+ pre-up)
+ # Create flag file to wait for in network-wait-online.service
+ touch "$state_dir/$interface"
+ ;;
+ post-up)
+ # This interface is up!
+ # Remove the flag file that was created in /sbin/ifup-pre-local
+ # so that the network-wait-online.service can reach the network-online.target
+ rm -rf "$state_dir/$interface"
+ ;;
+esac
diff --git a/cdist/conf/type/__network_interface/files/network-wait-online.service b/cdist/conf/type/__network_interface/files/network-wait-online.service
new file mode 100644
index 00000000..e194b91b
--- /dev/null
+++ b/cdist/conf/type/__network_interface/files/network-wait-online.service
@@ -0,0 +1,17 @@
+[Unit]
+Description=Wait for network to be configured
+Documentation=man:ifup(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=%NETWORK_SERVICE_NAME%
+Before=network-online.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+TimeoutStartSec=3min
+ExecStart=/bin/sh -ec 'while [ "$(ls -1 /run/network-online-interfaces/)" ]; do sleep 1; done'
+
+[Install]
+WantedBy=network-online.target
+
diff --git a/cdist/conf/type/__network_interface/files/redhat/ifup-pre-local b/cdist/conf/type/__network_interface/files/redhat/ifup-pre-local
new file mode 100755
index 00000000..0030906a
--- /dev/null
+++ b/cdist/conf/type/__network_interface/files/redhat/ifup-pre-local
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+#echo "/sbin/ifup-pre-local" >&2
+#set -x
+
+config="$1"
+interface="$1"
+
+cd /etc/sysconfig/network-scripts
+. ./network-functions
+
+[ -f ../network ] && . ../network
+
+need_config "$config"
+
+source_config
+
+# If not started at boot we don't care
+[ "${ONBOOT:-no}" = "no" ] && exit 0
+
+# noop for loopback
+[ "$DEVICE" = "lo" ] && exit 0
+
+state_dir=/run/network-online-interfaces
+mkdir -p "$state_dir"
+
+if [ "${NO_NETWORK_WAIT_ONLINE:-no}" = "no" ]; then
+ # remember device for later use in network-wait-online.service
+ touch "$state_dir/$DEVICE"
+fi
+
+# hackaround bugs in /etc/sysconfig/network-scripts/ifup
+wait_for_device=20
+index=0
+until [ -d "/sys/class/net/$DEVICE" -o $index -eq $wait_for_device ]; do
+ echo "waiting for /sys/class/net/$DEVICE $index/$wait_for_device" >&2
+ sleep 1
+ index=$(($index + 1))
+done
diff --git a/cdist/conf/type/__network_interface/files/redhat/ifupdown-local b/cdist/conf/type/__network_interface/files/redhat/ifupdown-local
new file mode 100755
index 00000000..31a02d0b
--- /dev/null
+++ b/cdist/conf/type/__network_interface/files/redhat/ifupdown-local
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+myname="${0##*/}"
+
+case "$myname" in
+ ifup-local)
+ action="up"
+ ;;
+ ifdown-local|ifdown-pre-local)
+ action="down"
+ ;;
+ *)
+ echo "Unable to determine action from script name: $myname" >&2
+ exit 1
+ ;;
+esac
+
+DEBUG=
+#DEBUG=1
+debug() {
+ if [ "$DEBUG" ]; then
+ echo "[DEBUG] $@" >&2
+ fi
+}
+
+interface="$1"
+
+# noop for loopback
+[ "$interface" = "lo" ] && exit 0
+
+
+cd /etc/sysconfig/network-scripts
+. ./network-functions
+
+[ -f ../network ] && . ../network
+
+need_config "$interface"
+source_config
+
+case "${BOOTPROTO}" in
+ bootp|dhcp)
+ generate_lease_file_name
+ ip_address="$(awk '/fixed-address/ {sub(/;$/,""); print $2}' "$LEASEFILE" | tail -1)"
+ subnet_mask_or_prefix="$(awk '/option subnet-mask/ {sub(/;$/,""); print $3}' "$LEASEFILE" | tail -1)"
+ gateway="$(awk '/option routers/ {sub(/;$/,""); print $3}' "$LEASEFILE" | tail -1)"
+ ;;
+ none)
+ # No ip address set -> nothing we could do
+ [ -n "$IPADDR" ] && ip_address="$IPADDR"
+ [ -n "$PREFIX" ] && subnet_mask_or_prefix="$PREFIX" || {
+ [ -n "$NETMASK" ] && subnet_mask_or_prefix="$NETMASK"
+ }
+ [ -n "$GATEWAY" ] && gateway="$GATEWAY"
+ ;;
+ *)
+ echo "Unknown/unsupported BOOTPROTO: $BOOTPROTO" >&2
+ exit 1
+ ;;
+esac
+
+debug "$interface -----"
+debug "action: $action"
+debug "interface: $interface"
+debug "ip_address: $ip_address"
+debug "subnet_mask_or_prefix: $subnet_mask_or_prefix"
+debug "gateway: $gateway"
+debug "/$interface -----"
+
+# Interface must be explicitly configured to do symmetric routing.
+if [ "${SYMMETRIC_ROUTING:-no}" = "yes" ]; then
+ if [ -n "$action" -a -n "$interface" -a -n "$ip_address" -a -n "$subnet_mask_or_prefix" ]; then
+ symmetric-routing "$action" "$interface" "$ip_address" "$subnet_mask_or_prefix" "$gateway"
+ fi
+fi
+
+case "$action" in
+ up)
+ # This interface is up!
+ # Remove the flag file that was created in /sbin/ifup-pre-local
+ # so that the network-wait-online.service can reach the network-online.target
+ state_dir=/run/network-online-interfaces
+ rm -rf "$state_dir/$interface"
+ ;;
+esac
diff --git a/cdist/conf/type/__network_interface/files/redhat/manifest b/cdist/conf/type/__network_interface/files/redhat/manifest
new file mode 100755
index 00000000..59f17818
--- /dev/null
+++ b/cdist/conf/type/__network_interface/files/redhat/manifest
@@ -0,0 +1,171 @@
+#!/bin/sh -e
+#
+# 2014 Steven Armstrong (steven-cdist at armstrong.cc)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist 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 cdist. If not, see .
+#
+
+type_files="$__type/files/redhat"
+mkdir "$__object/files"
+interface_filename="ifcfg-${name}"
+
+(
+cat << DONE
+# Created by cdist ${__type##*/}
+# Do not change. Changes will be overwritten.
+#
+
+DONE
+
+if [ -f "$__object/parameter/comment" ]; then
+ cat "$__object/parameter/comment" | awk '{ print "# "$0 }'
+fi
+
+printf 'DEVICE="%s"\n' "$name"
+printf 'NM_CONTROLLED=no\n'
+printf 'USERCTL=no\n'
+
+if [ -f "$__object/parameter/onboot" ]; then
+ printf 'ONBOOT=yes\n'
+else
+ printf 'ONBOOT=no\n'
+fi
+if [ -f "$__object/parameter/hotplug" ]; then
+ printf 'HOTPLUG=yes\n'
+else
+ printf 'HOTPLUG=no\n'
+fi
+if [ -f "$__object/parameter/nodns" ]; then
+ printf 'PEERDNS=no\n'
+else
+ printf 'PEERDNS=yes\n'
+fi
+if [ -f "$__object/parameter/noroute" ]; then
+ printf 'DEFROUTE=no\n'
+else
+ printf 'DEFROUTE=yes\n'
+fi
+if [ -f "$__object/parameter/no-network-wait-online" ]; then
+ printf 'NO_NETWORK_WAIT_ONLINE=yes\n'
+fi
+if [ -f "$__object/parameter/symmetric-routing" ]; then
+ # Deploy scripts that implement the feature ...
+ __file /sbin/symmetric-routing \
+ --owner root --group root --mode 0755 \
+ --source "$__type/files/symmetric-routing"
+ # ... then enable it in interface cfg file.
+ printf 'SYMMETRIC_ROUTING=yes\n'
+fi
+
+ignored_parameters=""
+manually_handled_parameters="name comment extra-config state method onboot hotplug nodns noroute no-network-wait-online symmetric-routing"
+case "$method" in
+ dhcp)
+ printf 'BOOTPROTO=dhcp\n'
+ ignored_parameters="$ignored_parameters address broadcast gateway netmask"
+ ;;
+ static|manual)
+ printf 'BOOTPROTO=none\n'
+ ;;
+ *)
+ echo "Unsupported value for parameter --method. Got '$method'. See man page for supported values." >&2
+ exit 1
+ ;;
+esac
+
+for param in $(ls "$__object/parameter/"); do
+ if echo "$ignored_parameters" | grep -w -q "$param"; then
+ continue
+ fi
+ if echo "$manually_handled_parameters" | grep -w -q "$param"; then
+ continue
+ fi
+
+ case "$param" in
+ bond-master)
+ # if someone is my master, I am a slave
+ printf 'SLAVE=yes\n'
+ printf 'MASTER=%s\n' "$(cat "$__object/parameter/$param")"
+ ;;
+ bond-*)
+ key="$(echo "${param#*bond-}" | tr - _)"
+ if [ "$param" = "bond-arp-ip-target" ]; then
+ value="$(cat "$__object/parameter/$param" | tr '\n' ,)"
+ # strip trailing comma
+ value="${value%,}"
+ else
+ value="$(cat "$__object/parameter/$param")"
+ fi
+ printf '%s=%s\n' "$key" "$value" >> "$__object/files/bonding_opts"
+ ;;
+ *)
+ # check for redhat specific name for this parameter
+ if [ -f "$type_files/name-map" ]; then
+ key="$(awk -v param=$param '{ if ($1 == param) {print $2;} else { print param;} }' "$type_files/name-map")"
+ else
+ key="$param"
+ fi
+ # redhat likes things uppercase
+ key="$(echo "$key" | tr '[:lower:]' '[:upper:]')"
+ printf '%s=%s\n' "$key" "$(cat "$__object/parameter/$param")"
+ ;;
+ esac
+done
+if [ -f "$__object/files/bonding_opts" ]; then
+ value="$(cat "$__object/files/bonding_opts" | tr '\n' ' ')"
+ # strip trailing space
+ value="${value% }"
+ printf 'BONDING_OPTS="%s"\n' "$value"
+fi
+
+if [ -n "$vlan" -a -n "$device" ]; then
+ # Enable vlan for this interface
+ printf 'VLAN=yes\n'
+fi
+
+if [ -f "$__object/parameter/extra-config" ]; then
+ extra_config="$(cat "$__object/parameter/extra-config")"
+ if [ "$extra_config" = "-" ]; then
+ extra_config="$__object/stdin"
+ fi
+ cat "$extra_config"
+fi
+
+
+) >> "$__object/files/$interface_filename"
+
+__file "/etc/sysconfig/network-scripts/$interface_filename" \
+ --owner root \
+ --group root \
+ --mode 644 \
+ --source "$__object/files/$interface_filename" \
+ --state "$state"
+
+# Deploy helper scripts
+__file /sbin/ifupdown-local \
+ --owner root --group root --mode 0755 \
+ --source "$__type/files/redhat/ifupdown-local"
+require="__file/sbin/ifupdown-local" \
+ __link /sbin/ifup-local \
+ --type symbolic \
+ --source ./ifupdown-local
+require="__file/sbin/ifupdown-local" \
+ __link /sbin/ifdown-pre-local \
+ --type symbolic \
+ --source ./ifupdown-local
+__file /sbin/ifup-pre-local \
+ --owner root --group root --mode 0755 \
+ --source "$__type/files/redhat/ifup-pre-local"
diff --git a/cdist/conf/type/__network_interface/files/redhat/name-map b/cdist/conf/type/__network_interface/files/redhat/name-map
new file mode 100644
index 00000000..3e4decf6
--- /dev/null
+++ b/cdist/conf/type/__network_interface/files/redhat/name-map
@@ -0,0 +1 @@
+address ipaddr
diff --git a/cdist/conf/type/__network_interface/files/symmetric-routing b/cdist/conf/type/__network_interface/files/symmetric-routing
new file mode 100755
index 00000000..ecedfff9
--- /dev/null
+++ b/cdist/conf/type/__network_interface/files/symmetric-routing
@@ -0,0 +1,240 @@
+#!/bin/sh
+#
+
+set -e
+
+error() {
+ echo "[ERROR] $@" >&2
+}
+die() {
+ error "$@"
+ exit 1
+}
+info() {
+ echo "[INFO] $@" >&2
+}
+debug() {
+ if [ "$DEBUG" ]; then
+ echo "[DEBUG] $@" >&2
+ fi
+}
+
+usage() {
+ cat << EOS 1>&2
+Usage: ${0##*/} [OPTIONS] ACTION INTERFACE IP_ADDRESS SUBNET_MASK_OR_PREFIX [GATEWAY]
+(see -h for more information)
+EOS
+}
+
+help() {
+ usage 2>&1 | head -n -1 1>&2
+
+ cat << EOS 1>&2
+
+Setup policy based routing for the given interface
+to ensure symmetric routing.
+
+ACTION must be either 'up' or 'down' to add respectively remove the
+routing table entries.
+
+Options:
+ -h show this help message
+ -d run in debug mode
+ -x run with 'set -x' set
+ -n no action, just show what would be done without doing it
+
+Examples:
+ ${0##*/} up eth1 192.168.42.23 255.255.255.0 192.168.0.1
+ ${0##*/} down eth1 192.168.42.23 255.255.255.0 192.168.0.1
+ # gateway is optional
+ ${0##*/} up eth1 192.168.42.23 255.255.255.0
+ ${0##*/} down eth1 192.168.42.23 255.255.255.0
+ # same but using prefix instead of subnet mask
+ ${0##*/} up eth1 192.168.42.23 24 192.168.0.1
+ ${0##*/} down eth1 192.168.42.23 24 192.168.0.1
+
+EOS
+}
+
+die_usage() {
+ error "$@"
+ usage
+ exit 1
+}
+
+
+### Utility functions
+
+# Convert ip to int.
+ip2int() {
+ _ip="$1"
+ { IFS=. read _a _b _c _d; } << _done
+$_ip
+_done
+ echo $(((((((_a << 8) | _b) << 8) | _c) << 8) | _d))
+ unset _ip _a _b _c _d
+}
+
+# Convert int to ip.
+int2ip() {
+ _ui32=$1; shift
+ _ip=
+ for _n in 1 2 3 4; do
+ _ip=$((_ui32 & 0xff))${_ip:+.}$_ip
+ _ui32=$((_ui32 >> 8))
+ done
+ echo $_ip
+ unset _ui32 _ip _n
+}
+
+# Convert the given prefix into a subnet mask.
+mask_from_prefix() {
+ _prefix="$1"
+ _mask=$((0xffffffff << (32 - $_prefix)))
+ int2ip $_mask
+ unset _prefix _mask
+}
+
+# Calculate network number from the given ip and prefix.
+network_from_ip_and_prefix() {
+ _ip="$1"
+ _prefix="$2"
+ _addr=$(ip2int $_ip)
+ _mask=$((0xffffffff << (32 - $_prefix)))
+ int2ip $((_addr & _mask))
+ unset _ip _prefix _addr _mask
+}
+
+# Calculate number of bits in the given subnet mask.
+prefix_from_mask() {
+ # Assumes there's no "255." after a non-255 byte in the mask
+ _mask="$1"
+ _x=${_mask##*255.}
+ set -- 0^^^128^192^224^240^248^252^254^ $(( (${#1} - ${#_x})*2 )) ${_x%%.*}
+ _x=${1%%$3*}
+ echo $(( $2 + (${#_x}/4) ))
+ unset _mask _x
+}
+
+rt_tables=/etc/iproute2/rt_tables
+#rt_tables=/tmp/rt_tables
+# Get and if required create a routing table for the given table name.
+table_id_from_name() {
+ _interface="$1"
+ _table_id=$(awk -vname=$_interface '{ if ($2 == name) print $1 }' "$rt_tables")
+ if [ -z "$_table_id" ]; then
+ # find unused table id and create a new table for this interface
+ _used_ids=$(awk '$1 !~ /^(#| |255|254|253|0)/ { print $1 }' "$rt_tables")
+ for _tid in $(seq 1 252); do
+ if echo "$_used_ids" | grep -q "$_tid"; then
+ continue
+ else
+ _table_id="$_tid"
+ [ $NOACTION ] || printf '%s %s\n' "$_table_id" "$_interface" >> "$rt_tables"
+ break
+ fi
+ done
+ fi
+ echo "$_table_id"
+ unset _interface _table_id _used_ids _tid
+}
+
+
+### Parse command line arguments
+
+NOACTION=
+DEBUG=
+SETX=
+while getopts "ndxh" options
+do
+ #echo "$flag" $OPTIND $OPTARG
+ case $options in
+ n) NOACTION=1;;
+ d) DEBUG=1;;
+ x) SETX=1;;
+ ?|h) help
+ exit 0
+ ;;
+ *) usage
+ exit 1
+ ;;
+ esac
+done
+# Strip arguments allready handled by getopts
+shift $((OPTIND-1))
+
+[ "$SETX" ] && set -x
+
+# Validate arguments
+[ "$#" -ge 4 ] || die_usage "Expected at least 4 arguments, got: $#"
+
+action="$1" # up | down
+interface="$2"
+ip_address="$3"
+subnet_mask_or_prefix="$4"
+gateway="$5"
+
+debug "action: $action"
+debug "interface: $interface"
+debug "ip_address: $ip_address"
+debug "subnet_mask_or_prefix: $subnet_mask_or_prefix"
+debug "gateway: $gateway"
+
+
+case "$subnet_mask_or_prefix" in
+ *.*)
+ # has a dot, must be a subnet mask
+ subnet_mask="$subnet_mask_or_prefix"
+ prefix=$(prefix_from_mask "$subnet_mask")
+ network="$(network_from_ip_and_prefix "$ip_address" "$prefix")"
+ ;;
+ *)
+ # no dot, must be prefix
+ prefix="$subnet_mask_or_prefix"
+ subnet_mask="$(mask_from_prefix "$prefix")"
+ network="$(network_from_ip_and_prefix "$ip_address" "$prefix")"
+ ;;
+esac
+
+table_name="$interface"
+table_id="$(table_id_from_name "$table_name")"
+
+debug "subnet_mask: $subnet_mask"
+debug "prefix: $prefix"
+debug "network: $network"
+debug "table_name: $table_name"
+debug "table_id: $table_id"
+
+(
+case "$action" in
+ up)
+ # setup routing table for interface
+ printf 'ip route add "%s/%s" dev "%s" proto static src "%s" table "%s"\n' \
+ "$network" "$prefix" "$interface" "$ip_address" "$table_name"
+ if [ -n "$gateway" ]; then
+ printf 'ip route add default via "%s" table "%s"\n' "$gateway" "$table_name"
+ fi
+ printf 'ip rule add from "%s" table "%s"\n' "$ip_address" "$table_name"
+ ;;
+ down)
+ printf 'ip rule del from "%s" table "%s"\n' "$ip_address" "$table_name"
+ if [ -n "$gateway" ]; then
+ printf 'ip route del default via "%s" table "%s"\n' "$gateway" "$table_name"
+ fi
+ printf 'ip route del "%s/%s" dev "%s" proto static src "%s" table "%s"\n' \
+ "$network" "$prefix" "$interface" "$ip_address" "$table_name"
+ ;;
+ *)
+ echo "Unknown action: $action" >&2
+ exit 1
+ ;;
+esac
+# tell the kernel that it needs to re-parse the policy database
+printf 'ip route flush cache\n'
+) | (
+ if [ "$NOACTION" ]; then
+ cat
+ else
+ /bin/sh -s
+ fi
+)
diff --git a/cdist/conf/type/__network_interface/man.text b/cdist/conf/type/__network_interface/man.text
new file mode 100644
index 00000000..c9115e99
--- /dev/null
+++ b/cdist/conf/type/__network_interface/man.text
@@ -0,0 +1,167 @@
+cdist-type__network_interface(7)
+================================
+Steven Armstrong
+
+
+NAME
+----
+cdist-type__network_interface - configure network interfaces
+
+
+DESCRIPTION
+-----------
+Configures network interfaces on debian an redhat based systems.
+Interface names containing a dot are assumed to be vlan tagged sub interfaces.
+e.g. eth0.10 is vlan 10 on physical device eth0
+
+
+REQUIRED PARAMETERS
+-------------------
+
+
+OPTIONAL PARAMETERS
+-------------------
+name::
+ The name of the physical or logical network device.
+ Defaults to __object_id.
+method::
+ The method for determining an IP address for the interface.
+ 'dhcp', 'static' or 'manual'.
+ Defaults to 'dhcp'.
+address::
+ The IP address of the network interface.
+ Only used if --method is not 'dhcp'
+broadcast::
+ Only used if --method is not 'dhcp'
+comment::
+extra-config::
+ additional config that is added to the generated interfaces file verbatim
+gateway::
+ Default gateway (dotted quad)
+ Only used if --method is not 'dhcp'
+netmask::
+ The subnet mask to apply to the interface
+ Only used if --method is not 'dhcp'
+metric::
+ Routing metric for the default gateway
+mtu::
+ The Maximum Transmission Unit size to use for the interface
+state::
+ 'present' or 'absent', defaults to 'present'
+bond-arp-interval::
+ Specifies (in milliseconds) how often ARP monitoring occurs.
+bond-arp-ip-target::
+ Specifies the target IP address of ARP requests when the arp_interval parameter is enabled.
+ Can be specified up to 16 times.
+bond-master::
+ The name of the master (bonding) interface to which this slave should be enslaved.
+bond-miimon::
+ Specifies (in milliseconds) how often MII link monitoring occurs.
+bond-mode::
+ Allows you to specify the bonding policy. The value can be one of:
+ balance-rr (0)
+ active-backup (1)
+ balance-xor (2)
+ broadcast (3)
+ 802.3ad (4)
+ balance-tlb (5)
+ balance-alb (6)
+bond-primary::
+ Specifies the interface name, such as eth0, of the primary device.
+bond-slaves::
+ The slave interfaces that form this bonding.
+linkdelay::
+ Only useable on Redhat based systems.
+ Time in seconds that the system should pause after the specific interface
+ is enabled. This may be useful if one interface is connected to a
+ switch which has spanning tree enabled and must wait for STP to
+ converge before the interface should be considered usable.
+
+
+BOOLEAN PARAMETERS
+------------------
+onboot:
+ Whether to bring the interface up on boot
+hotplug::
+ Allow/disallow hotplug support for this interface
+nodns::
+ Do not configure nameservers in /etc/resolv.conf.
+noroute::
+ Do not set default route.
+no-network-wait-online::
+ Do not consider this network interface in the network-wait-online.service unit.
+symmetric-routing::
+ Manage routing tables and rules to ensure symmetric routing.
+
+
+EXAMPLES
+--------
+
+--------------------------------------------------------------------------------
+__network_interface eth0 --onboot
+# Same thing, but explicitly define method
+__network_interface eth0 --method dhcp --onboot
+
+__network_interface eth1 \
+ --method static \
+ --address 192.168.42.23 \
+ --netmask 255.255.255.0 \
+ --gateway 192.168.42.1 \
+ --onboot
+
+__network_interface eth3 --method dhcp --hotplug
+
+
+# Don't wait for Infiniband interface to be up before reaching systemd network-online.target
+__network_interface ib0 --method dhcp --no-network-wait-online
+
+# active-backup bonding with 2 slaves
+__network_interface bond0 \
+ --onboot \
+ --method static \
+ --bond-mode active-backup \
+ --bond-miimon 500 \
+ --bond-primary eth5 \
+ --address 10.205.9.65 \
+ --netmask 255.255.224.0
+
+__network_interface eth5 \
+ --onboot \
+ --method manual \
+ --bond-master bond0
+
+__network_interface eth6 \
+ --onboot \
+ --method manual \
+ --bond-master bond0
+
+
+# extra config
+__network_interface eth0 \
+ --method dhcp \
+ --extra-config - << DONE
+post-up ip route add 10.205.0.0/19 via 10.205.161.1
+post-up ip route add 10.205.96.0/19 via 10.205.161.1
+pre-down ip route del 10.205.0.0/19 via 10.205.161.1
+pre-down ip route del 10.205.96.0/19 via 10.205.161.1
+DONE
+--------------------------------------------------------------------------------
+
+
+SEE ALSO
+--------
+- cdist-type(7)
+- Redhat bonding documentation
+ https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/sec-Using_Channel_Bonding.html
+ https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Networking_Guide/sec-Using_Channel_Bonding.html
+ https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/s2-networkscripts-interfaces-chan.html
+- Debian bonding documentation
+ /usr/share/doc/ifenslave-2.6/README.Debian.gz
+- Symmetric routing
+ http://www.microhowto.info/howto/ensure_symmetric_routing_on_a_server_with_multiple_default_gateways.html
+
+
+COPYING
+-------
+Copyright \(C) 2012-2016 Steven Armstrong. Free use of this software is
+granted under the terms of the GNU General Public License version 3 (GPLv3).
diff --git a/cdist/conf/type/__network_interface/manifest b/cdist/conf/type/__network_interface/manifest
new file mode 100755
index 00000000..0fed7894
--- /dev/null
+++ b/cdist/conf/type/__network_interface/manifest
@@ -0,0 +1,86 @@
+#!/bin/sh -e
+#
+# 2012-2014 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2020 Adapted for upstream cdist by Darko Poljak (darko.poljak at gmail.com)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist 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 cdist. If not, see .
+#
+
+os=$(cat "$__global/explorer/os")
+osv="$(cat "$__global/explorer/os_version")"
+
+not_supported() {
+ echo "Your operating system ($os $osv) is currently not supported by this type (${__type##*/})." >&2
+ echo "Please contribute an implementation for it if you can." >&2
+ exit 1
+}
+
+case "$os" in
+ ubuntu)
+ osv_int="$(echo "$osv" | tr -d .)"
+ if [ "$osv_int" -lt 1110 ]; then
+ not_supported
+ fi
+ manifest_file="$__type/files/debian/manifest"
+ systemd_network_service_name="networking.service"
+ ;;
+ debian)
+ manifest_file="$__type/files/debian/manifest"
+ systemd_network_service_name="networking.service"
+ ;;
+ centos|redhat)
+ manifest_file="$__type/files/redhat/manifest"
+ systemd_network_service_name="network.service"
+ ;;
+ *)
+ not_supported
+ ;;
+esac
+
+name="$(cat "$__object/parameter/name" 2>/dev/null || echo "$__object_id")"
+method="$(cat "$__object/parameter/method")"
+state="$(cat "$__object/parameter/state")"
+
+device=
+vlan=
+case "$name" in
+ *.*)
+ device="${name%.*}"
+ vlan="${name#*.}"
+ ;;
+esac
+
+
+# export variables
+export name
+export device
+export vlan
+export method
+export state
+
+# run os specific manifest
+"$manifest_file"
+
+
+if grep -q systemd "$__global/explorer/init"; then
+ sed -e "s|%NETWORK_SERVICE_NAME%|${systemd_network_service_name}|" \
+ "$__type/files/network-wait-online.service" | \
+ __file /etc/systemd/system/network-wait-online.service \
+ --owner root --group root --mode 0644 \
+ --source -
+ require="__file/etc/systemd/system/network-wait-online.service" \
+ __service network-wait-online --no-start
+fi
diff --git a/cdist/conf/type/__network_interface/parameter/boolean b/cdist/conf/type/__network_interface/parameter/boolean
new file mode 100644
index 00000000..a15f76a2
--- /dev/null
+++ b/cdist/conf/type/__network_interface/parameter/boolean
@@ -0,0 +1,6 @@
+hotplug
+nodns
+noroute
+onboot
+no-network-wait-online
+symmetric-routing
diff --git a/cdist/conf/type/__network_interface/parameter/default/method b/cdist/conf/type/__network_interface/parameter/default/method
new file mode 100644
index 00000000..72ab18f1
--- /dev/null
+++ b/cdist/conf/type/__network_interface/parameter/default/method
@@ -0,0 +1 @@
+dhcp
diff --git a/cdist/conf/type/__network_interface/parameter/default/state b/cdist/conf/type/__network_interface/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__network_interface/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__network_interface/parameter/optional b/cdist/conf/type/__network_interface/parameter/optional
new file mode 100644
index 00000000..17a87788
--- /dev/null
+++ b/cdist/conf/type/__network_interface/parameter/optional
@@ -0,0 +1,20 @@
+address
+bond-arp-interval
+bond-arp-ip-target
+bond-master
+bond-miimon
+bond-mode
+bond-primary
+bond-slaves
+broadcast
+comment
+extra-config
+gateway
+linkdelay
+method
+metric
+mtu
+name
+netmask
+network
+state