#!/usr/bin/env ruby # Build yaml files which provide arguments passed to {lv,pv,vg}s and subsequent # type conversions. # # ./generate_field_data ~/LVM2.2.02.38 require 'fileutils' require 'yaml' VERSION_FILE = "/VERSION" COLUMNS_FILE = "/lib/report/columns.h" debug = false TYPE_CONVERSION_MAP = { # Only types we can really trust "uint32" => "Integer", "int32" => "Integer", # These were determined by reading the code, they invoke _(u)int32_disp right away "pvmdas" => "Integer", "vgmdas" => "Integer", "lvcount" => "Integer", "lvsegcount" => "Integer", "segstartpe" => "Integer", # listed to return STR? "lvkmaj" => "Integer", "lvkmin" => "Integer", "snpercent" => "Float", "copypercent" => "Float", # size32/64, these do unit formatting unless overridden on command line. We # typically want them in bytes so we can convert them to Integers safely "size32" => "Integer", "size64" => "Integer", # These types return size32/size64 as well "lvkreadahead" => "Integer", "pvsize" => "Integer", "devsize" => "Integer", "originsize" => "Integer", "pvfree" => "Integer", "pvused" => "Integer", "pvmdafree" => "Integer", "pvmdasize" => "Integer", "vgsize" => "Integer", "vgfree" => "Integer", "vgmda_free" => "Integer", "chunksize" => "Integer", "segstart" => "Integer", "segsize" => "Integer", "snapcount" => "Integer", "vgmdafree" => "Integer", "vgmdasize" => "Integer", # Weird one, can be "auto" or size32 "lvreadahead" => "String", "lvmetadatasize" => "Integer", "datapercent" => "Float", "metadatapercent" => "Float", "pvmdasused" => "Integer", "vgmdasused" => "Integer", "vgmdacopies" => "Integer", "thincount" => "Integer", "thinid" => "Integer", "discards" => "Integer", "thinzero" => "Integer", "transactionid" => "Integer", "raidmismatchcount" => "Integer", "raidwritebehind" => "Integer", "raidminrecoveryrate" => "Integer", "raidmaxrecoveryrate" => "Integer", "segsizepe" => "Integer", "thinid" => "Integer", # New fields for caching "cache_total_blocks" => "Integer", "cache_used_blocks" => "Integer", "cache_dirty_blocks" => "Integer", "cache_read_hits" => "Integer", "cache_read_misses" => "Integer", "cache_write_hits" => "Integer", "cache_write_misses" => "Integer" } lvm_source = ARGV[0] version = File.readlines(lvm_source + VERSION_FILE)[0].split(' ')[0] lvs = [] lvssegs = [] pvs = [] pvssegs = [] vgs = [] File.readlines(lvm_source + COLUMNS_FILE).each do |line| # eg: FIELD(LVS, lv, STR, "LV UUID", lvid.id[1], 38, uuid, "lv_uuid", "Unique identifier") if line =~ %r{^FIELD\((.*)\)$} fields = $1.split(', ') fields.each { |f| f.gsub!(%r{^"}, ''); f.gsub!(%r{"$}, '') } p fields if debug app = fields[0] general_type = fields[2] specific_type = fields[6] column = fields[7] method = fields[7].dup description = fields[8] p app, general_type, specific_type, column, method, description if debug if ["NUM", "SIZ"].include?(general_type) attribute_type = TYPE_CONVERSION_MAP[specific_type] if attribute_type.nil? puts "Oops, missing type conversion data of column '#{specific_type}' use by '#{app}' which says its going to return a '#{specific_type}'" puts "Figure out the missing type and rerun." exit 1 end else attribute_type = "String" end # our shorter nicer method names, according to the man page these can be # dropped when passing column names as arguments as well, but i found a few # with issues (seg_start). case app when "LVS", "LVSINFOSTATUS" method.sub!(%r{^lv_}, '') when "SEGS" method.sub!(%r{^seg_}, '') when "LABEL" method.sub!(%r{^pv_}, '') when "PVS" method.sub!(%r{^pv_}, '') when "PVSEGS" method.sub!(%r{^pvseg_}, '') when "VGS" method.sub!(%r{^vg_}, '') end attribute = { :method => method, :column => column, :type_hint => attribute_type, :description => description } case app when "LVS", "LVSINFOSTATUS" lvs << attribute when "SEGS" lvssegs << attribute when "LABEL" pvs << attribute when "PVS" pvs << attribute when "PVSEGS" pvssegs << attribute when "VGS" vgs << attribute end end end # we use vg_uuid as our crossover attribute that links vg->lv and vg->pv attribute = { :method => "vg_uuid", :column => "vg_uuid", :type_hint => "String", :description => "For VolumeGroup to LogicalVolume relationship." } lvs << attribute attribute = { :method => "vg_uuid", :column => "vg_uuid", :type_hint => "String", :description => "For VolumeGroup to PhysicalVolume relationship." } pvs << attribute # and we link lv->lvsegment, pv->pvsegment attribute = { :method => "lv_uuid", :column => "lv_uuid", :type_hint => "String", :description => "For LogicalVolume to LogicalVolumeSegment relationship." } lvssegs << attribute attribute = { :method => "pv_uuid", :column => "pv_uuid", :type_hint => "String", :description => "For PhysicalVolume to PhysicalVolumeSegment relationship." } pvssegs << attribute lvs.sort! {|x,y| x[:column] <=> y[:column]} lvssegs.sort! {|x,y| x[:column] <=> y[:column]} pvs.sort! {|x,y| x[:column] <=> y[:column]} pvssegs.sort! {|x,y| x[:column] <=> y[:column]} vgs.sort! {|x,y| x[:column] <=> y[:column]} FileUtils.mkdir(version) disclaimer=<