Source

zfs-tools / bin / zfs_safe_destroy

Full commit
#!/usr/bin/env ruby

$:.unshift File.dirname(__FILE__) + '/../lib'

require 'mixlib/shellout'

if ARGV.empty? 
  puts %Q{
    Usage: zfs_safe_destroy DATASET

    Scans the given dataset and prints a summary of what zfs objects 
    (filesystems, volumes and snapshots) would be affected if one was to 
    recursively destroy the given dataset. If you type 'yes' at the prompt, 
    it will then perform a recursive destroy command on the dataset. 

    CAUTION: By typing yes at the prompt, you will loose data. If this is 
    your goal, this is your command. 
}
  exit 0
end

def enumerate_objects dataset
  zfs = Mixlib::ShellOut.new(
    'zfs list -r -Ho name,type ', dataset)
  zfs.run_command
  zfs.error!

  summary = Hash.new(0)

  zfs.stdout.lines.map do |line|
    name, _, type = line.chomp.rpartition(' ')
    name.strip!

    summary[type] += 1
  end

  summary
rescue => ex
  warn ex.to_s
  warn "Aborting."
  exit(3)
end
def zfs_recursive_destroy dataset
  destroy = Mixlib::ShellOut.new('zfs destroy -r ', dataset)
  destroy.run_command
  destroy.error!
end

# ----------------------------------------------------------------- main logic

dataset = ARGV.first
before = enumerate_objects(dataset)

puts "You're about to permanently destroy: "
before.each do |type, count|
  printf "%20s %d\n", type, count
end

puts "\n'zfs destroy -r #{dataset}'"
print "Please confirm the operation by typing 'yes': "
confirmation = gets
exit(1) unless confirmation.chomp == 'yes'

after = enumerate_objects(dataset)
if before != after
  warn "The data that would have been destroyed by the command has changed."
  exit(2)
end

$stdout.sync = true
print "Destroying..."
zfs_recursive_destroy dataset
puts ' Done.'