diff --git a/thin-provisioning/thin_metadata_size b/thin-provisioning/thin_metadata_size new file mode 100755 index 0000000..cb2cd9a --- /dev/null +++ b/thin-provisioning/thin_metadata_size @@ -0,0 +1,121 @@ +#!/usr/bin/env ruby +# +# Copyright (C) 2013 Red Hat, GmbH +# +# This file is released under the GPL +# +# +# Script to calculate device-mapper thin privisioning +# metadata device size based on pool, block size and +# maximum expected thin provisioned devices. +# + +require 'optparse' +require 'pathname' + +#---------------------------------------------------------------- + +$prg = Pathname.new($0).basename +$btree_entrysize = 16 +$btree_nodesize = 4096 + +def init_units + units = Hash.new + units[:chars] = "sKkMmGgTtPpEeZzYy" + units[:strings] = [ 'sectors', + 'kilobytes', 'kibibytes', 'megabytes', 'mebibytes', + 'gigabytes', 'gibibytes', 'terabytes', 'tebibytes', + 'petabytes', 'pebibytes', 'exabytes', 'ebibytes', + 'zetabytes', 'zebibytes', 'yottabytes', 'yobibytes' ] + units[:factors] = Array.new + + units[:factors][0] = 512 + 1.step(8) do |e| + units[:factors][(e - 1) * 2 + 1] = 1024**e + units[:factors][(e - 1) * 2 + 2] = 1000**e + end + units +end + +def check_opts(opts) + abort "#{$prg} - 3 arguments required!" if opts.length < 3 + abort "#{$prg} - poolsize must be much larger than blocksize" if opts[:poolsize] < opts[:blocksize] * 16 + abort "#{$prg} - maximum number of thin provisioned devices must be > 0" if opts[:maxthins].nil? || opts[:maxthins] == 0 +end + +def to_sectors(size, units) + unit_chars = units[:chars] + + a = size.split(/[#{unit_chars}]/) + s = size.to_i.to_s + abort "#{$prg} - only one unit character allowed!" if a.length > 1 || size.length - s.length > 1 + abort "#{$prg} - invalid unit specifier!" if s != a[0] + idx = unit_chars.index(size[a[0].length]) + r = size.to_i * units[:factors][idx] / 512 + r +end + +def parse_command_line(argv, units) + opts = {} + unit_chars = units[:chars] + + os = OptionParser.new do |o| + o.banner = "Thin Provisioning Metadata Size Calculator.\nUsage: #{$prg} [opts]" + o.on('-b', '--blocksize BLOCKSIZE', String, + "Block size, ie. allocation unit size for thin provisioned devices.") do |bs| + opts[:blocksize] = to_sectors(bs, units) + end + o.on('-s', '--poolsize SIZE', String, "Size of the pool device.") do |ps| + opts[:poolsize] = to_sectors(ps, units) + end + o.on('-m', '--maxthins MAXTHINS', Integer, "Size of the pool device.") do |mt| + opts[:maxthins] = mt + end + o.on('-u', '--units [skKmMgGtTpP]', String, "Output unit specifier.") do |u| + if u =~ /[#{unit_chars}]/ + opts[:units] = u + else + abort "#{$prg} - output unit specifier invalid." + end + end + o.on('-n', '--numeric-only', "Output numeric value only.") do + opts[:numeric] = true || nil + end + o.on('-h', '--help', "Outout this help.") do + puts o + exit + end + end + + begin + os.parse(argv) + rescue OptionParser::ParseError => e + abort "#{$prg} #{e}\n#{$prg} #{os}" + end + + check_opts(opts) + opts +end + +def entries_per_node + btree_node_headersize = 64 + ($btree_nodesize - btree_node_headersize) / $btree_entrysize +end + +def estimated_result(opts, units) + idx = opts[:units] ? units[:chars].index(opts[:units]) : 0 + r = 1.0 + ((opts[:poolsize] / opts[:blocksize] / entries_per_node) + opts[:maxthins]) * 512 / 8 + r /= units[:factors][idx] + tmp = "%.2f" % r + # FIXME: avoid zero decimal places + r = tmp.to_f > 0.0 ? tmp : "%.4e" % r + r = "#{$prg} estimated metadata area size is #{r} #{units[:strings][idx]}." if !opts[:numeric] + r +end + + +#---------------------------------------------------------------- +# Main + +units = init_units +puts estimated_result(parse_command_line(ARGV, units), units)