require 'pathname' require 'shellwords' require 'optparse' require 'securerandom' require 'to_lvm_xfs/exts' require 'to_lvm_xfs/sh' require 'to_lvm_xfs/structs' class ProgrammError < RuntimeError end class Base def self.run *args, &exe new( *args).instance_eval &exe end def d msg, eq raise ProgrammError, msg, caller[1..-1] unless eq end def err *args STDERR.puts "\e[31;1m#{args.join ' '}\e[0m" end def msg first, *args STDERR.puts "\e[33m#{first}\e[35m #{args.join ' '}\e[0m" end def kpartx image, read_only: nil sh.kpartx "-sa#{read_only ? :r : ''}", image lpartx image end def lpartx image mapper = Pathname.new '/dev/mapper' lines = sh.kpartx( -:al, image, return: :lines)[0..-1] if lines.grep( /^loop deleted/).empty? lines.map do |line| d "cannot extract partition «#{line}»", line =~ /^([^ :]+) : / mapper + $1 end else [] end end def lsblk path, *output output = %w[NAME TYPE MOUNTPOINT] if output.empty? output = output.map {|o| o.to_s } lines = sh.lsblk %w[--output], output.join(','), path op = output.map {|o| o.downcase.to_sym } lines.map {|l| Hash[*op.zip(l.split( /\s+/)).flatten]} end def departx image sh.kpartx -:sd, image end def kpartxed? image lines = sh.kpartx -:l, image, return: :lines not lines.grep( /^loop deleted/).empty? end def get_vgname_of_pv *a, &exe return to_enum( __method__, *a) unless block_given? sh.pvs( --:options, :vg_name, *a, return: :lines).each do |l| yield l.chomp.sub( /^\s*/, '') end end def remove_all_partitions_from image sh[].parted( -:ms, image, :print).each do sh.parted image, :rm, 1 end end def manipulate_file file, &exe flags = 'r+' msg :manipulate, file File.open file.to_s, flags do |file| lines = [] exe.call file, &lines.method(:push) file.truncate 0 file.pos = 0 lines.each &file.method(:puts) end end def capsulated_rescue yield rescue Object => e STDERR.puts "\e[31;1m#{e} (#{e.class}):" e.backtrace.each {|s| STDERR.puts " #{s}" } STDERR.print "\e[0m" end attr_reader :sh, :mounted, :looped, :base, :dest, :vgname def initialize *args OptionParser.new do |opts| getopts opts opts.parse! args end if :ask == @password v = b = nil require 'io/console' STDERR.print "Type Password. " v = STDIN.getpass STDERR.print "Type password again. " b = STDIN.getpass d "Password missmatch", v == b d "Password is empty", !v.empty? @password = v.crypt "$6$#{SecureRandom.urlsafe_base64 6}$" end @sh = Sh.new immediately: true, expect_status: 0 #x = sh.system :echo, :hallo, :welt, as_io: true do |io| # STDERR.puts io.inspect # STDERR.puts '---',io.readlines,'---' #end #STDERR.puts x.inspect #exit 1 @mounted, @activated_vgs = [], [] @looped = Hash.new {|h,k| h[k] = [] } @base = Image.new self, 'base', image: @baseimage @dest = Image.new self, 'dest', image: @destination STDERR.print </, hostname } end msg :write, hostname, :to, 'etc/hostname' dest.root.join( 'etc/hostname').open 'w' do |f| f.puts hostname end end def install_packages_from_dir *paths paths.each do |pkgs| next unless pkgs.directory? pkgs.each_child do |pn| next if /\A\./ =~ pn.basename.to_s # no dot-files. install_package pn end end end def install_package path case path.basename.to_s when /\.deb\z/ install_deb path when /\.tar\z/, /\.t(?:ar\.|)(?:xz|bzip2|gz|lzma|Z)\z/ sh.tar -:C, dest.root, -:xf, path else warn :ignore, path end end def install_deb path #sh.dpkg '--unpack', '--force-architecture', pn, chroot: dest.root adeb = Pathname.new( 'var/cache/apt/archives')+path.basename.to_s dest.root.join( adeb.to_s).copy path sh.dpkg -:i, adeb, chroot: dest.root #msg :unpack, path # sometimes, you have to use system to do something. but it is ok, if you know, how to do it. # this command should be safe. #d "unpacking failed: #{path}", # system( "ar p #{path.to_s.shellescape} data.tar.xz | tar -JxC #{dest.root.to_s.shellescape}") end end