Last active
February 12, 2025 03:21
-
-
Save frederikstroem/0c068f84cec78f832eaaf2519b3f33d3 to your computer and use it in GitHub Desktop.
Nushell ZFS dataset setup tool (2025-02-12 snapshot of script)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env nu | |
use std log | |
$env.NU_LOG_FORMAT = "%ANSI_START%%LEVEL%: %MSG%%ANSI_STOP%" | |
# _ _ _ | |
# | || |___| |_ __ ___ _ _ ___ | |
# | __ / -_) | '_ \/ -_) '_(_-< | |
# |_||_\___|_| .__/\___|_| /__/ | |
# |_| | |
module helpers { | |
export def check_is_root [] { | |
log info "Checking if script is run as root…" | |
if $env.USER != "root" { | |
log error "This script must be run as root!" | |
exit 1 | |
} | |
} | |
# Returns true if a dataset exists, false otherwise. | |
# Will only ever match a single dataset due to ZFS dataset name uniqueness. | |
def dataset_exists [dataset: string] { | |
return (zfs list --json | from json | get datasets | values | where name == $dataset | is-not-empty) | |
} | |
def create_dataset [dataset: string, dataset_compression: bool] { | |
( | |
zfs create $dataset | |
-o $"compression=(if $dataset_compression { "on" } else { "off" })" | |
) | |
} | |
def get_mountpoint [dataset: string] { | |
return (zfs get -H -o value mountpoint $dataset) | |
} | |
def get_dataset_owner [dataset: string] { | |
return (stat -c %u:%g (get_mountpoint $dataset)) | |
} | |
def set_dataset_owner [dataset: string, owner: string] { | |
chown $owner (get_mountpoint $dataset) | |
} | |
def get_dataset_permissions [dataset: string] { | |
return (stat -c %a (get_mountpoint $dataset)) | |
} | |
def set_dataset_permissions [dataset: string, permissions: int] { | |
chmod $permissions (get_mountpoint $dataset) | |
} | |
def get_dataset_compression [dataset: string] { | |
return (zfs get -H -o value compression $dataset) | |
} | |
def set_dataset_compression [dataset: string, compression: bool] { | |
zfs set $"compression=(if $compression { "on" } else { "off" })" $dataset | |
} | |
# The core exported helper function to setup a dataset. | |
export def setup_dataset [ | |
dataset: string, | |
dataset_owner: string, | |
dataset_permissions: int, | |
dataset_compression: bool, | |
] { | |
log info $'Setting up dataset with owner "($dataset_owner)", permissions "($dataset_permissions)" and compression (if $dataset_compression { "enabled" } else { "disabled" })…' | |
log info "Checking if dataset exists…" | |
if (dataset_exists $dataset) { | |
log info "Dataset already exists, skipping creation…" | |
} else { | |
log info "Dataset does not exist, creating…" | |
create_dataset $dataset $dataset_compression | |
} | |
log info "Checking dataset owner…" | |
let current_owner = (get_dataset_owner $dataset) | |
if $current_owner != $dataset_owner { | |
log info $"Setting dataset owner, was "($current_owner)", setting to "($dataset_owner)"…" | |
set_dataset_owner $dataset $dataset_owner | |
} else { | |
log info "Dataset owner is correct, skipping…" | |
} | |
log info "Checking dataset permissions…" | |
let current_permissions = (get_dataset_permissions $dataset) | |
if $current_permissions != $dataset_permissions { | |
log info $"Setting dataset permissions, were "($current_permissions)", setting to "($dataset_permissions)"…" | |
set_dataset_permissions $dataset $dataset_permissions | |
} else { | |
log info "Dataset permissions are correct, skipping…" | |
} | |
log info "Checking dataset compression…" | |
let dataset_compression = (if $dataset_compression { "on" } else { "off" }) # Shadow variable to ZFS format. | |
let current_compression = (get_dataset_compression $dataset) | |
if $current_compression != $dataset_compression { | |
log info $"Setting dataset compression, was "($current_compression)", setting to "($dataset_compression)"…" | |
set_dataset_compression $dataset $dataset_compression | |
} else { | |
log info "Dataset compression is correct, skipping…" | |
} | |
log info "Dataset setup complete!" | |
} | |
} | |
use helpers | |
# __ __ _ | |
# | \/ |__ _(_)_ _ | |
# | |\/| / _` | | ' \ | |
# |_| |_\__,_|_|_||_| | |
# --------------------------------------------------------------------------- | |
# This script ensures the creation and proper configuration of ZFS datasets, | |
# including setting properties such as compression, permissions, and ownership. | |
# It is designed to work universally for any ZFS dataset setup with customizable | |
# options for ownership and permissions. | |
# | |
# The log level for output can be set with the `NU_LOG_LEVEL` environment variable: | |
# $ NU_LOG_LEVEL=DEBUG nu std_log.nu | |
# --------------------------------------------------------------------------- | |
def main [] { | |
log error "A subcommand is required!" | |
exit 1 | |
} | |
def "main setup_base_dataset" [ | |
base_dataset: string, # Base dataset to create e.g. "tank/my_service", "tank/very/nested/service" | |
--base_dataset_owner: string = "0:0", # Owner of base dataset e.g. "1000:1000", "username:username" | |
--base_dataset_permissions: int = 755, # Permissions of base dataset | |
] { | |
helpers check_is_root | |
log info $'Setting up base dataset "($base_dataset)" with owner "($base_dataset_owner)" and permissions "($base_dataset_permissions)"…' | |
helpers setup_dataset $base_dataset $base_dataset_owner $base_dataset_permissions false | |
} | |
def "main setup_dataset" [ | |
base_dataset: string, # Base dataset to base dataset upon e.g. "tank/my_service", "tank/very/nested/service" | |
dataset: string, # Dataset to create e.g. "data", "logs", "very/much/nested/dataset" | |
--dataset_owner: string = "0:0", # Owner of dataset e.g. "1000:1000", "username:username" | |
--dataset_permissions: int = 700, # Permissions of dataset e.g. "750", "755" | |
--dataset_compression_disabled # Disable compression on dataset | |
] { | |
helpers check_is_root | |
log info $'Setting up dataset "($dataset)" on base dataset "($base_dataset)" with owner "($dataset_owner)", permissions "($dataset_permissions)" and compression (if $dataset_compression_disabled { "disabled" } else { "enabled" })…' | |
let dataset_path = $'($base_dataset)/($dataset)' | |
helpers setup_dataset $dataset_path $dataset_owner $dataset_permissions (not $dataset_compression_disabled) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment