module Frachtraum

Constants

BALLOTX
BYTES_IN_GB
BYTES_IN_GiB
BYTES_IN_KB

these define a KB as 1000 bits, according to the SI prefix

BYTES_IN_KiB

Kibibyte, Mebibyte, Gibibyte, etc… all the IEC sizes

BYTES_IN_MB
BYTES_IN_MiB
BYTES_IN_TB
BYTES_IN_TiB
CHECKMARK
COMPRESSION
CONFIG_FILE
ENCRYPTION
KEYLENGTH
MOUNTPOINT
OUTPUT_DOTS_LEN
REQUIRED_TOOLS_BSD
REQUIRED_TOOLS_LINUX
REQUIRED_TOOLS_OSX

Maybe this will come in time, but it is not a priority. There is some literature on disk encryption from the command line: krypted.com/mac-security/encrypting-os-x-mountain-lion/

TIMEMACHINE_TARGETS
VERSION
VOLUMES

Public Class Methods

attach(password, volume=nil) click to toggle source
# File lib/frachtraum.rb, line 73
def attach(password, volume=nil)
  case RUBY_PLATFORM
    when /bsd/    then attach_bsd   password, volume
    when /linux/  then attach_linux password, volume
    #when /darwin/ then attach_osx   password, volume
    else abort "OS not supported"
  end
end
capacity() click to toggle source
# File lib/frachtraum.rb, line 83
def capacity()
  total_used  = 0
  total_avail = 0
  Frachtraum::VOLUMES.each do |volume|
    used  = %x( zfs get -o value -Hp used #{volume} 2>&1 )
    avail = %x( zfs get -o value -Hp available #{volume} 2>&1 )

    total_used  += (used =="" ? 0 : used).to_i  # / 1000 # 1024
    total_avail += (avail=="" ? 0 : avail).to_i # / 1000 # 1024
  end

  total = total_used + total_avail

  return {:total => total, :avail => total_avail, :used => total_used}
end
pretty_IEC_bytes(bytes) click to toggle source
# File lib/frachtraum.rb, line 64
def pretty_IEC_bytes(bytes)
  return "%.1f TiB" % (bytes.to_f / BYTES_IN_TiB) if bytes > BYTES_IN_TiB
  return "%.1f GiB" % (bytes.to_f / BYTES_IN_GiB) if bytes > BYTES_IN_GiB
  return "%.1f MiB" % (bytes.to_f / BYTES_IN_MiB) if bytes > BYTES_IN_MiB
  return "%.1f KiB" % (bytes.to_f / BYTES_IN_KiB) if bytes > BYTES_IN_KiB
  return "#{bytes} B"
end
pretty_SI_bytes(bytes) click to toggle source

# File lib/frachtraum.rb, line 55
def pretty_SI_bytes(bytes)
  return "%.1f TB" % (bytes.to_f / BYTES_IN_TB) if bytes > BYTES_IN_TB
  return "%.1f GB" % (bytes.to_f / BYTES_IN_GB) if bytes > BYTES_IN_GB
  return "%.1f MB" % (bytes.to_f / BYTES_IN_MB) if bytes > BYTES_IN_MB
  return "%.1f KB" % (bytes.to_f / BYTES_IN_KB) if bytes > BYTES_IN_KB
  return "#{bytes} B"
end
report() click to toggle source
# File lib/frachtraum.rb, line 100
def report()

  report_table = {}
  reported_values = [:used,:available,:compression,:compressratio]

  (Frachtraum::VOLUMES + Frachtraum::TIMEMACHINE_TARGETS).each do |dataset|
    volume_info = {}

    # fetch the values
    if zfs_volume_exists?(dataset)
      reported_values.each do |repval|
        volume_info[repval] = %x( zfs get -o value -Hp #{repval.to_s} #{dataset} )
      end
    else
      reported_values.each {|repval| volume_info[repval] = "N/A" }
    end

    # calculate a total size for each volume
    volume_info[:total] =
      if volume_info[:used]=="N/A" || volume_info[:available]=="N/A"
        "N/A"
      else
        (volume_info[:used].to_i + volume_info[:available].to_i)
      end

      volume_info[:usage] =
        if volume_info[:total] == 0
          "0 %"
        elsif volume_info[:used]=="N/A" || volume_info[:total]=="N/A"
          "N/A"
        elsif volume_info[:available].to_i == 0
          "100 %"
        else
          (100 * volume_info[:used].to_f / volume_info[:total].to_f ).to_i.to_s + " %"
        end

    report_table[dataset] = volume_info
  end

  return report_table
end
run_system_test() click to toggle source
# File lib/frachtraum.rb, line 173
def run_system_test()
  tool_list = []
  case RUBY_PLATFORM
    when /bsd/    then tool_list = REQUIRED_TOOLS_BSD
    when /linux/  then tool_list = REQUIRED_TOOLS_LINUX
    #when /darwin/ then tool_list = REQUIRED_TOOLS_OSX
    else abort "OS not supported"
  end

  tool_list.each { |tool| find_executable tool }

  # find_executable seems to create such file in case executable is not found
  File.delete 'mkmf.log' if File.exists?('mkmf.log')
end
setupdisk(dev, label, password, compression, encryption, keylength, mountpoint) click to toggle source
# File lib/frachtraum.rb, line 144
def setupdisk(dev, label, password, compression, encryption, keylength, mountpoint)

  abort "untested procedure -- won't continue"

  case RUBY_PLATFORM
    when /bsd/    then setupdisk_bsd   dev, label, password, compression, encryption, keylength, mountpoint
    when /linux/  then setupdisk_linux dev, label, password, compression, encryption, keylength, mountpoint
    #when /darwin/ then setupdisk_osx   dev, label, password, compression, encryption, keylength, mountpoint
    else abort "OS not supported"
  end
end
sweep(volume) click to toggle source
# File lib/frachtraum.rb, line 157
def sweep(volume)

  target_volumes = volume.nil? ? Frachtraum::VOLUMES : volume

  # TODO
  abort "sweeping not supported yet"

  target_volumes.each do |volume|
    if zfs_volume_exists?(volume)
      # TODO
    end
  end
end
zfs_volume_exists?(dataset) click to toggle source
# File lib/frachtraum.rb, line 190
def zfs_volume_exists?(dataset)
  output = %x( zfs get -H mounted #{dataset} 2>&1 )
  case output
  when /yes/
    return true
  when /dataset does not exist/, /permission denied/
    return false
  else
    abort "can't handle output of zfs_volume_exists?: #{output}"
  end
end

Public Instance Methods

attach_bsd(password, volume=nil) click to toggle source
# File lib/frachtraum/bsd.rb, line 5
def attach_bsd(password, volume=nil)

  # if we provided a specific depot, run procedure only on that one
  volumes = volume.nil? ? Frachtraum::VOLUMES : [ volume ]

  # first of all, decrypt and mount all depots
  volumes.each do |v|
    print "decrypting #{v}...".ljust(OUTPUT_DOTS_LEN,".")

    output = %x( echo #{password} | geli attach -d -j - /dev/label/#{v} 2>&1 )
    if $?.success?
      output = %x( zfs mount #{v} 2>&1 )
      if $?.success? then puts Rainbow(CHECKMARK).green
      else puts Rainbow("#{BALLOTX}\n#{output}").red end
    else
      puts Rainbow("#{BALLOTX}\n#{output}").red
    end
  end # volumes.each

  # mount timemachine targets as well
  Frachtraum::TIMEMACHINE_TARGETS.each do |tmtarget|
    print "mounting #{tmtarget}...".ljust(OUTPUT_DOTS_LEN,".")

    output = %x( zfs mount #{tmtarget} 2>&1 )
    if $?.success? then puts Rainbow(CHECKMARK).green
    else puts Rainbow("#{BALLOTX}\n#{output}").red end
  end

  # restart samba so it reports the correct pool size
  print "restarting samba server...".ljust(OUTPUT_DOTS_LEN,".")

  output = %x( /usr/local/etc/rc.d/samba restart 2>&1 )
  if $?.success? then puts Rainbow(CHECKMARK).green
  else puts Rainbow("#{BALLOTX}\n#{output}").red end
end
attach_linux(password, volume) click to toggle source
# File lib/frachtraum/linux.rb, line 5
def attach_linux(password, volume)
  # TODO
  abort "not yet implemented"
end
attach_osx(password, volume) click to toggle source
# File lib/frachtraum/osx.rb, line 11
def attach_osx(password, volume)
  # TODO
  abort "not yet implemented"
end
exec_cmd(msg, cmd) click to toggle source
# File lib/frachtraum.rb, line 37
def exec_cmd(msg, cmd)

  print msg

  Open3.popen2e(cmd) do |stdin, stdout_err, wait_thr|
    puts line while line = stdout_err.gets

    exit_status = wait_thr.value
    if exit_status.success?
      puts Rainbow("done").green
    else
      abort Rainbow("FAILED!").red + " --> #{stdout_err}"
    end
  end
end
setupdisk_bsd(dev, label, password, compression, encryption, keylength, mountpoint) click to toggle source
# File lib/frachtraum/bsd.rb, line 41
def setupdisk_bsd(dev, label, password, compression, encryption, keylength, mountpoint)

  # TODO password promt, confirmation question, etc..
  abort "implementation not ready yet"


  exec_cmd "destroying previous partitioning on /dev/#{dev}...",
           "dd if=/dev/zero of=/dev/#{dev} bs=512 count=1"

  exec_cmd "creating gpart container on /dev/#{dev}...",
           "gpart create -s GPT #{dev}"

  exec_cmd "labeling /dev/#{dev} with '#{label}'...",
           "glabel label -v #{label} /dev/#{dev}"

  exec_cmd "initialising /dev/#{dev} as password protected GEOM provider with #{encryption} encryption...",
           "echo #{password} | geli init -s #{keylength} -e #{encryption} -J - /dev/label/#{label}"

  exec_cmd "attaching /dev/label/#{label} as GEOM provider, creating device /dev/label/#{label}.eli...",
           "echo #{password} | geli attach -d -j - /dev/label/#{label}"

  exec_cmd "creating zpool #{mountpoint}/#{label} on encrypted device /dev/label/#{label}.eli...",
           "zpool create -m #{mountpoint}/#{label} #{label} /dev/label/#{label}.eli"

  exec_cmd "setting compression '#{compression}' for new zfs on #{mountpoint}/#{label}...",
           "zfs set compression=#{compression} #{label}"

  exec_cmd "setting permissions...",
           "chmod -R 775 #{mountpoint}/#{label}"

  puts "setup finished"

end
setupdisk_linux(dev, label, password, compression, encryption, keylength, mountpoint) click to toggle source
# File lib/frachtraum/linux.rb, line 10
def setupdisk_linux(dev, label, password, compression, encryption, keylength, mountpoint)
  # TODO
  abort "not yet implemented"
end
setupdisk_osx(dev, label, password, compression, encryption, keylength, mountpoint) click to toggle source
# File lib/frachtraum/osx.rb, line 16
def setupdisk_osx(dev, label, password, compression, encryption, keylength, mountpoint)
  # TODO
  abort "not yet implemented"
end

Private Instance Methods

attach(password, volume=nil) click to toggle source
# File lib/frachtraum.rb, line 73
def attach(password, volume=nil)
  case RUBY_PLATFORM
    when /bsd/    then attach_bsd   password, volume
    when /linux/  then attach_linux password, volume
    #when /darwin/ then attach_osx   password, volume
    else abort "OS not supported"
  end
end
capacity() click to toggle source
# File lib/frachtraum.rb, line 83
def capacity()
  total_used  = 0
  total_avail = 0
  Frachtraum::VOLUMES.each do |volume|
    used  = %x( zfs get -o value -Hp used #{volume} 2>&1 )
    avail = %x( zfs get -o value -Hp available #{volume} 2>&1 )

    total_used  += (used =="" ? 0 : used).to_i  # / 1000 # 1024
    total_avail += (avail=="" ? 0 : avail).to_i # / 1000 # 1024
  end

  total = total_used + total_avail

  return {:total => total, :avail => total_avail, :used => total_used}
end
pretty_IEC_bytes(bytes) click to toggle source
# File lib/frachtraum.rb, line 64
def pretty_IEC_bytes(bytes)
  return "%.1f TiB" % (bytes.to_f / BYTES_IN_TiB) if bytes > BYTES_IN_TiB
  return "%.1f GiB" % (bytes.to_f / BYTES_IN_GiB) if bytes > BYTES_IN_GiB
  return "%.1f MiB" % (bytes.to_f / BYTES_IN_MiB) if bytes > BYTES_IN_MiB
  return "%.1f KiB" % (bytes.to_f / BYTES_IN_KiB) if bytes > BYTES_IN_KiB
  return "#{bytes} B"
end
pretty_SI_bytes(bytes) click to toggle source

# File lib/frachtraum.rb, line 55
def pretty_SI_bytes(bytes)
  return "%.1f TB" % (bytes.to_f / BYTES_IN_TB) if bytes > BYTES_IN_TB
  return "%.1f GB" % (bytes.to_f / BYTES_IN_GB) if bytes > BYTES_IN_GB
  return "%.1f MB" % (bytes.to_f / BYTES_IN_MB) if bytes > BYTES_IN_MB
  return "%.1f KB" % (bytes.to_f / BYTES_IN_KB) if bytes > BYTES_IN_KB
  return "#{bytes} B"
end
report() click to toggle source
# File lib/frachtraum.rb, line 100
def report()

  report_table = {}
  reported_values = [:used,:available,:compression,:compressratio]

  (Frachtraum::VOLUMES + Frachtraum::TIMEMACHINE_TARGETS).each do |dataset|
    volume_info = {}

    # fetch the values
    if zfs_volume_exists?(dataset)
      reported_values.each do |repval|
        volume_info[repval] = %x( zfs get -o value -Hp #{repval.to_s} #{dataset} )
      end
    else
      reported_values.each {|repval| volume_info[repval] = "N/A" }
    end

    # calculate a total size for each volume
    volume_info[:total] =
      if volume_info[:used]=="N/A" || volume_info[:available]=="N/A"
        "N/A"
      else
        (volume_info[:used].to_i + volume_info[:available].to_i)
      end

      volume_info[:usage] =
        if volume_info[:total] == 0
          "0 %"
        elsif volume_info[:used]=="N/A" || volume_info[:total]=="N/A"
          "N/A"
        elsif volume_info[:available].to_i == 0
          "100 %"
        else
          (100 * volume_info[:used].to_f / volume_info[:total].to_f ).to_i.to_s + " %"
        end

    report_table[dataset] = volume_info
  end

  return report_table
end
run_system_test() click to toggle source
# File lib/frachtraum.rb, line 173
def run_system_test()
  tool_list = []
  case RUBY_PLATFORM
    when /bsd/    then tool_list = REQUIRED_TOOLS_BSD
    when /linux/  then tool_list = REQUIRED_TOOLS_LINUX
    #when /darwin/ then tool_list = REQUIRED_TOOLS_OSX
    else abort "OS not supported"
  end

  tool_list.each { |tool| find_executable tool }

  # find_executable seems to create such file in case executable is not found
  File.delete 'mkmf.log' if File.exists?('mkmf.log')
end
setupdisk(dev, label, password, compression, encryption, keylength, mountpoint) click to toggle source
# File lib/frachtraum.rb, line 144
def setupdisk(dev, label, password, compression, encryption, keylength, mountpoint)

  abort "untested procedure -- won't continue"

  case RUBY_PLATFORM
    when /bsd/    then setupdisk_bsd   dev, label, password, compression, encryption, keylength, mountpoint
    when /linux/  then setupdisk_linux dev, label, password, compression, encryption, keylength, mountpoint
    #when /darwin/ then setupdisk_osx   dev, label, password, compression, encryption, keylength, mountpoint
    else abort "OS not supported"
  end
end
sweep(volume) click to toggle source
# File lib/frachtraum.rb, line 157
def sweep(volume)

  target_volumes = volume.nil? ? Frachtraum::VOLUMES : volume

  # TODO
  abort "sweeping not supported yet"

  target_volumes.each do |volume|
    if zfs_volume_exists?(volume)
      # TODO
    end
  end
end
zfs_volume_exists?(dataset) click to toggle source
# File lib/frachtraum.rb, line 190
def zfs_volume_exists?(dataset)
  output = %x( zfs get -H mounted #{dataset} 2>&1 )
  case output
  when /yes/
    return true
  when /dataset does not exist/, /permission denied/
    return false
  else
    abort "can't handle output of zfs_volume_exists?: #{output}"
  end
end