# JBoss, Home of Professional Open Source # Copyright 2009, Red Hat Middleware LLC, and individual contributors # by the @authors tag. See the copyright.txt in the distribution for a # full listing of individual contributors. # # This is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2.1 of # the License, or (at your option) any later version. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this software; if not, write to the Free # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA, or see the FSF site: http://www.fsf.org. require 'guestfs' require 'logger' module BoxGrinder class SilencerProxy def initialize( o, destination ) @o = o @destination = destination end def method_missing( m, *args, &block ) begin redirect_streams( @destination ) do @o.send(m, *args, &block) end rescue raise end end def redirect_streams( destination ) old_stdout_stream = STDOUT.dup old_stderr_stream = STDERR.dup STDOUT.reopen( destination ) STDERR.reopen( destination ) STDOUT.sync = true STDERR.sync = true yield ensure STDOUT.reopen( old_stdout_stream ) STDERR.reopen( old_stderr_stream ) end end end module Guestfs class Guestfs alias_method :sh_original, :sh def sh( command ) begin output = sh_original( command ) puts output rescue => e puts "Error occurred while executing above command. Appliance may not work properly." end output end def redirect( destination ) BoxGrinder::SilencerProxy.new( self, destination ) end end end module BoxGrinder class GuestFSHelper def initialize( raw_disk, options = {} ) @raw_disk = raw_disk @log = options[:log] || Logger.new(STDOUT) @partitions = {} end attr_reader :guestfs def customize read_pipe, write_pipe = IO.pipe fork do read_pipe.each do |o| if o.chomp.strip.eql?("") exit else @log.trace "GFS: #{o.chomp.strip}" end end end helper = execute( write_pipe ) yield @guestfs, helper clean_close write_pipe.puts "" Process.wait end def execute( pipe = nil ) @log.debug "Preparing guestfs..." @guestfs = pipe.nil? ? Guestfs::create : Guestfs::create.redirect( pipe ) # TODO remove this, https://bugzilla.redhat.com/show_bug.cgi?id=502058 @guestfs.set_append( "noapic" ) @guestfs.set_verbose(1) @guestfs.set_trace(1) # workaround for latest qemu # It'll only work if qemu-stable package is installed. It is installed by default on meta-appliance # TODO wait for stable qemu and remove this # Looks like in F13 (qemu-img-0.12.3-8.fc13.i686) this is fixed qemu_wrapper = "/usr/share/qemu-stable/bin/qemu.wrapper" if File.exists?( qemu_wrapper ) @guestfs.set_qemu( qemu_wrapper ) end @log.trace "Adding drive '#{@raw_disk}'..." @guestfs.add_drive( @raw_disk ) @log.trace "Drive added." @log.debug "Launching guestfs..." @guestfs.launch case @guestfs.list_partitions.size when 0 mount_partition( @guestfs.list_devices.first, '/' ) when 1 mount_partition( @guestfs.list_partitions.first, '/' ) else mount_partitions end @log.trace "Guestfs launched." self end def clean_close @log.trace "Closing guestfs..." @guestfs.sync @guestfs.umount_all @guestfs.close @log.trace "Guestfs closed." end def mount_partition( part, mount_point ) @log.trace "Mounting #{part} partition to #{mount_point}..." @guestfs.mount_options( "", part, mount_point ) @log.trace "Partition mounted." end # TODO this is shitty, I know... https://bugzilla.redhat.com/show_bug.cgi?id=507188 def rebuild_rpm_database @log.debug "Cleaning RPM database..." @guestfs.sh( "rm -f /var/lib/rpm/__db.*" ) @guestfs.sh( "rpm --rebuilddb" ) @log.debug "Cleaning RPM database finished." end def mount_partitions root_partition = nil @guestfs.list_partitions.each do |partition| mount_partition( partition, '/' ) if @guestfs.exists( '/sbin/e2label' ) != 0 root_partition = partition break end @guestfs.umount( partition ) end raise "No root partition found for '#{File.basename( @raw_disk )}' disk!" if root_partition.nil? @guestfs.list_partitions.each do |partition| next if partition == root_partition mount_partition( partition, @guestfs.sh( "/sbin/e2label #{partition}" ).chomp.strip ) end end end end