From 683c264650960748d253a6bfc85067136fa845d9 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Tue, 1 Nov 2022 22:42:46 -0400 Subject: [PATCH] Add a script to automatically mount datasets. --- ansible/playbook.yaml | 4 +- ansible/roles/base/files/bemount.bash | 126 +++++++++++++++++++++++++ ansible/roles/base/files/bemount_rc.sh | 20 ++++ ansible/roles/base/tasks/freebsd.yaml | 28 ++++++ 4 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 ansible/roles/base/files/bemount.bash create mode 100644 ansible/roles/base/files/bemount_rc.sh diff --git a/ansible/playbook.yaml b/ansible/playbook.yaml index 9e387b6..858e900 100644 --- a/ansible/playbook.yaml +++ b/ansible/playbook.yaml @@ -9,7 +9,7 @@ # - zsh # - network # - sshd - # - base + - base # - firewall # - cpu # - ntp @@ -27,4 +27,4 @@ # - fuse # - autofs # - exfat - - bhyve + # - bhyve diff --git a/ansible/roles/base/files/bemount.bash b/ansible/roles/base/files/bemount.bash new file mode 100644 index 0000000..84054d7 --- /dev/null +++ b/ansible/roles/base/files/bemount.bash @@ -0,0 +1,126 @@ +#!/usr/local/bin/bash +# +# Mount non-boot-environment datasets. +# +# We can't leave datasets outside the boot environment (for example, +# jails or bhyve VMs) as canmount=on because then every boot +# environment's external datasets would all attempt to mount every +# time. To work around this, we mark those datasets as canmount=noauto +# and run this script to mount datasets under the root of our boot +# environment. This script depends heavily on my zfs dataset structure +# so it needs to be improved to be robust enough for different +# layouts. An example of my layout is: +# +## NAME MOUNTPOINT CANMOUNT TA:BEMOUNT +## zroot none off - +## zroot/global /global on - +## zroot/freebsd none on - +## zroot/freebsd/13.1-RELEASE none on - +## zroot/freebsd/13.1-RELEASE/be none on - +## zroot/freebsd/13.1-RELEASE/be/main / noauto - +## zroot/freebsd/13.1-RELEASE/jails none on - +## zroot/freebsd/13.1-RELEASE/jails/foo /jail/foo noauto on +## zroot/freebsd/13.1-RELEASE/jails/bar /jail/bar noauto on +## zroot/freebsd/13.1-RELEASE/jails/baz /jail/baz noauto on +## zroot/freebsd/13.1-RELEASE/vm-bhyve /vm noauto on +## zroot/linux none on - +## zroot/linux/arch none on - +## zroot/linux/arch/be none on - +## zroot/linux/arch/be/main / noauto - +set -euo pipefail +IFS=$'\n\t' +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +function main { + local all_zfs_datasets=$(zfs list -Hp -o 'name,mountpoint,canmount,ta:bemount,mounted') + local root_dataset=$(find_root_dataset "$all_zfs_datasets") + local datasets_to_mount=$(find_datasets_to_mount_for_boot_environment "$all_zfs_datasets" "$root_dataset") + if [ -n "$datasets_to_mount" ]; then + mount_datasets "$datasets_to_mount" + fi +} + +function reverse_lines { + sed '1!x;H;1h;$!d;g' +} + +function find_dataset { + local all_zfs_datasets="$1" + local dataset_name="$2" + while read dataset; do + local ds_name=$(awk '{print $1}'<<<"$dataset") + if [ "$ds_name" = "$dataset_name" ]; then + echo "$dataset" + return + fi + done<<<"$all_zfs_datasets" +} + +function find_root_dataset { + local all_zfs_datasets="$1" + while read dataset; do + local ds_name=$(awk '{print $1}'<<<"$dataset") + local ds_mountpoint=$(awk '{print $2}'<<<"$dataset") + # local ds_canmount=$(awk '{print $3}'<<<"$dataset") + # local ds_bemount=$(awk '{print $4}'<<<"$dataset") + local ds_mounted=$(awk '{print $5}'<<<"$dataset") + if [ "$ds_mounted" = "yes" ] && [ "$ds_mountpoint" = "/" ]; then + echo "$ds_name" + return + fi + done<<<"$all_zfs_datasets" +} + +function find_datasets_to_mount_for_boot_environment { + local all_zfs_datasets="$1" + local root_dataset="$2" + # This is a consequence of my layout for zfs datasets. I should + # make this more robust. Perhaps a zfs property like search up + # from dataset mounted at / until you find a dataset with property + # ta:bemountroot="on"? + local be_root_name="${root_dataset%/*/*}" + local be_root_dataset=$(find_dataset "$all_zfs_datasets" "$be_root_name") + + while read dataset; do + local ds_name=$(awk '{print $1}'<<<"$dataset") + # local ds_mountpoint=$(awk '{print $2}'<<<"$dataset") + local ds_canmount=$(awk '{print $3}'<<<"$dataset") + local ds_bemount=$(awk '{print $4}'<<<"$dataset") + local ds_mounted=$(awk '{print $5}'<<<"$dataset") + + case "$ds_name" in + "${be_root_name}/"*) ;; + *) continue ;; + esac + + if [ "$ds_bemount" != "on" ]; then + continue + fi + + if [ "$ds_mounted" != "no" ]; then + continue + fi + + if [ "$ds_canmount" != "noauto" ]; then + continue + fi + echo "$dataset" + + done<<<"$all_zfs_datasets" +} + +function mount_datasets { + local datasets_to_mount=$(reverse_lines<<<"$1") + while read dataset; do + local ds_name=$(awk '{print $1}'<<<"$dataset") + local ds_mountpoint=$(awk '{print $2}'<<<"$dataset") + local ds_canmount=$(awk '{print $3}'<<<"$dataset") + local ds_bemount=$(awk '{print $4}'<<<"$dataset") + local ds_mounted=$(awk '{print $5}'<<<"$dataset") + + mount -v -t zfs "$ds_name" "$ds_mountpoint" + + done<<<"$datasets_to_mount" +} + +main diff --git a/ansible/roles/base/files/bemount_rc.sh b/ansible/roles/base/files/bemount_rc.sh new file mode 100644 index 0000000..b4eaa5c --- /dev/null +++ b/ansible/roles/base/files/bemount_rc.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# +# REQUIRE: FILESYSTEM kld +# PROVIDE: bemount + +. /etc/rc.subr +name=bemount +rcvar=${name}_enable +start_cmd="${name}_start" +stop_cmd="${name}_stop" +load_rc_config $name + +bemount_start() { + /usr/local/bin/bemount +} + +bemount_stop() { +} + +run_rc_command "$1" diff --git a/ansible/roles/base/tasks/freebsd.yaml b/ansible/roles/base/tasks/freebsd.yaml index 6d3315b..363b2f8 100644 --- a/ansible/roles/base/tasks/freebsd.yaml +++ b/ansible/roles/base/tasks/freebsd.yaml @@ -94,3 +94,31 @@ src: tmpfs fstype: tmpfs opts: rw,mode=777 + +- name: Install scripts + copy: + src: "files/{{ item.src }}" + dest: "{{ item.dest }}" + mode: 0755 + owner: root + group: wheel + loop: + - src: bemount.bash + dest: /usr/local/bin/bemount + +- name: Install rc script + copy: + src: "files/{{ item.src }}" + dest: "/usr/local/etc/rc.d/{{ item.dest|default(item.src) }}" + owner: root + group: wheel + mode: 0755 + loop: + - src: bemount_rc.sh + dest: bemount + +- name: Enable bemount + community.general.sysrc: + name: bemount_enable + value: "YES" + path: /etc/rc.conf.d/bemount