Module: Frachtraum

Extended by:
Frachtraum
Included in:
Frachtraum
Defined in:
lib/frachtraum.rb,
lib/frachtraum/bsd.rb,
lib/frachtraum/cli.rb,
lib/frachtraum/osx.rb,
lib/frachtraum/linux.rb,
lib/frachtraum/config.rb

Defined Under Namespace

Classes: CLI

Constant Summary collapse

VERSION =
'0.0.12'.freeze
BYTES_IN_KiB =

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

2**10
BYTES_IN_MiB =
2**20
BYTES_IN_GiB =
2**30
BYTES_IN_TiB =
2**40
BYTES_IN_KB =

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

10**3
BYTES_IN_MB =
10**6
BYTES_IN_GB =
10**9
BYTES_IN_TB =
10**12
OUTPUT_DOTS_LEN =

TODO: the length should be dynamically calculated, based on the strlen of longest tmtarget or volume

40
CHECKMARK =

> ✓

"\u2713"
BALLOTX =

> ✗

"\u2717"
REQUIRED_TOOLS_BSD =
['dd','grep','gpart','glabel','geli','zfs','zpool']
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/

[]
REQUIRED_TOOLS_LINUX =

not yet supported

[]
COMPRESSION =
'lz4'
ENCRYPTION =
'AES-XTS'
KEYLENGTH =
4096
MOUNTPOINT =
'/frachtraum'
VOLUMES =
[]
TIMEMACHINE_TARGETS =
[]

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.attach(password, volume = nil) ⇒ Object



73
74
75
76
77
78
79
80
# 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

.capacityObject



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# 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) ⇒ Object



64
65
66
67
68
69
70
# 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) ⇒ Object




55
56
57
58
59
60
61
# 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

.reportObject



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# 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_testObject



173
174
175
176
177
178
179
180
181
182
183
184
185
186
# 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) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
# 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) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
168
169
# 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) ⇒ Boolean

Returns:

  • (Boolean)


190
191
192
193
194
195
196
197
198
199
200
# 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

Instance Method Details

#attach_bsd(password, volume = nil) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 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) ⇒ Object



5
6
7
8
# File 'lib/frachtraum/linux.rb', line 5

def attach_linux(password, volume)
  # TODO
  abort "not yet implemented"
end

#attach_osx(password, volume) ⇒ Object

not yet supported



11
12
13
14
# File 'lib/frachtraum/osx.rb', line 11

def attach_osx(password, volume)
  # TODO
  abort "not yet implemented"
end

#exec_cmd(msg, cmd) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 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) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 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) ⇒ Object



10
11
12
13
# 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) ⇒ Object



16
17
18
19
# File 'lib/frachtraum/osx.rb', line 16

def setupdisk_osx(dev, label, password, compression, encryption, keylength, mountpoint)
  # TODO
  abort "not yet implemented"
end