Skip to content

Instantly share code, notes, and snippets.

@frederikstroem
Last active February 12, 2025 03:21
Show Gist options
  • Save frederikstroem/0c068f84cec78f832eaaf2519b3f33d3 to your computer and use it in GitHub Desktop.
Save frederikstroem/0c068f84cec78f832eaaf2519b3f33d3 to your computer and use it in GitHub Desktop.
Nushell ZFS dataset setup tool (2025-02-12 snapshot of script)
#!/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