diff --git a/README.md b/README.md index 085482a..270ba7b 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ instance: Optional features, which may not be universally necessary: * manage symlinks from NVMe block devices to `/dev/xvd` and `/dev/sd` devices (i.e. AWS Nitro instances) +* manage hotpluggable network interfaces * manage secondary IPv4 and IPv6 addresses on network interfaces ## Requirements @@ -125,3 +126,9 @@ If you're instantiating an instance in order to create a new cloud image (using [Packer](https://packer.io), or some other means), you will need to remove this file before creating the image to ensure that instances using the new image will also run Tiny Cloud init scripts during their first boot. + +## Cloud Hotplug Modules + +### `vnic_eth_hotplug` + +### `nvme_ebs_links` \ No newline at end of file diff --git a/etc/network/interfaces.d/DEFAULT b/etc/network/interfaces.d/DEFAULT new file mode 100644 index 0000000..0c99d69 --- /dev/null +++ b/etc/network/interfaces.d/DEFAULT @@ -0,0 +1,2 @@ +auto %% +iface %% inet dhcp diff --git a/etc/network/interfaces.d/lo b/etc/network/interfaces.d/lo new file mode 100644 index 0000000..f1bd92e --- /dev/null +++ b/etc/network/interfaces.d/lo @@ -0,0 +1,2 @@ +auto lo +iface lo inet loopback diff --git a/lib/mdev/vnic-eth-hotplug b/lib/mdev/vnic-eth-hotplug new file mode 100755 index 0000000..67d73d6 --- /dev/null +++ b/lib/mdev/vnic-eth-hotplug @@ -0,0 +1,109 @@ +#!/bin/sh +# vim: set ts=4 et: + +set -e + +PROC="$(basename "$0")[$$]" + +DEBUG= + +log() { + [ -z "$DEBUG" ] && [ "$1" = "debug" ] && return + local facility="kern.$1" + shift + logger -p "$facility" -t "$PROC" "$@" +} + +if [ -z "$MDEV" ]; then + log err "MDEV env not defined" + exit 1 +fi + +# where's the tiny-cloud lib directory? +if [ -z "$TINY_CLOUD_LIBS" ]; then + TINY_CLOUD_CONF=${TINY_CLOUD_CONF:-/etc/conf.d/tiny-cloud} + [ -f "$TINY_CLOUD_CONF" ] && source "$TINY_CLOUD_CONF" + TINY_CLOUD_LIBS=${TINY_CLOUD_LIBS:-/lib/tiny-cloud} +fi + +IFACE_CFG=/etc/network/interfaces + +ip() { + local v=-4 lev=info + if [ "$1" = '-4' ] || [ "$1" = '-6' ]; then + v="$1" + shift + fi + local op="$2" + + [ "$op" = show ] && lev=debug + if /sbin/ip "$v" "$@" || [ -n "$FAIL_OK" ]; then + log "$lev" "OK: ip $v $*" + else + log err "FAIL: ip $v $*" + fi +} + +assemble_interfaces() { + log info "Rebuilding $IFACE_CFG" + # TODO: might be elsewhere, source /etc/conf.d/tiny-cloud + "$TINY_CLOUD_LIBS"/network-interfaces +} + +interface_up() { + log info "Bringing up $MDEV" + # so /run/udhcpcd.$MDEV.pid isn't non-owner writeable + (umask 0022 && ifup "$MDEV") +} + +cleanup_interface() { + local v p rtable="${MDEV#eth}" + let rtable+=1000 + + log info "Cleaning up $MDEV" + + # kill related udhcpc, don't panic if it's not there + kill "$(cat "/run/udhcpc.$MDEV.pid")" || true + + # tidy up /run/ifstate, if it exists + [ -f /run/ifstate ] && sed -i -e "/^$MDEV=/d" /run/ifstate + rm -f /run/ifstate."$MDEV".lock + + # remove related rules + for v in 4 6; do + for p in $(ip -"$v" rule show table "$rtable" | cut -d: -f1); do + ip -"$v" rule del pref "$p" + done + done +} + +is_networking_started() { service networking status -q 2>/dev/null; } + +log info "STARTING: $ACTION $MDEV" + +if exec 200>>"$IFACE_CFG"; then + if flock 200; then + case $ACTION in + add|"") + assemble_interfaces + is_networking_started && interface_up + ;; + remove) + assemble_interfaces + is_networking_started && cleanup_interface + ;; + *) + log err "Unknown action '$ACTION'" + exit 1 + ;; + esac + else + log err "Unable to flock $IFACE_CFG" + exit 1 + fi +else + log err "Unable to assign fd 200 to flock $IFACE_CFG" + exit 1 +fi + +log info "FINISHED: $ACTION $MDEV" diff --git a/lib/tiny-cloud/aws/imds b/lib/tiny-cloud/aws/imds index b1ab4b9..17a3589 100644 --- a/lib/tiny-cloud/aws/imds +++ b/lib/tiny-cloud/aws/imds @@ -22,6 +22,8 @@ _imds_header() { # digs deeper than the default imds_ssh_keys() { + local key + for key in $(imds "$IMDS_SSH_KEYS"); do imds "$IMDS_SSH_KEYS/${key%=*}/openssh-key" done | sort -u diff --git a/lib/tiny-cloud/azure/imds b/lib/tiny-cloud/azure/imds index 3acd55f..82b2b87 100644 --- a/lib/tiny-cloud/azure/imds +++ b/lib/tiny-cloud/azure/imds @@ -15,6 +15,8 @@ _imds_header() { # dig deeper than default imds_ssh_keys() { + local key + for key in $(imds "$IMDS_SSH_KEYS"); do imds "$IMDS_SSH_KEYS/${key}/keyData" done | sort -u diff --git a/lib/tiny-cloud/gcp/imds b/lib/tiny-cloud/gcp/imds index e5362f4..7acfefa 100644 --- a/lib/tiny-cloud/gcp/imds +++ b/lib/tiny-cloud/gcp/imds @@ -17,6 +17,8 @@ _imds_header() { # merge project and instance keys imds_ssh_keys() { + local ssh_keys + for ssh_keys in $IMDS_SSH_KEYS; do # ignore errors and strip leading ':' _imds "$ssh_keys" | cut -d: -f2- diff --git a/lib/tiny-cloud/init-early b/lib/tiny-cloud/init-early index 5fedfb8..2011a19 100644 --- a/lib/tiny-cloud/init-early +++ b/lib/tiny-cloud/init-early @@ -15,10 +15,11 @@ expand_root() { local volume=$(echo "$mountpoint" | sed -Ee "s/(nvme\d+n\d|(xv|s)d[a-z])p?\d?$/\1/" ) + local partition if [ "$mountpoint" != "$volume" ]; then # it's a partition, resize it - local partition=$(echo "$mountpoint" | sed -Ee "s/.*(\d+)$/\1/") + partition=$(echo "$mountpoint" | sed -Ee "s/.*(\d+)$/\1/") echo ", +" | sfdisk -q --no-reread -N "$partition" "$volume" partx -u "$volume" fi @@ -27,12 +28,15 @@ expand_root() { resize2fs "$mountpoint" } +assemble_interfaces() { "$TINY_CLOUD_LIBS"/network-interfaces; } + has_cloud_hotplugs() { [ -n "$HOTPLUG_MODULES" ]; } install_hotplugs() { - for module in $HOTPLUG_MODULES; do - local result='-' + local result + for module in $HOTPLUG_MODULES; do + result='-' echo -n " $module" if type "mod__$module" | grep -q "is a function"; then "mod__$module" && result='+' || result='!' diff --git a/lib/tiny-cloud/mdev b/lib/tiny-cloud/mdev index bcb3aff..bbc9930 100644 --- a/lib/tiny-cloud/mdev +++ b/lib/tiny-cloud/mdev @@ -16,15 +16,18 @@ install_before() { else # no rule exists, put it before the catch-all fallback before='^# fallback' + line="$line\n" fi sed -i -Ee "s|($before.*)|$line\n\1|" /etc/mdev.conf } # hotpluggable VNICs (multi-cloud) mod__vnic_eth_hotplug() { - # TODO: missing dependencies? return 1 install_before '^eth' \ - 'eth[0-9]* root:root 0644 */lib/mdev/vnic-eth-hotplug' + 'eth[0-9] root:root 0644 */lib/mdev/vnic-eth-hotplug' + + # NICs attached at launch don't get added with mdev -s + assemble_interfaces } # load cloud-specific functions diff --git a/lib/tiny-cloud/network-interfaces b/lib/tiny-cloud/network-interfaces new file mode 100755 index 0000000..4ca23d5 --- /dev/null +++ b/lib/tiny-cloud/network-interfaces @@ -0,0 +1,39 @@ +#!/bin/sh +# vim: set ts=4 et: + +set -e + +IFACE_CFG=/etc/network/interfaces +IFACE_DIR="${IFACE_CFG}.d" + +cd "$IFACE_DIR" + +cat > "$IFACE_CFG.new" < "$IFACE" + printf "%s\n\n" "$(cat "$IFACE")" >> "$IFACE_CFG.new" +done + +# all the rest +for i in "$IFACE_DIR"/*; do + IFACE="$(basename "$i")" + case $IFACE in + DEFAULT|lo|eth*) + continue + ;; + *) + printf "%s\n\n" "$(cat "$IFACE")" >> "$IFACE_CFG.new" + ;; + esac +done + +# install new interfaces config +[ -f "$IFACE_CFG" ] && cp -a "$IFACE_CFG" "$IFACE_CFG.bak" +mv "$IFACE_CFG.new" "$IFACE_CFG"