Source

zfs-tools / lib / zfs / dataset.rb

Full commit

require 'shellwords'

# A zfs dataset such as 'pool1/backup'
#
class ZFS::Dataset
  attr_reader :name
  def initialize(name)
    @name = name
  end
  
  # Returns an array of snapshots on the dataset. This does not include
  # snapshots on the datasets children.
  #
  def snapshots
    list(name).
      lines.
      select { |l| l.index('@') }.      # only snapshots
      map { |l| l.chomp.split('@') }.   # <path, snapshot_name>
      select { |path, sn| path==name }. # only direct snapshots
      map { |path,sn| sn }              # only snapshot names
  end

  # Snapshots the dataset with the aid of 'zfs snapshot'. +name+ is sanitized
  # to something zfs accepts, which means that spaces and non-word chars
  # are replaced with '_'. 
  #
  def snapshot(name, recursive=false)
    arguments = []
    
    snapshot_name = sanitize_snapshot_name(name)
    
    arguments << '-r' if recursive
    arguments << self.name + "@" + snapshot_name
    
    zfs_snapshot(*arguments)
    
    return snapshot_name
  end
  
  # Snapshots the dataset with a timestamp that looks like 201101011345 (year,
  # month, day, hour and minute). If a +comment+ is provided, the snapshot
  # will have the comment appended to it, separated by a dash
  # (201101011345-my_silly_comment). Note that the sanitizing that #snapshot
  # does applies to the comment as well. 
  #
  # This snapshotting is always recursive.
  #
  def snapshot_with_timestamp(comment=nil, time=Time.now)
    name = time.strftime("%Y%m%d%H%M")
    name << "-#{comment}" if comment
    
    snapshot(name, true)
  end
  
private

  # Sanitizes a name to be used as a snapshot name. 
  #
  def sanitize_snapshot_name(name)
    name.gsub(/[^-_a-z0-9]/i, '_')
  end

  # Raw command 'zfs snapshot', args are passed as command line arguments.
  #
  def zfs_snapshot(*args)
    zfs "snapshot", *args
  end
  
  # Raw command 'zfs', args are passed as command line arguments. 
  # 
  def zfs(*args)
    arguments = args.map { |e| Shellwords.escape(e) }.join(' ')
    `sudo /sbin/zfs #{arguments}`
  end
  
  def list(dataset)
    zfs 'list', %w(-t all -rH -oname), dataset
  end
end