diff --git a/lib/hobo.rb b/lib/hobo.rb index 7a34b4ca3..23913541f 100644 --- a/lib/hobo.rb +++ b/lib/hobo.rb @@ -8,7 +8,7 @@ require 'logger' require 'virtualbox' require 'net/ssh' require 'ping' -require 'hobo/error' +require 'hobo/util' require 'hobo/config' require 'hobo/env' require 'hobo/ssh' diff --git a/lib/hobo/config.rb b/lib/hobo/config.rb index db94a290f..35ad9d24c 100644 --- a/lib/hobo/config.rb +++ b/lib/hobo/config.rb @@ -7,7 +7,7 @@ module Hobo @config = nil @config_runners = [] - class < protocol } end + + def hd_location=(val) + raise Exception.new "disk_storage must be set to a directory" unless File.directory?(val) + @hd_location=val + end end class Top < Base diff --git a/lib/hobo/env.rb b/lib/hobo/env.rb index f0b553fe2..a50ceb4b0 100644 --- a/lib/hobo/env.rb +++ b/lib/hobo/env.rb @@ -8,7 +8,7 @@ module Hobo @@persisted_vm = nil @@root_path = nil - extend Hobo::Error + extend Hobo::Util class << self def persisted_vm; @@persisted_vm; end diff --git a/lib/hobo/error.rb b/lib/hobo/util.rb similarity index 61% rename from lib/hobo/error.rb rename to lib/hobo/util.rb index d9f5145c7..36c18a22d 100644 --- a/lib/hobo/error.rb +++ b/lib/hobo/util.rb @@ -1,14 +1,19 @@ module Hobo - module Error - def error_and_exit(error) - puts <<-error + module Util + def error_and_exit(error) + puts <<-error ===================================================================== Hobo experienced an error! #{error.chomp} ===================================================================== error - exit - end + exit + end + + def logger + HOBO_LOGGER + end end -end +end + diff --git a/lib/hobo/vm.rb b/lib/hobo/vm.rb index 40129eb0a..119a96632 100644 --- a/lib/hobo/vm.rb +++ b/lib/hobo/vm.rb @@ -1,8 +1,10 @@ module Hobo class VM + HD_EXT_DEFAULT = 'VMDK' attr_reader :vm - extend Hobo::Error + extend Hobo::Util + include Hobo::Util class << self # Bring up the virtual machine. Imports the base image and @@ -28,9 +30,10 @@ module Hobo def suspend Env.require_persisted_vm error_and_exit(<<-error) if Env.persisted_vm.saved? -The hobo virtual environment you are trying to resume is already in a +The hobo virtual environment you are trying to suspend is already in a suspended state. error + logger.info "Saving VM state..." Env.persisted_vm.save_state(true) end @@ -59,6 +62,7 @@ error def create import + move_hd if Hobo.config[:vm][:hd_location] persist setup_mac_address forward_ports @@ -69,26 +73,46 @@ error def destroy if @vm.running? - HOBO_LOGGER.info "VM is running. Forcing immediate shutdown..." + logger.info "VM is running. Forcing immediate shutdown..." @vm.stop(true) end - HOBO_LOGGER.info "Destroying VM and associated drives..." + logger.info "Destroying VM and associated drives..." @vm.destroy(:destroy_image => true) end + def move_hd + error_and_exit(<<-error) unless @vm.powered_off? +The virtual machine must be powered off to move its disk. +error + + old_image = hd.image.dup + new_image_file = Hobo.config[:vm][:hd_location] + old_image.filename + + logger.info "Cloning current VM Disk to new location (#{ new_image_file })..." + # TODO image extension default? + new_image = hd.image.clone(new_image_file , HD_EXT_DEFAULT, true) + hd.image = new_image + + logger.info "Attaching new disk to VM ..." + @vm.save + + logger.info "Destroying old VM Disk (#{ old_image.filename })..." + old_image.destroy(true) + end + def import - HOBO_LOGGER.info "Importing base VM (#{Hobo.config[:vm][:base]})..." + logger.info "Importing base VM (#{Hobo.config[:vm][:base]})..." @vm = VirtualBox::VM.import(File.expand_path(Hobo.config[:vm][:base])) end def persist - HOBO_LOGGER.info "Persisting the VM UUID (#{@vm.uuid})..." + logger.info "Persisting the VM UUID (#{@vm.uuid})..." Env.persist_vm(@vm) end def setup_mac_address - HOBO_LOGGER.info "Matching MAC addresses..." + logger.info "Matching MAC addresses..." @vm.nics.first.macaddress = Hobo.config[:vm][:base_mac] @vm.save(true) end @@ -109,7 +133,7 @@ error end def setup_shared_folder - HOBO_LOGGER.info "Creating shared folders..." + logger.info "Creating shared folders..." folder = VirtualBox::SharedFolder.new folder.name = "hobo-root-path" folder.hostpath = Env.root_path @@ -126,28 +150,31 @@ error end def start - HOBO_LOGGER.info "Booting VM..." + logger.info "Booting VM..." @vm.start(:headless, true) # Now we have to wait for the boot to be successful - HOBO_LOGGER.info "Waiting for VM to boot..." - + logger.info "Waiting for VM to boot..." + Hobo.config[:ssh][:max_tries].to_i.times do |i| sleep 5 unless ENV['HOBO_ENV'] == 'test' - HOBO_LOGGER.info "Trying to connect (attempt ##{i+1} of #{Hobo.config[:ssh][:max_tries]})..." + logger.info "Trying to connect (attempt ##{i+1} of #{Hobo.config[:ssh][:max_tries]})..." if Hobo::SSH.up? - HOBO_LOGGER.info "VM booted and ready for use!" + logger.info "VM booted and ready for use!" return true end end - HOBO_LOGGER.info "Failed to connect to VM! Failed to boot?" + logger.info "Failed to connect to VM! Failed to boot?" false end def saved?; @vm.saved? end def save_state(errs); @vm.save_state(errs) end + + # TODO need a better way to which controller is the hd + def hd; @vm.storage_controllers.first.devices.first end end end diff --git a/script/hobo-ssh-expect.sh b/script/hobo-ssh-expect.sh index 82b1dd268..f27aeebeb 100755 --- a/script/hobo-ssh-expect.sh +++ b/script/hobo-ssh-expect.sh @@ -11,10 +11,9 @@ if { $port != "" } { set port_option "" } -spawn ssh $port_option $uname@$host +spawn ssh $port_option -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $uname@$host expect "*password: " { - sleep 1 send "$password\r" } timeout { send_user "Error connecting" diff --git a/test/hobo/vm_test.rb b/test/hobo/vm_test.rb index 84e25a298..ff11e1a5e 100644 --- a/test/hobo/vm_test.rb +++ b/test/hobo/vm_test.rb @@ -235,5 +235,41 @@ class VMTest < Test::Unit::TestCase Hobo::Env.persisted_vm.expects(:start).once.returns(true) end end + + context "creating a new vm with a specified disk storage location" do + + should "error and exit of the vm is not powered off" do + # Exit does not prevent method from proceeding in test, so we must set expectations + vm = move_hd_expectations + @mock_vm.expects(:powered_off?).returns(false) + vm.expects(:error_and_exit) + vm.move_hd + end + + should "create assign a new disk image, and delete the old one" do + vm = move_hd_expectations + @mock_vm.expects(:powered_off?).returns(true) + vm.move_hd + end + + def move_hd_expectations + image, hd = mock('image'), mock('hd') + + Hobo.config[:vm].expects(:hd_location).at_least_once.returns('/locations/') + image.expects(:clone).with(Hobo.config[:vm][:hd_location] + 'foo', Hobo::VM::HD_EXT_DEFAULT, true).returns(image) + image.expects(:filename).twice.returns('foo') + + hd.expects(:image).twice.returns(image) + hd.expects(:image=).with(image) + + image.expects(:destroy) + + @mock_vm.expects(:save) + + vm = Hobo::VM.new(@mock_vm) + vm.expects(:hd).times(3).returns(hd) + vm + end + end end end