module Veewee module Provider module Core module BoxCommand def build(options={}) if definition.nil? raise Veewee::Error,"Could not find the definition. Make sure you are one level above the definitions directory when you execute the build command." end # Requires valid definition ui.info "Building Box #{name} with Definition #{definition.name}:" options.each do |name,value| ui.info "- #{name} : #{value}" end # Checking regexp of postinstall include/excludes validate_postinstall_regex(options) # Check the iso file we need to build the box definition.verify_iso(options) if self.exists? # check if --force option was given if options['force']==true self.destroy self.reload else ui.error("you need to provide --force because the box #{name} already exists",:prefix => false) raise Veewee::Error,"you need to provide --force because the box #{name} already exists" end end # By now the box should have been gone, just checking again if self.exists? ui.error("The box should have been deleted by now. Something went terribly wrong. Sorry",:prefix => false) raise Veewee::Error, "The box should have been deleted by now. Something went terribly wrong. Sorry" end self.create(options) # Check the GUI mode required env.logger.info "Provider asks the box to start: GUI enabled? #{!options['nogui']}" self.up(options) # Waiting for it to boot ui.info "Waiting #{definition.boot_wait.to_i} seconds for the machine to boot" sleep definition.boot_wait.to_i # Calculate an available kickstart port unless definition.kickstart_port.nil? guessed_port=guess_free_port(definition.kickstart_port.to_i,7199).to_s if guessed_port.to_s!=definition.kickstart_port ui.warn "Changing kickstart port from #{definition.kickstart_port} to #{guessed_port}" definition.kickstart_port=guessed_port.to_s end end # Let fill's in the variable we need boot_sequence=fill_sequence(definition.boot_cmd_sequence,{ :ip =>host_ip_as_seen_by_guest, :port => definition.kickstart_port.to_s, :name => name }) # Type the boot sequence Thread.new { self.console_type(boot_sequence) } self.handle_kickstart(options) # Wait for an ipaddress # This needs to be done after the kickstart: # As the dhcp request will likely occur just before the kickstart fetch until !self.ip_address.nil? env.logger.info "wait for Ip address" sleep 2 end if ! definition.skip_iso_transfer then self.transfer_buildinfo(options) end # Filtering post install files based upon --postinstall-include and --postinstall--exclude definition.postinstall_files=filter_postinstall_files(options) self.handle_postinstall(options) ui.success "The box #{name} was built successfully!" ui.info "You can now login to the box with:" if (definition.winrm_user && definition.winrm_password) env.ui.info winrm_command_string else env.ui.info ssh_command_string end return self end def validate_postinstall_regex(options) env.logger.info "Checking the postinstall excludes" unless options["postinstall_exclude"].nil? options["postinstall_exclude"].each do |p| begin r=::Regexp.new(p) rescue ::RegexpError => ex raise Veewee::Error ,"\nError in postinstall exclude (ruby regexp) pattern: #{p}:\n- #{ex}" end end end env.logger.info "Checking the postinstall includes" unless options["postinstall_include"].nil? options["postinstall_include"].each do |p| begin r=Regexp.new(p) rescue RegexpError => ex raise Veewee::Error ,"\nError in postinstall include (ruby regexp) pattern: #{p}:\n- #{ex}" end end end end def filter_postinstall_files(options) new_definition=definition.clone env.logger.info "Applying the postinstall excludes" unless options["postinstall_exclude"].nil? options["postinstall_exclude"].each do |p| env.logger.info "Exclude pattern #{p}" new_definition.postinstall_files.reject! { |f| f.match(p) } end end env.logger.info "Applying the postinstall includes" unless options["postinstall_include"].nil? options["postinstall_include"].each do |p| env.logger.info "Include pattern #{p}" new_definition.postinstall_files.collect! { |f| f.match(p) ? f.gsub(/^_/,""): f} end end env.logger.info "filtered postinstall files:" new_definition.postinstall_files.each do |p| env.logger.info "- "+p end return new_definition.postinstall_files end # This will take a sequence and fill in the variables specified in the options # f.i. options={:ip => "name"} will substitute "%IP%" -> "name" def fill_sequence(sequence,options) filled=sequence.dup options.each do |key,value| filled.each do |s| s.gsub!("%#{key.to_s.upcase}%",value) end end return filled end def build_info [ {:filename => ".veewee_version",:content => "#{Veewee::VERSION}"}] end # This function handles all the post-install scripts # It requires a definition to find all the necessary information def handle_kickstart(options) # Handling the kickstart by web kickstartfiles=definition.kickstart_file if kickstartfiles.nil? || kickstartfiles.length == 0 env.ui.info "Skipping webserver as no kickstartfile was specified" else env.ui.info "Starting a webserver #{definition.kickstart_ip}:#{definition.kickstart_port}\n" end # Check if the kickstart is an array or a single string if kickstartfiles.is_a?(String) # Let's turn it into an array kickstartfiles=kickstartfiles.split end # For each kickstart file spinup a webserver and wait for the file to be fetched unless kickstartfiles.nil? kickstartfiles.each do |kickfile| wait_for_http_request(kickfile,{ :port => definition.kickstart_port, :host => definition.kickstart_ip, :timeout => definition.kickstart_timeout, :web_dir => definition.path }) end end end # This function handles all the post-install scripts # It requires a box(to login to) and a definition(listing the postinstall files) def handle_postinstall(options) # Transfer all postinstall files definition.postinstall_files.each do |postinstall_file| # Filenames of postinstall_files are relative to their definition filename=File.join(definition.path,postinstall_file) self.copy_to_box(filename,File.basename(filename)) if not (definition.winrm_user && definition.winrm_password) self.exec("chmod +x \"#{File.basename(filename)}\"") end end # Prepare a pre_poinstall file if needed (not nil , or not empty) unless definition.pre_postinstall_file.to_s.empty? pre_filename=File.join(definition.path, definition.pre_postinstall_file) self.copy_to_box(filename,File.basename(pre_filename)) if (definition.winrm_user && definition.winrm_password) # not implemented on windows yet else self.exec("chmod +x \"#{File.basename(pre_filename)}\"") # Inject the call to the real script by executing the first argument (it will be the postinstall script file name to be executed) self.exec("execute=\"\\n# We must execute the script passed as the first argument\\n\\$1\" && printf \"%b\\n\" \"$execute\" >> #{File.basename(pre_filename)}") end end # Now iterate over the postinstall files definition.postinstall_files.each do |postinstall_file| # Filenames of postinstall_files are relative to their definition filename=File.join(definition.path,postinstall_file) unless File.basename(postinstall_file).start_with?("_") unless definition.pre_postinstall_file.to_s.empty? raise 'not implemented on windows yet' if (definition.winrm_user && definition.winrm_password) # Filename of pre_postinstall_file are relative to their definition pre_filename=File.join(definition.path, definition.pre_postinstall_file) # Upload the pre postinstall script if not already transfered command = "./" + File.basename(pre_filename) command = sudo(command) + " ./"+File.basename(filename) else if (definition.winrm_user && definition.winrm_password) # no sudo on windows, batch files only please? self.exec(File.basename(filename)) else self.exec(sudo("./"+File.basename(filename))) end end self.exec(command) else env.logger.info "Skipping postinstallfile #{postinstall_file}" end end end # Transfer information provide by the Provider to the box # # def transfer_buildinfo(options) build_info.each do |info| begin infofile=Tempfile.open("#{info[:filename]}") # Force binary mode to prevent windows from putting CR-LF end line style # http://www.ruby-forum.com/topic/127453#568546 infofile.binmode infofile.puts "#{info[:content]}" infofile.rewind infofile.close self.copy_to_box(infofile.path,info[:filename]) infofile.delete rescue RuntimeError => ex ui.error("Error transfering file #{info[:filename]} failed, possible not enough permissions to write? #{ex}",:prefix => false) raise Veewee::Error,"Error transfering file #{info[:filename]} failed, possible not enough permissions to write? #{ex}" end end end end #Module end #Module end #Module end #Module